程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> SSDT Hook實現內核級的進程保護

SSDT Hook實現內核級的進程保護

編輯:C++入門知識

目錄

SSDT Hook效果圖

加載驅動並成功Hook  NtTerminateProcess函數: 當對 指定的進程進行保護後,嘗試使用“任務管理器”結束進程的時候,會彈出“拒絕訪問”的窗口,說明,我們的目的已經達到:

SSDT簡介

SSDT 的全稱是 System Services Descriptor Table,系統服務描述符表。

 

這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的內核 API 聯系起來。

SSDT 並不僅僅只包含一個龐大的地址索引表,它還包含著一些其它有用的信息,諸如地址索引的基地址、服務函數個數等。

通過修改此表的函數地址可以對常用 Windows 函數及 API 進行 Hook,從而實現對一些關心的系統動作進行過濾、監控的目的。

一些 HIPS、防毒軟件、系統監控、注冊表監控軟件往往會采用此接口來實現自己的監控模塊。

 

SSDT結構

SSDT即系統服務描述符表,它的結構如下(參考《Undocument Windows 2000 Secretes》第二章):
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次都想不明白是怎麼回事)。

SSDT HOOK原理

關於內核 Hook 有多種類型,下面也給出一副圖示: SSDT HOOK只是其中一種Hook技術,本篇文章主要講解SSDT Hook的使用。 SSDT HOOK原理圖 通過Kernel Detective工具,我們可以發現,SSDT Hook前後,NtTerminateProcess的當前地址會發生變化,其中,變化後的當前地址:0xF885A110為我們自定義的Hook函數(即:HookNtTerminateProcess)的地址。這樣,以後每次執行NtTerminateProcess的時候,就會根據執行“當前地址”所指向的函數了,這也就是SSDT Hook的原理。 另外,看雪的"墮落天才"寫的不錯,我直接引用下: SSDT HOOK 的原理其實非常簡單,我們先實際看看KeServiceDescriptorTable是什麼樣的。 
 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函數所在的起始地址。  
    嗯,如果我們把8057559e改為指向我們函數的地址呢?比如 MyNtOpenProcess,那麼系統就會直接調用MyNtOpenProcess,而不是原來的NtOpenProcess了。這就是SSDT HOOK 原理所在。 另外,關於Ring3層轉入Ring0層的具體流程,可以參考下我的這篇博文,對加深理解SSDT Hook技術還是有幫助的:Ring3轉入Ring0跟蹤

Hook前准備

我們要修改SSDT表,首先這個表必須是可寫的,但在xp以後的系統中他都是只讀的,三個辦法來修改內存保護機制 (1) 更改注冊表  恢復頁面保護:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\EnforceWriteProtection=0 去掉頁面保護:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\DisablePagingExecutive=1 (2)改變CR0寄存器的第1位 Windows對內存的分配,是采用的分頁管理。其中有個CR0寄存器,如下圖:
其中第1位叫做保護屬性位,控制著頁的讀或寫屬性。如果為1,則可以讀/寫/執行;如果為0,則只可以讀/執行。 SSDT,IDT的頁屬性在默認下都是只讀,可執行的,但不能寫。 代碼如下:


(3)通過Memory Descriptor List(MDL)

具體做法可以google下,這裡就不介紹了

 

如何獲得SSDT中函數的地址呢?

  這裡主要使用了兩個宏:

①獲取指定服務的索引號: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)]

SSDT Hook流程

在驅動的入口函數中(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 來結束進程。

SSDT Hook實現進程保護

有了上面的理論基礎之後,接下來可以談談SSDT Hook實現進程保護的具體實現了。 實現進程保護,可以Hook NtTermianteProcess,另外也可以Hook NtOpenProcess,這裡,我是Hook NtTermianteProcess。 SSDT Hook原理一節中已經說過,SSDT Hook原理的本質是:自定義一個函數(HookNtTerminateProcess),讓系統服務NtTermianteProcess的當前地址指向我們自定義函數地址。 這一步工作是在驅動入口函數中執行的。當驅動加載的時候,將自定義函數的地址寫入SSDT表中NtTermianteProcess服務的當前地址:
= 
        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 =

Ring3與Ring0的通信

請看考:張帆《Windows驅動開發技術詳解》一書第7章:派遣函數

如何安裝、啟動、停止、卸載服務

請參考我的另一篇博文:NT式驅動加載器(附帶源碼)

參考文獻

SSDT Hook的妙用-對抗ring0 inline hook:http://bbs.pediy.com/showthread.php?t=40832 進程隱藏與進程保護(SSDT Hook 實現):http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html 張帆的《Windows驅動開發技術詳解》一書

源碼附件

SSDT Hook實現內核級的進程保護

版權

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved