0x00 Hook技術
hook技術分為兩塊:
- Ring3層的Hook,俗稱應用層hook技術
- Ring0層的Hook,俗稱內核層Hook技術
Ring3層的hook又分為兩種類型
- Windows消息的hook
- WindowsAPI的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,常見于病毒以及殺軟中。
下圖展示了內核hook的幾個基本類型。
0x01 SSDT簡介
SSDT全稱System Service Descriptor Table(系統描述符表),這個表用于將Ring3的Win32API和內核的API聯系起來。
SSDT并不僅僅只包含一個龐大的地址索引表,它還包含一些有用的信息,如地址索引的基地址,服務函數的個數等。通過修改此表可以達到對一些關心的系統動作進行過濾以及監控的目的。
在NT4.0的windows操作系統中,默認存在兩個系統服務描述符表,這兩個描述符表對應了兩類不同的系統服務,這兩個表為:KeServiceDescriptorTable(SSDT)和KeServiceDescriptorTable(SSDT Shadow)。其中SSDT負責處理來自Ring3層的Kernel32.dll的系統調用。而SSDT Shadow則主要處理來自User32.dll和GDI32.dll的系統調用。同時SSDT在ntoskrnl.exe中是導出的,而SSDT Shadow如其名是未被Windows所導出的,而關于SSDT的全部內容都是通KeServiceDescriptorTable來完成的。
以下截圖說明,KeServiceDescriptorTable是在ntoskrnl.exe中被導出的:
隨后我們看看看看Windows操作系統的源碼中如何定義KeServiceDescriptorTable的,通過觀察WRK可知,
這么看還是有點蛋疼啊。改寫以下變量的名稱吧。
typedef struct _KSYSTEM_SERVICE_TABLE{
PULONG ServiceTableBase; //SSDT的基地址指針
PULONG ServiceCounterTableBase; //SSDT中每個服務被調用次數表的基地址指針
ULONG NumberOfService; //服務函數個數,NumberOfService*4就是整個地址表的大小
ULONG ParamTableBase; //SSPT的基地址
}KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE;
typedef struct _KSERVICE_TABLE_DESCRIPTOR{
KSYSTEM_SERVICE_TABLE ntoskrnl; //ntoskrnl.exe的服務函數
KSYSTEM_SERVICE_TABLE win32k; //win32k.sys的服務函數(GDI32/User32)
KSYSTEM_SERVICE_TABLE notUsed1;
KSYSTEM_SERVICE_TABLE notUsed2;
}KSERVICE_TABLE_DESCRIPTOR,*PKSERVICE_TABLE_DESCRIPTOR;
//導出由ntoskrnl.exe所導出的SSDT
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
從上述介紹可知,KeServiceDescriptorTable可以看做是一個數組,是4個KSYSTEM_SERVICE_TABLE結構形成的數組,而每個KSYSTEM_SERVICE_TABLE對應一個PE文件導出的服務描述符表,根據這些服務描述符表我們可以獲得更加詳細的服務信息。在應用層ntdll.dll中的API在這個系統服務描述表中都存在一個與之對應的服務,當我們的應用程序調用ntdll.dll里的API時最終可以調用對應的系統服務函數,通知給內核一個索引,內核通過該索引在SSDT中查找對應的服務,內核調用服務完成應用的API調用請求。
應用層調用Win32API流程
有了以上的SSDT基礎后,我們再來看看在應用層調用Win32API(主要指ntdll.dll中的API)的流程,我們主要針對ntdll.dll中的NtQuerySystemInformation這個API的調用流程來進行闡述。
這里存在四個類似的API。
再給出這些API的調用流程。
這里我們可以看到ntdll.dll中的nt和zw都會進入內核層去調用ntoskrnl的zw函數,而zw最終會調用nt函數,這個函數作為內核API最終去請求系統服務的執行。
用exescope工具可以打開ntdll.dll,看到NtQuerySystemInformation以及ZwQuerySystemInformation。
而實質上zw和nt都是同一函數,指向同一區域,入口地址相同。
因此Ntdll.dll中的API都是對內核API的封裝,當Kernel32.dll中的API通過Ntdll.dll去調用系統API時,會進行參數檢查,并調用中斷(int 2Eh或SysEnter),從而從Ring3進入Ring0,并將所要調用的服務號,即SSDT數組的索引值,存放進寄存器EAX中,并且將參數地址放到指定的寄存器EDX中,再復制參數到內核地址空間,根據存放在EAX中的索引值來在SSDT數組中調用指定的服務。
經過上面步驟我們來到Ring0層。使用exescope看看ntoskrnl.exe中的ZwQuerySystemInformation以及NtQuerySystemInformation。
再反匯編ntoskrnl這個文件可以看到Zw函數中調用KiSystemService系統服務分發函數時往EAX中存放了索引號ADh。如圖:
隨后根據該索引值檢索SSDT項,最后根據該SSDT項中所存放的系統服務地址來調用這個系統服務。在這里就是調用KeServiceDescriptorTable[ADh]處保存的地址對應的系統服務。那就是Ring0下的NtQuerySystemInformation。
0x02 詳解SSDT
這節內我們用WinDbg來調試XP系統,借此說明SSDT是個什么鬼。
據我們上文的結構定義可知,KeServiceDescriptorTable是一個指向4個KSYSTEM_SERVICE_TABLE結構首地址的指針,以下圖為例吧。
這里的0x80563520這一行就是ntoskrnl對應的服務描述符表結構KSYSTEM_SERVICE_TABLE。那么第一個32位的0x804e58a0則是對應ntoskrnl對應的KSYSTEM_SERVICE_TABLE中的SSDT Base,即服務描述符表的首地址。通過對該首地址的dump,我們可以看到許多以128位為一組排列的服務描述符,每個描述符中的第一個32位(0x80591bfb)對應著系統服務的入口地址。通過對該入口地址的反匯編,可以看到這是SSDT第一個系統服務NtAcceptConnectPort函數。如圖:
那么知道了SSDT首地址,同時知道了索引,那么我們就可以通過索引來找到對應的系統服務入口地址了。通過計算“SSDT中系統服務地址所在的地址 = SSDT首地址 + 4 * 索引值”,可以推算出NtQuerySystemInformation的起始地址位0x80586ff1,對該地址進行反匯編,可得下圖:
由此可知,SSDT就是個保存Windows系統服務地址的數組。
0x03 SSDT hook原理
從上面的分析中我們可以看到SSDT數組中保存了系統服務的地址,如Ring0下的NtQuerySystemInformation系統服務地址,就保存在KeServiceDescriptorTable[ADh]中,既然hook就是取出這個地址后替換上我們的hook函數,在hook函數中執行原函數即可。
參考文獻:
http://www.cnblogs.com/boyxiao/archive/2011/09/03/2164574.html