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 KiSystemServiceRepeat
nt!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],80h
fffff802`7c1d2ba9 7413 je nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)
fffff802`7c1d2bab f7437800002000 test dword ptr [rbx+78h],200000h
fffff802`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)
{
// 获取高位地址fffff802
memcpy(&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 KiSystemServiceRepeat
nt!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],80h
fffff802`7c1d2ba9 7413 je nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)
fffff802`7c1d2bab f7437800002000 test dword ptr [rbx+78h],200000h
fffff802`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)
{
// 获取高位地址fffff802
memcpy(&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);
// 循环枚举SSSDT
for (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)-0x10000000
ULONGLONG 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表基址的更多相关文章
- 驱动开发:Win10内核枚举SSDT表基址
三年前面朝黄土背朝天的我,写了一篇如何在Windows 7系统下枚举内核SSDT表的文章<驱动开发:内核读取SSDT表基址>三年过去了我还是个单身狗,开个玩笑,微软的Windows 10系 ...
- Windows驱动开发-内核常用内存函数
搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool
- Windows驱动开发(中间层)
Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...
- [Windows驱动开发](一)序言
笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...
- windows驱动开发推荐书籍
[作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...
- windows 驱动开发入门——驱动中的数据结构
最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...
- Windows驱动——读书笔记《Windows驱动开发技术详解》
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- Windows驱动开发-IRP的完成例程
<Windows驱动开发技术详解 >331页, 在将IRP发送给底层驱动或其他驱动之前,可以对IRP设置一个完成例程,一旦底层驱动将IRP完成后,IRP完成例程立刻被处罚,通过设置完成例程 ...
- C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载
基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...
- C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...
随机推荐
- pip 的高阶玩法
pip 的高阶玩法 pip 应该是大家最熟悉的 Python 包安装与管理工具了,但是除了pip install 这个最常用的命令,还有很多有用的玩法.这里就介绍几个我平时会用到的,希望对大家有所帮助 ...
- SpringBoot 项目实战 | 瑞吉外卖 Day03
该系列将记录一份完整的实战项目的完成过程,该篇属于第三天 案例来自B站黑马程序员Java项目实战<瑞吉外卖>,请结合课程资料阅读以下内容 该篇我们将完成以下内容: 公共字段自动填充 新添分 ...
- AtCoder Beginner Contest 242(C~E)
AB 水题 C - 1111gal password 题意:给出 N(\(2\le N\le 1e6\))求满足以下条件的 \(X\) 的数量,需除以模 (\(998244353\)) $X $ 是 ...
- 汇编 | 数据段寄存器DS和[address]
Description CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086PC中,内存地址由段地址和偏移地址组成.8086CPU中有一个DS寄存器,通常用来存放要访问 数据的段地 ...
- P1525 关押罪犯 (并查集 / 二分图)| 二分图伪码
原题链接:https://www.luogu.com.cn/problem/P1525 题目概括: 给你m对关系,每对关系分别涉及到x,y两人,矛盾值为w 请你判断分配x和y到两个集合中,能否避免冲突 ...
- Codeforces Round #725 (Div. 3) A~G 题解记录
补题链接:Here 1538A. Stone Game 数组 \(a\) 的大小为 \(n\) ,请问每次可以删除最左和最右侧的元素,请问最少执行多少次能删除掉数组中的最大值和最小值 (\(1\le ...
- P1439-DP【绿】
轻敌了啊...题目一共只有几句话但我却忽略了一个重大信息... 总之我显示写出了时空复杂度都是n^2级别的朴素递推算法,这没什么,基本功而已,然后50分 我试了试滚动数组,把空间复杂度降到了n级别,但 ...
- 二分图的匹配 hdu 1083
***题意:n个学生,p门课,求最大匹配,即p门课是否都有人上*** 匈牙利算法 #include<iostream> #include<cstdio> #include< ...
- [转帖]TLS/SSL (Schannel SSP) 中的密码套件
https://learn.microsoft.com/zh-cn/windows/win32/secauthn/cipher-suites-in-schannel 密码套件是一组加密算法. TLS/ ...
- [转帖]ramfs、tmpfs、rootfs、ramdisk介绍
ramfs.tmpfs.rootfs.ramdisk介绍 bootleader--->kernel---->initrd(是xz.cpio.是ramfs的一种,主要是驱动和为了加载ro ...