目錄
這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的內核 API 聯系起來。
SSDT 並不僅僅只包含一個龐大的地址索引表,它還包含著一些其它有用的信息,諸如地址索引的基地址、服務函數個數等。
通過修改此表的函數地址可以對常用 Windows 函數及 API 進行 Hook,從而實現對一些關心的系統動作進行過濾、監控的目的。
一些 HIPS、防毒軟件、系統監控、注冊表監控軟件往往會采用此接口來實現自己的監控模塊。
typedef PULONG ServiceCounterTableBase; ULONG NumberOfService; ULONG ParamTableBase; } KSYSTEM_SERVICE_TABLE, * KSYSTEM_SERVICE_TABLE win32k; *PKSERVICE_TABLE_DESCRIPTOR;內核中有兩個系統服務描述符表,一個是KeServiceDescriptorTable(由ntoskrnl.exe導出),一個是KeServieDescriptorTableShadow(沒有導出)。 兩者的區別是,KeServiceDescriptorTable僅有ntoskrnel一項,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服務地址由KeServiceDescriptorTable分派,gdi.dll/user.dll的內核API調用服務地址由KeServieDescriptorTableShadow分派。還有要清楚一點的是win32k.sys只有在GUI線程中才加載,一般情況下是不加載的,所以要Hook KeServieDescriptorTableShadow的話,一般是用一個GUI程序通過IoControlCode來觸發(想當初不明白這點,藍屏死機了N次都想不明白是怎麼回事)。
lkd>
如上,80587691 805716ef 8057ab71 80581b5c 這些就是系統服務函數的地址了。比如當我們在ring3調用OpenProcess時,進入sysenter的ID是0x7A(XP SP2),然後系統查KeServiceDescriptorTable,大概是這樣KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然後804E3F08 ->8057559e 這個就是OpenProcess系統服務函數所在,我們再跟蹤看看:
lkd>!!ObReferenceObjectByPointer+!InterlockedPushEntrySList+原來8057559e就是NtOpenProcess函數所在的起始地址。
(3)通過Memory Descriptor List(MDL)
具體做法可以google下,這裡就不介紹了
這裡主要使用了兩個宏:
①獲取指定服務的索引號:SYSCALL_INDEX
②獲取指定服務的當前地址:SYSCALL_FUNCTION
這兩個宏的具體定義如下:
//根據 ZwServiceFunction 獲取 ZwServiceFunction 在 SSDT 中所對應的服務的索引號 #define SYSCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1)) //根據ZwServiceFunction 來獲得服務在 SSDT 中的索引號,然後再通過該索引號來獲取ntServiceFunction的地址 #define SYSCALL_FUNCTION(ServiceFunction) KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(ServiceFunction)]
在驅動的入口函數中(DriverEntry),對未進行SSDT Hook前的SSDT表進行了備份(用一個數組保存),備份時,一個索引號對應一個當前地址,如上圖所示。
這樣,在解除Hook的時候,就可以從全局數組中根據索引號獲取未Hook前的服務名的當前地址,以便將原來的地址寫回去,這一步很重要。
當用戶選擇保護某個進程的時候,就會通過DeviceIoControl發送一個IO_INSERT_PROTECT_PROCESS控制碼給驅動程序,此時驅動程序會生成一個IRP:IRP_MJ_DEVICE_CONTROL,我們事先已經在驅動程序中為
IRP_MJ_DEVICE_CONTROL指定了一個派遣函數:SSDTHook_DispatchRoutine_CONTROL。在該派遣函數中:我們通過獲取控制碼(是保護進程還是取消保護進程),如果是要保護某個進程,則通過 DeviceIoControl的第3個參數將要保護的進程的pid傳遞給驅動程序。然後在派遣函數SSDTHook_DispatchRoutine_CONTROL中從緩沖區中讀取該pid,如果是要保護進程,則將要“保護進程”的pid添加到一個數組中,如果是要“取消保護進程”,則將要取消保護的進程PID從數組中移除。
在Hook NtTermianteProcess函數後,會執行我們自定義的函數:HookNtTerminateProcess,在HookNtTerminateProcess函數中,我們判斷當前進程是否在要保護的進程數組中,如果該數組中存在該pid,則我們返回一個“權限不夠”的異常,如果進程保護數組中不存在該pid,則直接調用原來 SSDT 中的 NtTerminateProcess 來結束進程。
= KdPrint((,( SYSCALL_FUNCTION(oldService) = newService; DisableWrite();這裡需要注意的是:在Hook前,需要去掉內存的頁面保護屬性,Hook後,需要回復內存的頁面保護屬性。 HookNtTerminateProcess函數的代碼如下:
rtStatus = ObReferenceObjectByHandle(ProcessHandle, FILE_READ_DATA, NULL, KernelMode, (PVOID*)& (! pOldNtTerminateProcess = uPID == _strupr((TCHAR *)PsGetProcessImageFileName(pEProcess)); RtlInitAnsiString(& (ValidateProcessNeedProtect(uPID) != - (uPID != rtStatus =