系統呼叫篇——SSDT

寂靜的羽夏發表於2021-11-17

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問一個問題,你明確學系統呼叫的目的了嗎? 沒有的話就不要繼續了,請重新學習 羽夏看Win系統核心——系統呼叫篇 裡面的內容。


? 華麗的分割線 ?


SSDT

  SSDT的全稱是System Services Descriptor Table,意為系統服務描述符表。在32位XP中,我們可以通過ETHREAD結構體加偏移的方式進行訪問。在核心檔案中,有一個變數是匯出的:KeServiceDescriptorTable。通過它我們可以訪問SSDT

系統呼叫篇——SSDT

  我們在WinDbg看一看SSDT是什麼樣子:

kd> dd KeServiceDescriptorTable
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000
80553fe0  00002730 bf80c0b6 00000000 00000000
80553ff0  bad6da80 ba0deb60 89c3e0f0 ba6bf8e8
80554000  00000000 00000000 00000000 00000000
80554010  0ceb6c40 01d7db79 00000000 00000000

  SSDT有四個成員,每個成員都是一張系統服務表。根據系統服務表的結構,它有四個四位元組的成員,第一個是函式地址表,第二個是呼叫次數,第三個是函式個數,最後一個是引數個數表,我們之前用過ReadProcessMemory做的實驗,它的服務號是0xBA,我們做一下測試:

kd> dd 80502b8c+ba*4
80502e74  805aa712 805c99e0 8060ea76 8060c43c
80502e84  8056f0d2 8063ab56 8061aca8 8061d332
80502e94  8059b804 8059c7cc 8059c1d4 8059baee
80502ea4  805bf456 80598d62 8059908e 805bf264
80502eb4  806064b6 8051ee82 8061cc3e 805cbd40
80502ec4  805cbc22 8061cd3a 8061ce20 8061cf48
80502ed4  8059a07c 8060db50 8060db50 805c892a
80502ee4  8063d80e 8060be28 80607fb8 8060882a
kd> uf 805aa712
805aa712 6a1c            push    1Ch
805aa714 68d8a44d80      push    offset nt!MmClaimParameterAdjustDownTime+0x90 (804da4d8)
805aa719 e8f2e7f8ff      call    nt!_SEH_prolog (80538f10)
805aa71e 64a124010000    mov     eax,dword ptr fs:[00000124h]
805aa724 8bf8            mov     edi,eax
805aa726 8a8740010000    mov     al,byte ptr [edi+140h]
805aa72c 8845e0          mov     byte ptr [ebp-20h],al
805aa72f 8b7514          mov     esi,dword ptr [ebp+14h]
805aa732 84c0            test    al,al
805aa734 7466            je      nt!NtReadVirtualMemory+0x8a (805aa79c)  Branch

kd> db 80503000+ba
805030ba  14 04 08 0c 14 08 08 0c-08 10 14 08 04 08 0c 04  ................
805030ca  08 0c 0c 04 08 08 0c 0c-24 08 08 08 0c 04 08 04  ........$.......
805030da  10 08 04 04 04 14 14 10-10 10 10 10 10 08 14 18  ................
805030ea  04 04 10 0c 08 14 0c 0c-08 08 1c 0c 04 18 14 04  ................
805030fa  10 04 04 04 08 18 08 08-08 00 10 10 04 04 08 14  ................
8050310a  10 08 08 10 14 0c 04 04-24 24 18 14 00 10 0c 10  ........$$......
8050311a  10 00 00 00 00 00 cc cc-cc cc cc cc cc cc 0f 57  ...............W
8050312a  c0 b8 40 00 00 00 0f 2b-41 00 0f 2b 41 10 0f 2b  ..@....+A..+A..+

  是不是逐個對應起來了?那麼我們如何在驅動程式使用呢?我們只需要在驅動程式輸入這行程式碼宣告即可。

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

  注意,PKSERVICE_TABLE_DESCRIPTOR是自己構造的系統服務表指標,結構體需要自行編寫,我們來測試一下:

#include <ntddk.h>

extern ULONG KeServiceDescriptorTable;    //只是為了訪問地址,就不寫那個結構體了

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    DbgPrint("解除安裝成功!!!");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    DriverObject->DriverUnload = UnloadDriver;

    DbgPrint("SSDT 的地址:%X", KeServiceDescriptorTable);
    return STATUS_SUCCESS;
}

  演示效果如下:

系統呼叫篇——SSDT

  如果你細心地發現,我們作業系統系統服務表不是用了兩個嗎?的確,但SSDT並沒有匯出與GUI相關的服務表。那麼如何別的方式找到呢?接下來我們來介紹SSDTShadow

SSDTShadow

  SSDTShadowSSDT不一樣的是它並沒有從核心檔案匯出。不過我們還是可以從WinDbg找到它:

kd> dd KeServiceDescriptorTableShadow
80553f60  80502b8c 00000000 0000011c 80503000
80553f70  bf999b80 00000000 0000029b bf99a890
80553f80  00000000 00000000 00000000 00000000
80553f90  00000000 00000000 00000000 00000000
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000

  它的結構和SSDT是一模一樣的,只不過它多了一張表,就是少的那個與GUI相關的服務表。那麼我們能不能直接用驅動訪問這張表呢?答案是不行,我們用WinDbg測試一下。

kd> dd bf999b80
ReadVirtual: bf999b80 not properly sign extended
bf999b80  ???????? ???????? ???????? ????????
bf999b90  ???????? ???????? ???????? ????????
bf999ba0  ???????? ???????? ???????? ????????
bf999bb0  ???????? ???????? ???????? ????????
bf999bc0  ???????? ???????? ???????? ????????
bf999bd0  ???????? ???????? ???????? ????????
bf999be0  ???????? ???????? ???????? ????????
bf999bf0  ???????? ???????? ???????? ????????

  嘿嘿,是不是人傻了?根本看不到,是為什麼呢?根據我們學過的段頁的知識很容易地判斷出沒有掛物理頁。並不是每一個應用程式都是有GUI,有的是後臺沒介面的。我們繫結一個GUI程式看一看:

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
(***省略無關程式***)

Failed to get VadRoot
PROCESS 89b28768  SessionId: 0  Cid: 06cc    Peb: 7ffd9000  ParentCid: 05d8
    DirBase: 157001a0  ObjectTable: e1d763f8  HandleCount:  44.
    Image: notepad.exe

kd> .process 89b28768
ReadVirtual: 89b28780 not properly sign extended
Implicit process is now 89b28768
WARNING: .cache forcedecodeuser is not enabled

kd> dd bf999b80
ReadVirtual: bf999b80 not properly sign extended
bf999b80  bf935f7e bf947b29 bf88ca52 bf93f6f0
bf999b90  bf949140 bf936212 bf9362b7 bf83b4cd
bf999ba0  bf948a67 bf934a17 bf94905f bf90f2f4
bf999bb0  bf902318 bf809fdf bf948f31 bf94a72d
bf999bc0  bf900c15 bf893b44 bf94900f bf94a860
bf999bd0  bf820f34 bf8dcb55 bf87a2e4 bf8c29a0
bf999be0  bf91052f bf80e2c5 bf8dc7fd bf94a525
bf999bf0  bf94b430 bf813a71 bf80cf90 bf8d14e4

  是不是可以正常訪問了?我們隨便u一個試試:

kd> u bf935f7e
ReadVirtual: bf935f7e not properly sign extended
bf935f7e ??              ???
             ^ Memory access error in 'u bf935f7e'

  發現不成功,並不代表每一個函式不行,我們再隨便u一個試試:

kd> u bf949140
ReadVirtual: bf949140 not properly sign extended
bf949140 6a5c            push    5Ch
ReadVirtual: bf949150 not properly sign extended
bf949142 68804599bf      push    offset win32k!`string'+0x4dc (bf994580)
ReadVirtual: bf949152 not properly sign extended
bf949147 e8bc7aebff      call    win32k!_SEH_prolog (bf800c08)
bf94914c 33db            xor     ebx,ebx
bf94914e 43              inc     ebx
bf94914f 33f6            xor     esi,esi
bf949151 8975e4          mov     dword ptr [ebp-1Ch],esi
bf949154 39750c          cmp     dword ptr [ebp+0Ch],esi

  SSDTShadow就介紹到這裡了,感興趣地自行繼續研究。

本節練習

本節的答案將會在下一節的正文給出,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

  俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習只有一個,請保質保量的完成。

1️⃣ 寫程式碼保護指定程式(比如記事本),防止別人關閉它,而自己關閉正常退出。

下一篇

  系統呼叫篇——總結與提升

相關文章