Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
Windows系統調用中的系統服務表描述符(SSDT)ios
在前面,咱們將解過 系統服務表。但是,咱們有個疑問,系統服務表存儲在哪裏呢?
數據結構
答案就是:系統服務表 存儲在 系統服務描述符表中。(其又稱爲 SSDT Service Descriptor Table)
函數
1、使用PELord函數從ntoskrnl.exe文件中查看SSDT導出函數ui
如圖,能夠看出KeServiceDescriptorTable導出函數。spa
經過該函數能夠查找SSDT表的位置。3d
2、經過Windbg來內存中查看SSDT表code
使用Windbg,可使用 kd> dd nt!KeServiceDescriptorTable 指令來查看SSDT表。htm
但該指令存在缺點,能夠看到第二張表爲0,說明若是使用KeServiceDescriptorTable這個公開的導出函數,咱們沒法看到win32k.sys這張表結構blog
kd> dd nt!KeServiceDescriptorTable
83f759c0 83e89d9c 00000000 00000191 83e8a3e4
83f759d0 00000000 00000000 00000000 00000000
83f759e0 83ee86af 00000000 0327aa43 000000bb
83f759f0 00000011 00000100 5385d2ba d717548f
爲了解決上面這個問題,咱們只能使用另一個指令,該指令對應的是一個未公開導出的函數。
以下,能夠看到其第二行,win32k.sys系統服務表已經可見。
kd> dd KeServiceDescriptorTableShadow
83f75a00 83e89d9c 00000000 00000191 83e8a3e4
83f75a10 83b66000 00000000 00000339 83b6702c
83f75a20 00000000 00000000 83f75a24 00000340
83f75a30 00000340 855e8440 00000007 00000000
3、驗證ReadMemory真正的內核實現部分
咱們在這篇《Windows系統調用中API的三環部分(依據分析重寫ReadProcessMemory函數)》中曾提到過直接使用快速調用來摒棄R3層層封裝的API,其中給eax一個函數號,如今咱們來實戰刨析一下。
mov eax, 0x115 mov edx, 0X7FFE0300
以下,系統描述符的數據結構,其依次分別爲
其依次分別爲 ServiceTable 83e89d9c,Count 00000000,ServiceLimit 00000191,ServiceTable 83e8a3e4
使用Windbg來查看其115h序號的函數地址 115h*4 + 83e89d9c (ServiceTable)
獲得函數地址爲 8406c82c
kd> dd 115h*4 + 83e89d9c
83e8a1f0 8406c82c 840feb46 83fb488c 83fb6128
再對此進行反彙編可得
kd > u 8406c82c
nt!NtReadVirtualMemory:
8406c82c 6a18 push 18h
8406c82e 68282ae683 push offset nt!? ? ::FNODOBFM::`string'+0x3ea8 (83e62a28)
8406c833 e870e3e1ff call nt!_SEH_prolog4(83e8aba8)
8406c838 648b3d24010000 mov edi, dword ptr fs : [124h]
8406c83f 8a873a010000 mov al, byte ptr[edi + 13Ah]
8406c845 8845e4 mov byte ptr[ebp - 1Ch], al
8406c848 8b7514 mov esi, dword ptr[ebp + 14h]
8406c84b 84c0 test al, al
以後,咱們查看該nt!NtReadVirtualMemory函數的參數個數
kd > db 83e8a3e4 + 115
83e8a4f9 14 08 04 04 14 04 10 08 - 0c 04 14 18 08 08 08 0c
83e8a509 0c 08 10 14 08 08 0c 08 - 0c 0c 04 08 08 08 08 08
83e8a519 08 0c 0c 24 00 08 08 08 - 0c 04 08 04 08 10 08 04
4、經過修改SSDT表增添系統服務函數
咱們在 Windows系統調用中API的三環部分(依據分析重寫ReadProcessMemory函數) 調用的是 115h 號函數。
如今,咱們將該函數地址放到 191 號函數處(以前一共有191個函數,佔據0-190位)。
修改思路:
1)將 nt!NtReadVirtualMemory 函數地址 8406c82c 放到 191號處(83e89d9 + 191h*4)
kd> ed 83e89d9 + 191h*4 8406c82c
2) 增大 服務表最大個數。 (由於咱們上一節分析其反彙編代碼的時候,發現其會進行最大個數的判斷)
kd> ed 83f75a00+8 192
3) 修改參數個數表中對應的191號參數個數。(咱們以前查閱過其爲 14,以字節爲單位)
kd> eb 83e8a3e4+191 14
4) 以後,咱們運行下列代碼。其與《Windows系統調用中API的三環部分(依據分析重寫ReadProcessMemory函數)》惟一的不一樣調用函數號爲192,最終效果徹底同樣。
1 #include "pch.h" 2 #include <iostream> 3 #include <algorithm> 4 #include <Windows.h> 5 void ReadMemory(HANDLE hProcess, PVOID pAddr, PVOID pBuffer, DWORD dwSize, DWORD *dwSizeRet) 6 { 7 8 _asm 9 { 10 lea eax, [ebp + 0x14] 11 push eax 12 push[ebp + 0x14] 13 push[ebp + 0x10] 14 push[ebp + 0xc] 15 push[ebp + 8] 16 sub esp, 4 17 mov eax, 0x192 // 注意:修改的是這裏 18 mov edx, 0X7FFE0300 //sysenter不能直接調用,我間接call的 19 CALL DWORD PTR[EDX] 20 add esp, 24 21 22 } 23 } 24 int main() 25 { 26 HANDLE hProcess = 0; 27 int t = 123; 28 DWORD pBuffer; 29 //hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0,a); 30 ReadMemory((HANDLE)-1, (PVOID)&t, &pBuffer, sizeof(int), 0); 31 printf("%X\n", pBuffer); 32 ReadProcessMemory((HANDLE)-1, &t, &pBuffer, sizeof(int), 0); 33 printf("%X\n", pBuffer); 34 35 getchar(); 36 return 0; 37 }
5、驅動代碼實現
其中涉及頁保護問題,能夠查看個人博客其餘文章,有介紹。
1 /* 2 利用IDT_HOOK技術,將115h號函數地址掛靠在191h處。 3 */ 4 5 #include <ntddk.h> 6 #include <ntstatus.h> 7 // 系統服務表的結構體 8 typedef struct _KSYSTEM_SERVICE_TABLE 9 { 10 PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址 11 PULONG ServiceCounterTableBase; // 用於 checked builds, 包含 SSDT 中每一個服務被調用的次數 12 ULONG NumberOfService; // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小 13 // ParamTableBase[0x115],一個字節一個數,所以要使用PUCHAR數據類型而不是PULONG 14 PUCHAR ParamTableBase // SSPT(System Service Parameter Table)的基地址 15 } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; 16 // SSDT結構體 17 typedef struct _KSERVICE_TABLE_DESCRIPTOR 18 { 19 KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服務函數 20 KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服務函數(GDI32.dll/User32.dll 的內核支持) 21 KSYSTEM_SERVICE_TABLE notUsed1; 22 KSYSTEM_SERVICE_TABLE notUsed2; 23 }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; 24 // KeServiceDescriptorTable 這個變量名不能改,其爲 ntoskrnl.exe 導出的變量名 25 extern PKSYSTEM_SERVICE_TABLE KeServiceDescriptorTable; 26 //關閉內存保護裸函數 27 void _declspec(naked)OffMemoryProtect() 28 { 29 __asm { //關閉內存保護 30 push eax; 31 mov eax, cr0; 32 and eax, ~0x10000; 33 mov cr0, eax; 34 pop eax; 35 ret; 36 } 37 } 38 //開啓內存保護裸函數 39 void _declspec(naked)OnMemoryProtect() 40 { 41 __asm { //恢復內存保護 42 push eax; 43 mov eax, cr0; 44 or eax, 0x10000; 45 mov cr0, eax; 46 pop eax; 47 ret; 48 } 49 } 50 51 // 驅動卸載函數 52 NTSTATUS DriverUnload(PDRIVER_OBJECT Driver) { 53 return STATUS_SUCCESS; 54 } 55 56 // 驅動入口函數 57 NTSTATUS DriverEntry(PDRIVER_OBJECT Driver, PUNICODE_STRING RegPath) { 58 NTSTATUS status; 59 60 Driver->DriverUnload = DriverUnload; 61 DbgPrint("---> %x", KeServiceDescriptorTable); 62 63 // 關閉內存保護 64 OffMemoryProtect(); 65 66 // 修改服務表的最大個數 67 KeServiceDescriptorTable->NumberOfService++; 68 // 增長一個新地址 69 KeServiceDescriptorTable->ServiceTableBase[0x191] = KeServiceDescriptorTable->ServiceTableBase[0x115]; 70 // 將參數地址也給同步更新 71 KeServiceDescriptorTable->ParamTableBase[0x191] = KeServiceDescriptorTable->ParamTableBase[0x115]; 72 73 // 開啓內存保護 74 OnMemoryProtect(); 75 return STATUS_SUCCESS; 76 }