驅動開發:內核枚舉ShadowSSDT基址

在筆者上一篇文章《驅動開發:Win10枚舉完整SSDT地址表》實現了針對SSDT表的枚舉功能 , 本章繼續實現對SSSDT表的枚舉,ShadowSSDT中文名影子系統服務描述表,SSSDT其主要的作用是管理系統中的圖形化界面,其Win32子系統的內核實現是Win32k.sys驅動,屬于GUI線程的一部分 , 其自身沒有導出表,枚舉SSSDT表其與SSDT原理基本一致 。
如下是閉源ARK工具的枚舉效果:

驅動開發:內核枚舉ShadowSSDT基址

文章插圖
首先需要找到SSSDT表的位置 , 通過《驅動開發:Win10內核枚舉SSDT表基址》文章中的分析可知,SSSDT就在SSDT的下面,只需要枚舉4c8d1dde1e3a00特征即可,如果你找不到上一篇具體分析流程了 , 那么多半你是看到了轉載文章 。
驅動開發:內核枚舉ShadowSSDT基址

文章插圖
先實現第一個功能,得到SSSDT表的基地址以及SSDT函數個數,完整代碼如下所示 。
// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include <ntifs.h>#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE{ PVOIDServiceTableBase; PVOIDServiceCounterTableBase; ULONGLONGNumberOfServices; PVOIDParamTableBase;} 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.com kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00lear10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00lear11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000testdword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413jent!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000testdword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407jent!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00lear11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3movr10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 獲取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 與低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}} } return0;}// 得到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.com \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表基地址,以及該表中函數個數 。
驅動開發:內核枚舉ShadowSSDT基址

文章插圖
在此基礎之上增加枚舉計算過程即可,完整源代碼如下所示 。
SSSDT 函數起始index是0x1000,但W32pServiceTable是從基址開始記錄的,這個誤差則需要(index-0x1000)來得到,至于+4則是下一個元素與上一個元素的偏移 。
計算公式:
  • W32pServiceTable + 4 * (index-0x1000)
// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include <ntifs.h>#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE{ PVOIDServiceTableBase; PVOIDServiceCounterTableBase; ULONGLONGNumberOfServices; PVOIDParamTableBase;} 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.com kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00lear10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00lear11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000testdword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413jent!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000testdword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407jent!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00lear11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3movr10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 獲取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 與低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}} } return0;}// 得到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.com \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)-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;}

推薦閱讀