文章目錄:html
1. 引子 – Hook 技術:前端
2. SSDT 簡介:數組
3. 應用層調用 Win32 API 的完整執行流程:安全
4. 詳解 SSDT:函數
5. SSDT Hook 原理:工具
6. 小結:post
1. 引子 – Hook 技術:學習
前面一篇博文呢介紹了代碼的注入技術(遠程線程實現),博文地址以下:ui
http://www.cnblogs.com/BoyXiao/archive/2011/08/11/2134367.htmlurl
雖然代碼注入是很老的技術了,可是這種技術也仍是比較常見,
固然也比較好用的,好比在 Spy++ 中就使用了遠程線程注入技術,
同時,若是有興趣的閱讀過 Spy++ 的源碼的朋友,固然也能夠在其源碼中閱讀到關於遠程線程注入技術了。
(這篇博文雖然我會截斷分爲兩篇博文撰寫,可是博文仍然會比較長,內容實際上是比較多的,覆蓋面也比較廣,
須要有必定耐心和基礎方可閱讀完,有興趣者請自備茶水以及零食,而後慢慢閱讀全文,
PS:這話引用自園子裏某位園友)
(而後的話就是漫漫長夜,心情不佳,因而寫了篇博文,恰好又喝了點,因此估計會有些許疏漏之處,還請見諒 ~)
在這一篇博文中呢,介紹的是一種 Hook 技術,對於 Hook 技術,能夠分爲兩塊,
第一塊是在 Ring3 層的 Hook,俗稱應用層 Hook 技術,
而另一塊天然是在 Ring0 層得 Hook,俗稱爲內核層 Hook 技術,
而在 Ring3 層的 Hook 基本上能夠分爲兩種大的類型,
第一類便是 Windows 消息的 Hook,第二類則是 Windows API 的 Hook。
關於 Hook 的幾種類型呢,下面給出幾個簡潔的圖示:
關於 Windows 消息的 Hook,相信不少朋友都有接觸過的,由於一個 SetWindowsHookEx 便可以完成消息 Hook,
在這裏簡要介紹一下消息 Hook,消息 Hook 是經過 SetWindowsHookEx 能夠實現將本身的鉤子插入到鉤子鏈的最前端,
而對於發送給被 Hook 的窗口(也有多是全部的窗口,即全局 Hook)的消息都會被咱們的鉤子處理函數所捕獲到,
也就是咱們能夠優先於窗體先捕獲到這些消息,Windows 消息 Hook 能夠實現爲進程內消息 Hook 和全局消息 Hook,
對於進程內消息 Hook,則能夠簡單的將 Hook 處理函數直接寫在這個進程內,便是本身 Hook 本身,
而對於用途更爲普遍的全局消息 Hook,則須要將 Hook 處理函數寫在一個 DLL 中,
這樣纔可讓你的處理函數被全部的進程所加載(進程自動加載包含 Hook 消息處理函數的 DLL)。
對於 Windows 消息 Hook 呢,能夠有個簡單的邪惡應用,就是記錄鍵盤按鍵消息,
從而達到監視用戶輸入的鍵值信息的目的,這樣,對於一些簡單的用戶經過鍵盤輸入的密碼就能夠被 Hook 獲取到,
由於沒當用戶按下一個鍵時,Windows 都會產生一個按鍵消息(固然有按下,彈起等消息的區分),
而後咱們能夠 Hook 到這個按鍵消息,這樣就能夠在 Hook 的消息處理函數中獲取到用戶按下的是什麼鍵了。
固然關於消息 Hook 的話,其不是這篇博文的重點,
這篇博文主要介紹的是 SSDT Hook 技術,即內核 Hook 技術的一種,
這種技術呢,也是比較老的技術了,貌似是當年 Rootkit 起火的時候出來的,
可是 SSDT Hook 如今也還比較流行,好比在不少的殺毒軟件或者安全軟件裏面也都會使用到 SSDT Hook 技術。
關於內核 Hook 也有幾種類型,下面也給出一副圖示:
上面的幾種內核級 Hook 技術,在看雪啊,debugman,xfocus 上都有不少的介紹,
而我只不過是落後這些技術不少年的小輩後生,在這裏也只是將本身的學習以及一些總結的經驗給列出來而已,
若是有興趣想深刻了解這些內容的話,徹底能夠在看雪上找到資料 ~
2. SSDT 簡介:
如下介紹來自百度(PS:被百度文庫弄去了不少博文,這裏也抄它一下):
SSDT 的全稱是 System Services Descriptor Table,系統服務描述符表。
這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的內核 API 聯繫起來。
SSDT 並不只僅只包含一個龐大的地址索引表,它還包含着一些其它有用的信息,諸如地址索引的基地址、服務函數個數等。
經過修改此表的函數地址能夠對經常使用 Windows 函數及 API 進行 Hook,從而實現對一些關心的系統動做進行過濾、監控的目的。
一些 HIPS、防毒軟件、系統監控、註冊表監控軟件每每會採用此接口來實現本身的監控模塊。
在 NT 4.0 以上的 Windows 操做系統中,默認就存在兩個系統服務描述表,這兩個調度表對應了兩類不一樣的系統服務,
這兩個調度表爲:KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow,
其中 KeServiceDescriptorTable 主要是處理來自 Ring3 層得 Kernel32.dll 中的系統調用,
而 KeServiceDescriptorTableShadow 則主要處理來自 User32.dll 和 GDI32.dll 中的系統調用,
而且 KeServiceDescriptorTable 在 ntoskrnl.exe(Windows 操做系統內核文件,包括內核和執行體層)是導出的,
而 KeServiceDescriptorTableShadow 則是沒有被 Windows 操做系統所導出,
而關於 SSDT 的所有內容則都是經過 KeServiceDescriptorTable 來完成的 ~
從下面的截圖能夠看出 KeServiceDescriptorTable 在 ntoskrnl.exe 中被導出:
而後咱們再來看看在 Windows 操做系統的源碼 WRK 中,KeServiceDescriptorTable 是怎麼被定義的 ~
首先來看 KeServiceDescriptorTable 是如何被 Windows 操做系統源碼給導出的:
從下面的截圖能夠看出,這個系統服務描述表是在 WRK 源碼中的某一個模塊劃分文件(.def)中所導出的。
關於 WRK 是什麼東西 ? 則能夠參閱個人另外一篇博文《Windows 內核(WRK)簡介》,博文地址以下:
http://www.cnblogs.com/BoyXiao/archive/2011/01/08/1930904.html
而在 Windows 源碼 WRK 中對於系統服務描述符表的代碼定義以下(KeServiceDecriptorTable 即由該結構定義):
上面的這個結構定義在成員變量的名稱上還看不出什麼名堂,下面給出咱們將在本身代碼中所使用的結構體:
1: typedef struct _KSYSTEM_SERVICE_TABLE
2: {
3: PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
4: PULONG ServiceCounterTableBase; // 用於 checked builds, 包含 SSDT 中每一個服務被調用的次數
5: ULONG NumberOfService; // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小
6: ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
7:
8: } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
9:
10: typedef struct _KSERVICE_TABLE_DESCRIPTOR
11: {
12: KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服務函數
13: KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服務函數(GDI32.dll/User32.dll 的內核支持)
14: KSYSTEM_SERVICE_TABLE notUsed1;
15: KSYSTEM_SERVICE_TABLE notUsed2;
16:
17: } KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
18:
19: //導出由 ntoskrnl.exe 所導出的 SSDT
20: extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
有了上面的介紹後,咱們能夠簡單的將 KeServiceDescriptor 看作是一個數組了(其實質也就是個數組),
在應用層 ntdll.dll 中的 API 在這個系統服務描述表(SSDT)中都存在一個與之相對應的服務,
當咱們的應用程序調用 ntdll.dll 中的 API 時,最終會調用內核中與之相對應的系統服務,
因爲有了 SSDT,因此咱們只須要告訴內核須要調用的服務所在 SSDT 中的索引就 OK 了,
而後內核根據這個索引值就能夠在 SSDT 中找到相對應的服務了,而後再由內核調用服務完成應用程序 API 的調用請求便可。
基本結構能夠參考下圖:
3. 應用層調用 Win32 API 的完整執行流程:
有了上面的 SSDT 基礎後,咱們再來看一下在應用層調用 Win32 API(這裏主要指的是 ntdll.dll 中的 API)的完整流程,
這裏咱們主要是分析 ntdll.dll 中的 NtQuerySystemInformation 這個 API 的調用流程,
(PS:Windows 任務管理器便是經過這個 API 來獲取到系統的進程等等信息的)。
先給出一副圖示(先記住這裏有四個相似的 API,可是必須得注意區分開來,弄混淆了就麻煩大了):
再給出這些個 API 的基本的調用流程(讓大夥有個印象,至少不會迷失):
首先,使用 PE 工具來打開 ntdll.dll 文件,能夠看到 NtQuerySystemInformation,
除了 NtQuerySystemInformation 外,同時還能夠看到 ZwQuerySystemInformation,
而實質上,在 Windows 操做系統中,
Ntdll.dll 中的ZwQuerySystemInformation 和 NtQuerySystemInformation 是同一函數,
能夠經過下面的截圖看出,這兩個函數的入口地址指向同一區域,他們的函數入口地址都是同樣的 ~
很奇怪吧 ~ 其實我也以爲奇怪 ~ 何須畫蛇添足呢 ~
衆所周知 Ntdll.dll 中的 API 都只不過是一個簡單的包裝函數而已,
當 Kernel32.dll 中的 API 經過 Ntdll.dll 時,會完成參數的檢查,
再調用一箇中斷(int 2Eh 或者 SysEnter 指令),從而實現從 Ring3 進入 Ring0 層,
而且將所要調用的服務號(也就是在 SSDT 數組中的索引值)存放到寄存器 EAX 中,
而且將參數地址放到指定的寄存器(EDX)中,再將參數複製到內核地址空間中,
再根據存放在 EAX 中的索引值來在 SSDT 數組中調用指定的服務 ~
通過上面的步驟後,便由 Ring3 層進入了 Ring0 層,
咱們再經過 PE 工具來查看 ntoskrnl.exe 中的 ZwQuerySystemInformation 和 NtQuerySystemInformation
先來看 ntoskrnl.exe 中的 ZwQuerySystemInformation:
在上面的這幅截圖中,能夠看到在 Ring0 下的 ZwQuerySystemInformation 將 0ADh 放入了寄存器 eax 中,
而後調用了系統服務分發函數 KiSystemService,而這個 KiSystemService 函數則是根據 eax 寄存器中的索引值,
而後再 SSDT 數組中找到索引值爲 eax 寄存器中存放的值得那個 SSDT 項,
最後就是根據這個 SSDT 項中所存放的系統服務的地址來調用這個系統服務了 ~
好比在這裏就是調用 KeServiceDescriptorTable[0ADh] 處所保存的地址所對應的系統服務了 ~
也就是調用 Ring0 下的 NtQuerySystemInformation 了 ~
至此,在應用層中調用 NtQuerySystemInformation 的所有流程也就結束了 ~
最後,貼出一點在 Ring0 下的 NtQuerySystemInformation 的反彙編代碼:
4. 詳解 SSDT:
在這一節裏面,咱們未來看看 SSDT 究竟是個什麼東西 ~ 這裏使用 WinDbg 來調試 XP SP2 系統 ~
首先來看看 KeServiceDescriptorTable 是何物 ?
從下面的截圖中能夠看到 KeServiceDesciptorTable 的首地址爲 804e58a0,
而後查看分析這個地址,能夠查看到第一個系統服務的入口地址爲 80591bfb !
咱們再來看看 80591bfb 這個地址對應的到底是何系統服務 ?
從下面的截圖中,能夠看到 SSDT 中第一個系統服務就是 NtAcceptConnectPort !!!
因爲咱們知道了 SSDT 的首地址,又知道了 Ring0 下 NtQuerySystemInformation 服務的索引號,
因此能夠根據 「SSDT 中系統服務地址所在的 Address = SSDT 首地址 + 4 * 索引號」,
推算出 NtQuerySystemInformation 服務的地址,
所以有 Address = 804e58a0 + 4 * 0adh = 804E5B54;
而後咱們再來看 804E5B54 這個地址的信息,信息以下截圖:
從截圖中,咱們能夠看到 NtQuerySystemInformation 的起始地址爲 80586ff1,
下面就來驗證一下地址 80586ff1 究竟是不是 NtQuerySystemInformation 的首地址 ~
從下面的截圖中能夠確定 80586ff1 確實就是 NtQuerySystemInformation 的首地址,
這和咱們上面對 SSDT 中指定索引號的服務的地址的計算公式計算出來的結果是統一的 !!!
從上面的介紹,能夠看出,其實 SSDT 就是一個用來保存 Windows 系統服務地址的數組而已 !!!
5. SSDT Hook 原理:
有了上面的這部分基礎後,就能夠來看 SSDT HOOK 的原理了,
其實 SSDT Hook 的原理是很簡單的,從上面的分析中,
咱們能夠知道在 SSDT 這個數組中呢,保存了系統服務的地址,
好比對於 Ring0 下的 NtQuerySystemInformation 這個系統服務的地址,
就保存在 KeServiceDescriptorTable[0ADh] 中,
既然是 Hook 的話,咱們就能夠將這個 KeServiceDescriptorTable[0ADh] 下保存的服務地址替換掉,
將咱們本身的 Hook 處理函數的地址來替換掉原來的地址,
這樣當每次調用 KeServiceDescriptorTable[0ADh]時就會調用咱們本身的這個 Hook 處理函數了。
下面用幾幅截圖來表示:
下面的截圖則是 SSDT Hook 以後了,能夠看到將 SSDT 中的服務地址修改成 MyHookNtQuerySystemInformation 了,
這樣的話,每次系統調用 NtQuerySystemInformation 這個系統服務時,
實質上調用的就是 MyHookNtQuerySystemInformation 了,而咱們爲了保證系統的穩定性(至少不讓其崩潰),
通常會在 MyHookNtQuerySystemInformation 中調用系統中原來的服務,也就是 NtQuerySystemInformation。
6. 小結:
本篇博文呢尚還只是介紹了 SSDT 究竟是個什麼東西,而尚未給出具體的 SSDT Hook 的實現,
對於 SSDT Hook 的實現以及 Demo 我都放到(二)中完成,也就是本篇博文未完 , 待續 ……
關於 SSDT 的話,在看雪上有不少的文章,因爲我也是前陣子對這東西忽然感興趣了,
因此我也算是初次瞭解,天然也看過了不少的文章,SSDT 在 Google 一搜索能夠出來一大堆,
可是要說介紹 SSDT 最詳細的話,我想仍是個人這篇文章介紹的比較詳細,
由於網上不少介紹 SSDT 的都只是將 SSDT 原理作了簡單的介紹,而後在網上 down 一個 Demo,
把代碼貼出來就完事了,甚至是代碼都還沒法完整編譯經過的,
因此若是讀者想對 SSDT 有所瞭解的話,能夠好好看一看這篇文章的 ~
順便這裏還帶出一個問題,是我這陣子腦子裏忽然冒出來的一個疑問,
可是因爲時間或者說是我的狀態問題,一直沒有去研究 ~ 不曉得園子裏有木有對這個有研究的 ~
衆所周知,在 Windows 操做系統中,System 進程的進程 PID 爲 4,
我想問的就是:System 進程的 PID 爲什麼是 4 ?
歡迎你們對這個問題討論啊 ~ 在這裏先給點思路,
那就是能夠經過 Windows 操做系統的啓動過程,而後結合 WRK 源碼進行研究 ~