Windows系統調用中的系統服務表描述符(SSDT)


 Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.html

Windows系統調用中的系統服務表描述符(SSDT)

  在前面,我們將解過 系統服務表。可是,我們有個疑問,系統服務表存儲在哪里呢?

  答案就是:系統服務表 存儲在 系統服務描述符表中。(其又稱為 SSDT Service Descriptor Table)

  

 

 

 一、使用PELord函數從ntoskrnl.exe文件中查看SSDT導出函數

  如圖,可以看出KeServiceDescriptorTable導出函數。

  通過該函數可以查找SSDT表的位置。

  

 

二、通過Windbg來內存中查看SSDT表

  使用Windbg,可以使用 kd> dd nt!KeServiceDescriptorTable 指令來查看SSDT表。

  但該指令存在缺點,可以看到第二張表為0,說明如果使用KeServiceDescriptorTable這個公開的導出函數,我們無法看到win32k.sys這張表結構

  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

三、驗證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  

  

四、通過修改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 }

 

 五、驅動代碼實現

  其中涉及頁保護問題,可以查看我的博客其他文章,有介紹。

 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 }

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM