中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SSDT Hook底層原理介紹以及怎么實現進程保護

發布時間:2021-10-23 10:09:50 來源:億速云 閱讀:141 作者:iii 欄目:編程語言

本篇內容主要講解“SSDT Hook底層原理介紹以及怎么實現進程保護”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“SSDT Hook底層原理介紹以及怎么實現進程保護”吧!

目錄

  1. SSDT Hook效果圖

  2. SSDT簡介

  3. SSDT結構

  4. SSDT HOOK原理

  5. Hook前準備

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

  7. SSDT Hook流程

  8. SSDT Hook實現進程保護


SSDT Hook效果圖

加載驅動并成功Hook  NtTerminateProcess函數:

SSDT Hook底層原理介紹以及怎么實現進程保護

當對 指定的進程進行保護后,嘗試使用“任務管理器”結束進程的時候,會彈出“拒絕訪問”的窗口,說明,我們的目的已經達到:

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底層原理介紹以及怎么實現進程保護

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 原理所在。

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寄存器,如下圖:
SSDT Hook底層原理介紹以及怎么實現進程保護

其中第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流程

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 來替換掉 oldService
NTSTATUS 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;
}

到此,相信大家對“SSDT Hook底層原理介紹以及怎么實現進程保護”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

格尔木市| 建阳市| 彭泽县| 楚雄市| 夹江县| 什邡市| 金阳县| 响水县| 阜新市| 鹤峰县| 平罗县| 吉林市| 封开县| 秦安县| 镶黄旗| 黄梅县| 越西县| 北海市| 贡嘎县| 共和县| 敦煌市| 会宁县| 镇赉县| 隆回县| 安福县| 青阳县| 怀柔区| 都兰县| 潮安县| 兴化市| 沛县| 玛曲县| 威远县| 孟州市| 辽阳市| 霍林郭勒市| 裕民县| 安吉县| 正宁县| 兰西县| 清水县|