驅動開發:Win10內核枚舉SSDT表基址

三年前面朝黃土背朝天的我,寫了一篇如何在Windows 7系統下枚舉內核SSDT表的文章《驅動開發:內核讀取SSDT表基址》三年過去了我還是個單身狗,開個玩笑,微軟的Windows 10系統已經覆蓋了大多數個人PC終端,以前的方法也該進行迭代更新了,或許在網上你能夠找到類似的文章,但我可以百分百肯定都不能用,今天LyShark將帶大家一起分析Win10 x64最新系統SSDT表的枚舉實現 。
看一款閉源ARK工具的枚舉效果:

驅動開發:Win10內核枚舉SSDT表基址

文章插圖
直接步入正題 , 首先SSDT表中文為系統服務描述符表,SSDT表的作用是把應用層與內核聯系起來起到橋梁的作用,枚舉SSDT表也是反內核工具最基本的功能,通常在64位系統中要想找到SSDT表,需要先找到KeServiceDescriptorTable這個函數,由于該函數沒有被導出 , 所以只能動態的查找它的地址 , 慶幸的是我們可以通過查找msr(c0000082)這個特殊的寄存器來替代查找KeServiceDescriptorTable這一步,在新版系統中查找SSDT可以歸納為如下這幾個步驟 。
  • rdmsr c0000082 -> KiSystemCall64Shadow -> KiSystemServiceUser -> SSDT
首先第一步通過rdmsr C0000082 MSR寄存器得到KiSystemCall64Shadow的函數地址 , 計算KiSystemCall64ShadowKiSystemServiceUser偏移量 , 如下圖所示 。
  • 得到相對偏移6ed53180(KiSystemCall64Shadow) - 6ebd2a82(KiSystemServiceUser) = 1806FE
  • 也就是說 6ed53180(rdmsr) - 1806FE = KiSystemServiceUser

驅動開發:Win10內核枚舉SSDT表基址

文章插圖
【驅動開發:Win10內核枚舉SSDT表基址】如上當我們找到了KiSystemServiceUser的地址以后 , 在KiSystemServiceUser向下搜索可找到KiSystemServiceRepeat里面就是我們要找的SSDT表基址 。
其中fffff8036ef8c880則是SSDT表的基地址,緊隨其后的fffff8036ef74a80則是SSSDT表的基地址 。
驅動開發:Win10內核枚舉SSDT表基址

文章插圖
那么如果將這個過程通過代碼的方式來實現,我們還需要使用《驅動開發:內核枚舉IoTimer定時器》中所使用的特征碼定位技術,如下我們查找這段特征 。
// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include <ntifs.h>#pragma intrinsic(__readmsr)ULONGLONG ssdt_address = 0;// 獲取 KeServiceDescriptorTable 首地址ULONGLONG GetLySharkCOMKeServiceDescriptorTable(){ // 設置起始位置 PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1806FE; // 設置結束位置 PUCHAR EndSearchAddress = StartSearchAddress + 0x100000; DbgPrint("[LyShark Search] 掃描起始地址: %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 函數地址/*nt!KiSystemServiceRepeat:fffff803`6ebd2b94 4c8d15e59c3b00lear10,[nt!KeServiceDescriptorTable (fffff803`6ef8c880)]fffff803`6ebd2b9b 4c8d1dde1e3a00lear11,[nt!KeServiceDescriptorTableShadow (fffff803`6ef74a80)]fffff803`6ebd2ba2 f7437880000000testdword ptr [rbx+78h],80hfffff803`6ebd2ba9 7413jent!KiSystemServiceRepeat+0x2a (fffff803`6ebd2bbe)Branch*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x15){// 獲取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 與低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}} } return0;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("驅動程序卸載成功! \n"));}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark.com"); ssdt_address = GetLySharkCOMKeServiceDescriptorTable(); DbgPrint("[LyShark] SSDT = %p \n", ssdt_address); DriverObject->DriverUnload = UnDriver; return STATUS_SUCCESS;}如上代碼中所提及的步驟我想不需要再做解釋了,這段代碼運行后即可輸出SSDT表的基址 。

推薦閱讀