SSDT Hook結構

來源:互聯網
上載者:User

標籤:des   blog   http   io   os   使用   ar   strong   for   

目錄

  1. SSDT Hook
  2. SSDT簡介
  3. SSDT結構
  4. SSDT HOOK原理
  5. Hook前準備
  6. 如何獲得SSDT中函數的地址呢
  7. SSDT Hook流程
  8. SSDT Hook實現進程保護
  9. Ring3與Ring0的通訊
  10. 如何安裝啟動停止卸載服務
  11. 參考文獻
  12. 源碼附件
  13. 著作權
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》第二章):
// KSYSTEM_SERVICE_TABLE 和 KSERVICE_TABLE_DESCRIPTOR// 用來定義 SSDT 結構typedef struct _KSYSTEM_SERVICE_TABLE{    PULONG  ServiceTableBase;                               // SSDT (System Service Dispatch Table)的基地址    PULONG  ServiceCounterTableBase;                        // 用於 checked builds, 包含 SSDT 中每個服務被調用的次數    ULONG   NumberOfService;                                // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小    ULONG   ParamTableBase;                                 // SSPT(System Service Parameter Table)的基地址} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;typedef struct _KSERVICE_TABLE_DESCRIPTOR{    KSYSTEM_SERVICE_TABLE   ntoskrnl;                       // ntoskrnl.exe 的服務函數    KSYSTEM_SERVICE_TABLE   win32k;                         // win32k.sys 的服務函數(GDI32.dll/User32.dll 的核心支援)    KSYSTEM_SERVICE_TABLE   notUsed1;    KSYSTEM_SERVICE_TABLE   notUsed2;}KSERVICE_TABLE_DESCRIPTOR, *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> dd KeServiceDescriptorTable    8055ab80  804e3d20 00000000 0000011c 804d9f48    8055ab90  00000000 00000000 00000000 00000000    8055aba0  00000000 00000000 00000000 00000000    8055abb0  00000000 00000000 00000000 00000000 

  如上,80587691 805716ef 8057ab71 80581b5c 這些就是系統服務函數的地址了。比如當我們在ring3調用OpenProcess時,進入sysenter的ID是0x7A(XP SP2),然後系統查KeServiceDescriptorTable,大概是這樣KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然後804E3F08 ->8057559e 這個就是OpenProcess系統服務函數所在,我們再跟蹤看看: 

lkd> u 8057559e    nt!NtOpenProcess:    8057559e 68c4000000      push    0C4h    805755a3 6860b54e80      push    offset nt!ObReferenceObjectByPointer+0x127 (804eb560)    805755a8 e8e5e4f6ff      call    nt!InterlockedPushEntrySList+0x79 (804e3a92)    805755ad 33f6            xor     esi,esi
  原來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的頁屬性在預設下都是唯讀,可執行檔,但不能寫。代碼如下:
//設定為不可寫void DisableWrite(){    __try    {        _asm        {            mov eax, cr0             or  eax, 10000h             mov cr0, eax             sti         }    }    __except(1)    {        DbgPrint("DisableWrite執行失敗!");    }}// 設定為可寫void EnableWrite(){    __try    {        _asm        {            cli            mov eax,cr0            and eax,not 10000h //and eax,0FFFEFFFFh            mov cr0,eax        }    }    __except(1)    {        DbgPrint("EnableWrite執行失敗!");    }}
(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服務的當前地址:
// 實現 Hook 的安裝,主要是在 SSDT 中用 newService 來替換掉 oldServiceNTSTATUS InstallHook(ULONG oldService, ULONG newService){    __try    {        ULONG uOldAttr = 0;                EnableWrite();    //去掉頁面保護            KdPrint(("偽造NtTerminateProcess地址: %x\n",(int)newService));        //KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(oldService)] = newService;        SYSCALL_FUNCTION(oldService) = newService;//        DisableWrite();    //恢複頁面保護        return STATUS_SUCCESS;    }    __except(1)    {        KdPrint(("安裝Hook失敗!"));    }}
這裡需要注意的是:在Hook前,需要去掉記憶體的頁面保護屬性,Hook後,需要回複記憶體的頁面保護屬性。HookNtTerminateProcess函數的代碼如下:
//************************************// 函數名稱 : HookNtTerminateProcess// 描    述 : 自訂的 NtOpenProcess,用來實現 Hook Kernel API// 日    期 : 2013/06/28// 參    數 : ProcessHandle:進程控制代碼 ExitStatus:// 返 回 值 : //************************************NTSTATUS HookNtTerminateProcess(__in_opt HANDLE ProcessHandle,__in NTSTATUS ExitStatus){    ULONG uPID;    NTSTATUS rtStatus;    PCHAR pStrProcName;    PEPROCESS pEProcess;    ANSI_STRING strProcName;    // 通過進程控制代碼來獲得該進程所對應的 FileObject 對象,由於這裡是進程對象,自然獲得的是 EPROCESS 對象    rtStatus = ObReferenceObjectByHandle(ProcessHandle, FILE_READ_DATA, NULL, KernelMode, (PVOID*)&pEProcess, NULL);    if (!NT_SUCCESS(rtStatus))    {        return rtStatus;    }    // 儲存 SSDT 中原來的 NtTerminateProcess 地址    pOldNtTerminateProcess = (NTTERMINATEPROCESS)oldSysServiceAddr[SYSCALL_INDEX(ZwTerminateProcess)];    // 通過該函數可以擷取到進程名稱和進程 ID,該函數在核心中實質是匯出的(在 WRK 中可以看到)    // 但是 ntddk.h 中並沒有到處,所以需要自己聲明才能使用    uPID = (ULONG)PsGetProcessId(pEProcess);    pStrProcName = _strupr((TCHAR *)PsGetProcessImageFileName(pEProcess));//使用微軟未公開的PsGetProcessImageFileName函數擷取進程名    // 通過進程名來初始化一個 ASCII 字串    RtlInitAnsiString(&strProcName, pStrProcName);    if (ValidateProcessNeedProtect(uPID) != -1)    {        // 確保調用者進程能夠結束(這裡主要是指 taskmgr.exe)        if (uPID != (ULONG)PsGetProcessId(PsGetCurrentProcess()))        {            // 如果該進程是所保護的的進程的話,則返回許可權不夠的異常即可            return STATUS_ACCESS_DENIED;        }    }    // 對於非保護的進程可以直接調用原來 SSDT 中的 NtTerminateProcess 來結束進程    rtStatus = pOldNtTerminateProcess(ProcessHandle, ExitStatus);    return 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實現核心級的進程保護著作權 著作權,歡迎轉載,但轉載請註明: 轉載自 

SSDT Hook結構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.