当前位置: 首页 > news >正文

6.2 Windows驱动开发:内核枚举SSSDT表基址

在Windows内核中,SSSDT(System Service Shadow Descriptor Table)是SSDT(System Service Descriptor Table)的一种变种,其主要用途是提供Windows系统对系统服务调用的阴影拷贝。SSSDT表存储了系统调用的函数地址,类似于SSDT表,但在某些情况下,Windows系统会使用SSSDT表来对系统服务进行引导和调用。

SSSDT表的存在是为了加强系统的安全性和稳定性。通过使用SSSDT表,操作系统可以在运行时检查系统服务的合法性,并确保其不被非法修改。这有助于防止恶意软件或恶意行为修改系统服务地址,提高系统的整体安全性。

在笔者上一篇文章《枚举完整SSDT地址表》实现了针对SSDT表的枚举功能,本章继续实现对SSSDT表的枚举,ShadowSSDT中文名影子系统服务描述表,SSSDT其主要的作用是管理系统中的图形化界面,其Win32子系统的内核实现是Win32k.sys驱动,属于GUI线程的一部分,其自身没有导出表,枚举SSSDT表其与SSDT原理基本一致。

如下是闭源ARK工具的枚举效果:

首先需要找到SSSDT表的位置,通过《Win10内核枚举SSDT表基址》文章中的分析可知,SSSDT就在SSDT的下面,只需要枚举4c8d1dde1e3a00特征即可,如果你找不到上一篇具体分析流程了,那么多半你是看到了转载文章。

先实现第一个功能,得到SSSDT表的基地址以及SSDT函数个数,完整代码如下所示。

#include <ntifs.h>
#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE
{PVOID          ServiceTableBase;PVOID          ServiceCounterTableBase;ULONGLONG      NumberOfServices;PVOID          ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow = 0;
ULONG64 ul64W32pServiceTable = 0;// 获取 KeServiceDescriptorTableShadow 首地址
ULONGLONG GetKeServiceDescriptorTableShadow()
{// 设置起始位置PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1808FE;// 设置结束位置PUCHAR EndSearchAddress = StartSearchAddress + 0x8192;// DbgPrint("扫描起始地址: %p --> 扫描结束地址: %p \n", StartSearchAddress, EndSearchAddress);PUCHAR ByteCode = NULL;UCHAR OpCodeA = 0, OpCodeB = 0, OpCodeC = 0;ULONGLONG addr = 0;ULONG templong = 0;for (ByteCode = StartSearchAddress; ByteCode < EndSearchAddress; ByteCode++){// 使用MmIsAddressValid()函数检查地址是否有页面错误if (MmIsAddressValid(ByteCode) && MmIsAddressValid(ByteCode + 1) && MmIsAddressValid(ByteCode + 2)){OpCodeA = *ByteCode;OpCodeB = *(ByteCode + 1);OpCodeC = *(ByteCode + 2);// 对比特征值 寻找 nt!KeServiceDescriptorTable 函数地址/*lyshark kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00  lea     r10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00  lea     r11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000  test    dword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407            je      nt!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00  lea     r11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3          mov     r10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 获取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 与低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}}}return  0;
}// 得到SSSDT个数
ULONGLONG GetSSSDTCount()
{PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// DbgPrint("Count => %d \n", pWin32k->NumberOfServices);return pWin32k->NumberOfServices;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("驱动程序卸载成功! \n"));
}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");KeServiceDescriptorTableShadow = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTableShadow();DbgPrint("[LyShark] SSSDT基地址 = 0x%p \n", KeServiceDescriptorTableShadow);ULONGLONG count = GetSSSDTCount();DbgPrint("[LyShark] SSSDT个数 = %d \n", count);DriverObject->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

这段代码运行后即可得到SSSDT表基地址,以及该表中函数个数。

在此基础之上增加枚举计算过程即可,完整源代码如下所示。

SSSDT 函数起始index是0x1000,但W32pServiceTable是从基址开始记录的,这个误差则需要(index-0x1000)来得到,至于+4则是下一个元素与上一个元素的偏移。

计算公式:

  • W32pServiceTable + 4 * (index-0x1000)
#include <ntifs.h>
#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE
{PVOID          ServiceTableBase;PVOID          ServiceCounterTableBase;ULONGLONG      NumberOfServices;PVOID          ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow = 0;
ULONG64 ul64W32pServiceTable = 0;// 获取 KeServiceDescriptorTableShadow 首地址
ULONGLONG GetKeServiceDescriptorTableShadow()
{// 设置起始位置PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1808FE;// 设置结束位置PUCHAR EndSearchAddress = StartSearchAddress + 0x8192;// DbgPrint("扫描起始地址: %p --> 扫描结束地址: %p \n", StartSearchAddress, EndSearchAddress);PUCHAR ByteCode = NULL;UCHAR OpCodeA = 0, OpCodeB = 0, OpCodeC = 0;ULONGLONG addr = 0;ULONG templong = 0;for (ByteCode = StartSearchAddress; ByteCode < EndSearchAddress; ByteCode++){// 使用MmIsAddressValid()函数检查地址是否有页面错误if (MmIsAddressValid(ByteCode) && MmIsAddressValid(ByteCode + 1) && MmIsAddressValid(ByteCode + 2)){OpCodeA = *ByteCode;OpCodeB = *(ByteCode + 1);OpCodeC = *(ByteCode + 2);// 对比特征值 寻找 nt!KeServiceDescriptorTable 函数地址/*lyshark kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00  lea     r10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00  lea     r11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000  test    dword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407            je      nt!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00  lea     r11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3          mov     r10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 获取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 与低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}}}return  0;
}// 得到SSSDT个数
ULONGLONG GetSSSDTCount()
{PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// DbgPrint("Count => %d \n", pWin32k->NumberOfServices);return pWin32k->NumberOfServices;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("驱动程序卸载成功! \n"));
}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");KeServiceDescriptorTableShadow = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTableShadow();DbgPrint("[LyShark] SSSDT基地址 = 0x%p \n", KeServiceDescriptorTableShadow);ULONGLONG count = GetSSSDTCount();DbgPrint("[LyShark] SSSDT个数 = %d \n", count);// 循环枚举SSSDTfor (size_t Index = 0; Index < count; Index++){PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// 获取SSSDT地址//ln win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(1-1000))&0x00000000`ffffffff)>>4)-10000000//u win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(Index-0x1000))&0x00000000`ffffffff)>>4)-0x10000000//u poi(win32k!W32pServiceTable+4*(1-0x1000))//u poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff//u (poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff)>>4//u win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff)>>4)-0x10000000ULONGLONG qword_temp = 0;LONG dw = 0;// SSSDT 下标从1000开始,而W32pServiceTable是从0开始// + 4 则是每次向下4字节就是下一个地址qword_temp = W32pServiceTable + 4 * (Index - 0x1000);dw = *(PLONG)qword_temp;// dw = qword_temp & 0x00000000ffffffff;dw = dw >> 4;qword_temp = W32pServiceTable + (LONG64)dw;DbgPrint("[LyShark] ID: %d | SSSDT: 0x%p \n", Index, qword_temp);}DriverObject->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

枚举效果如下图所示所示,注意这一步必须要在GUI线程中执行,否则会异常,建议将枚举过程写成DLL文件,注入到explorer.exe进程内执行;

相关文章:

6.2 Windows驱动开发:内核枚举SSSDT表基址

在Windows内核中&#xff0c;SSSDT&#xff08;System Service Shadow Descriptor Table&#xff09;是SSDT&#xff08;System Service Descriptor Table&#xff09;的一种变种&#xff0c;其主要用途是提供Windows系统对系统服务调用的阴影拷贝。SSSDT表存储了系统调用的函数…...

实时LCM的ImgPilot搭建部署

ImgPilot是具有实时潜在一致性模型&#xff08;LCM&#xff09;功能的图像试点 下载源码 GitHub - leptonai/imgpilot: Image pilot with the power of Real-Time Latent Consistency Modelhttps://github.com/leptonai/imgpilot安装前端web cd imgpilot npm install 安装…...

开源与闭源:大模型未来的发展之争

在当今数字化时代&#xff0c;开源与闭源软件一直是技术界争论的热点话题。随着人工智能技术的快速发展&#xff0c;特别是大模型&#xff08;如GPT-4等&#xff09;的广泛应用&#xff0c;这个辩论在大模型技术的背景下变得更加引人注目。本文将探讨开源与闭源的优劣势比较&am…...

linux系统初始化本地git,创建ssh-key

step1, 在linux系统配置你的git信息 sudo apt install -y git//step1 git config --global user.name your_name // github官网注册的用户名 git config --global user.email your_email //gitub官网注册绑定的邮箱 git config --list //可以查看刚才你的配置内容…...

JDBC 操作 SQL Server 时如何传入列表参数

本文是作为将要对 PostgreSQL 的 in, any() 操作的一个铺垫&#xff0c;也是对先前用 JDBC 操作 SQL Server 的温习。以此记录一下用 JDBC 查询 SQL Server 时如何传递一个列表参数。比如想像一下查询语句 select * from users where id in (?) 我们是否能给这里的问题参数传递…...

[算法总结] - 蓄水池采样算法

问题描述 在长度为N的数组中&#xff0c;随机等概率选取K个元素&#xff0c;如何实现这个随机算法。 思路很简单&#xff0c;生成一个[0, N]的随机数index&#xff0c;然后返回index上的数值即可。 但是&#xff0c;如果输入是一个长度未知的数组比如stream&#xff0c;先遍历…...

【Dockerfile】将自己的项目构建成镜像部署运行

目录 1.Dockerfile 2.镜像结构 3.Dockerfile语法 4.构建Java项目 5.基于Java8构建项目 1.Dockerfile 常见的镜像在DockerHub就能找到&#xff0c;但是我们自己写的项目就必须自己构建镜像了。 而要自定义镜像&#xff0c;就必须先了解镜像的结构才行。 2.镜像结构 镜…...

flink和机器学习模型的常用组合方式

背景 flink是一个低延迟高吞吐的系统&#xff0c;每秒处理的数据量高达数百万&#xff0c;而机器模型一般比较笨重&#xff0c;虽然功能强大&#xff0c;但是qps一般都比较低&#xff0c;日常工作中&#xff0c;我们一般是如何把flink和机器学习模型组合起来一起使用呢? fli…...

自动驾驶学习笔记(十二)——定位技术

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 卫星定位 RTK定位 IMU定位 GNSS定…...

【MySQL系列】PolarDB入门使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

第二节HarmonyOS DevEco Studio创建项目以及界面认识

一、创建项目 如果你是首次打开DevEco Studio&#xff0c;那么首先会进入欢迎页。 在欢迎页中单击Create Project&#xff0c;进入项目创建页面。 选择‘Application’&#xff0c;然后选择‘Empty Ability’&#xff0c;单击‘Next’进入工程配置页。 配置页中&#xff0c;详…...

网页设计--第5次课后作业

1、快速学习JavaScript的基本知识第11-14章 JavaScript入门 - 绿叶学习网 2、使用所学的知识完成以下练习。 1&#xff09;点击 “点亮”按钮 点亮灯泡&#xff0c;点击“熄灭”按钮 熄灭灯泡 2&#xff09;输入框鼠标聚焦后&#xff0c;展示小写&#xff1b;鼠标离焦后…...

Spring Cache框架,实现了基于注解的缓存功能。

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Spring Cache框架 简介Spring Cache 环境准备S…...

CSS-鼠标属性篇

属性名&#xff1a;cursor 功能&#xff1a;设置鼠标光标的样式 属性值&#xff1a; pointer&#xff1a;小手move&#xff1a;移动图标text&#xff1a;文字选择器crosshair&#xff1a;十字架wait&#xff1a;等待help&#xff1a;帮助 eg.html{ cursor: wait;}(此处使用css改…...

Fiddler弱网测试究竟该怎么做?

前言 使用Fiddler对手机App应用进行抓包&#xff0c;可以对App接口进行测试&#xff0c;也可以了解App传输中流量使用及请求响应情况&#xff0c;从而测试数据传输过程中流量使用的是否合理。 抓包过程&#xff1a; 1、Fiddler设置 1&#xff09;启动Fiddler->Tools->…...

蓝桥杯-平方和(599)

【题目】平方和 【通过测试】代码 import java.util.Scanner; import java.util.ArrayList; import java.util.List; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);//在此…...

从零构建属于自己的GPT系列1:预处理模块(逐行代码解读)、文本tokenizer化

1 训练数据 在本任务的训练数据中&#xff0c;我选择了金庸的15本小说&#xff0c;全部都是txt文件 数据打开后的样子 数据预处理需要做的事情就是使用huggingface的transformers包的tokenizer模块&#xff0c;将文本转化为token 最后生成的文件就是train_novel.pkl文件&a…...

STM32内存介绍

ROM是一种只读存储器&#xff0c;经历了从NOR Flash到NAND Flash再到现在的eMMC的发展。为了便于使用和大批量生产&#xff0c;ROM进一步分为了4种类型&#xff1a;PROM、EPROM、EEPROM和Flash。PROM只能被编程一次&#xff0c;EPROM可擦写可编程且可达1000次&#xff0c;EEPRO…...

Qt::Window 、Qt::Tool是 Qt 框架中的一个窗口标志(Window Flag),用于指定窗口的类型和行为

Qt::Window Qt::Window 是 Qt 框架中的一个窗口标志&#xff08;Window Flag&#xff09;&#xff0c;用于指定窗口的类型和行为。 在 Qt 中&#xff0c;窗口标志用于控制窗口的外观、行为和交互方式。通过使用不同的窗口标志组合&#xff0c;可以定制窗口的特性&#xff0c;…...

东胜物流软件 SQL注入漏洞复现

0x01 产品简介 东胜物流软件是一款致力于为客户提供IT支撑的 SOP&#xff0c; 帮助客户大幅提高工作效率&#xff0c;降低各个环节潜在风险的物流软件。 0x02 漏洞概述 东胜物流软件 TCodeVoynoAdapter.aspx、/TruckMng/MsWlDriver/GetDataList、/MvcShipping/MsBaseInfo/Sav…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...