在我的上一篇文章中【Hook技術(shù)】實(shí)現(xiàn)從"任務(wù)管理器"中保護(hù)進(jìn)程不被關(guān)閉 + 附帶源碼 + 進(jìn)程保護(hù)知識(shí)擴(kuò)展 介紹了X86下基于IAT Hook技術(shù),通過(guò)Hook OpenProcess來(lái)實(shí)現(xiàn)從任務(wù)管理器中保護(hù)進(jìn)程,看到一些評(píng)論,有朋友問(wèn)該代碼是否適用于64位系統(tǒng)? 答案是肯定的,需要修改兩個(gè)地方: a. 編譯時(shí)候,設(shè)置平臺(tái)為X64 b.修改SetWindowsHookEx第三個(gè)參數(shù)為0,關(guān)于這點(diǎn),是因?yàn)?4為下和32為下調(diào)用SetWindowsHookEx略有不同,官方關(guān)于該函數(shù)的有一段解析為: SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names. 如果你希望在X64下通過(guò)HOOK技術(shù)實(shí)現(xiàn)進(jìn)程的保護(hù),不推薦Hook OpenProcess,可以考慮通過(guò)Hook Ntdll的函數(shù)(NtOpenProcess)實(shí)現(xiàn),這也是樓主寫此文的目的的,介紹通過(guò)MHook實(shí)現(xiàn)對(duì)NtOpenProcess的hook實(shí)現(xiàn)進(jìn)程的保護(hù). 1. Hook技術(shù)基礎(chǔ)知識(shí)(Inline Hook) 在開始本文的實(shí)例前想先通過(guò)對(duì)Hook技術(shù)做些簡(jiǎn)單的介紹,幫助大家更好的理解所謂的Hook技術(shù),所謂的hook技術(shù)其實(shí)就是修改函數(shù)行為的一種技術(shù),通過(guò)對(duì)函數(shù)行為的修改可以實(shí)現(xiàn)例如:文件的監(jiān)控/保護(hù) , 進(jìn)程的監(jiān)控/隱藏等,例如大多數(shù)的安全軟件都是基于這種技術(shù)實(shí)現(xiàn)的,還有些病毒木馬技術(shù)也會(huì)涉及到hook技術(shù)。一般的hook函數(shù)的流程: { 目標(biāo)API } ----- 【 Hook API 】 ------- 修改函數(shù)入口前幾個(gè)字節(jié),添加跳轉(zhuǎn)指令 類似JMP DWORD Ptr Address \ { jump 我們的代碼處 } |- a. 執(zhí)行我們代碼 |- b. 修復(fù)Hook |- c. 調(diào)用目標(biāo)API 通過(guò)修改目標(biāo)函數(shù)的前幾個(gè)字節(jié),然后跳轉(zhuǎn)到我們的代碼執(zhí)行,等執(zhí)行完我們定制的代碼后,在調(diào)用真實(shí)的目標(biāo)API返回結(jié)果給調(diào)用程序. 大多數(shù)的系統(tǒng)API,前幾個(gè)字節(jié)都是對(duì)堆棧的操作,而hook技術(shù)就是利用了這幾個(gè)字節(jié)實(shí)現(xiàn)jmp的,偽代碼描述: 目標(biāo)函數(shù): mov edi, edi // HOOK Jmp DWORD PTR MyFunctionAddress jump后 MyFunctionAddress內(nèi)部邏輯處理push ebp // 堆棧操作 < ==========> xor ecx ,exc <=========> 修復(fù)原函數(shù)堆棧操作 { mov edi,edi push ebp,...}mov ebp, esp // 跳到原函數(shù)執(zhí)行JMP 【ADDRESS】xor ecx, ecx ==> 標(biāo)記地址為【ADDRESS】 這是一種比較常用的HOOK思路,還有修改函數(shù)中部或者尾部的,思路相同,patch的位置不同 還有中Hook的思路是通過(guò)“跳板”函數(shù)實(shí)現(xiàn),定制函數(shù)和目標(biāo)函數(shù)之間的關(guān)系 { Hook Api } \ { Jump 定制函數(shù) } \ { jump Trampoline(跳板函數(shù)) } \ ______ { Call 目標(biāo)函數(shù) } ------ { 結(jié)果返回給調(diào)用程序 } 成功Hook函數(shù)后,跳轉(zhuǎn)我們定制的函數(shù)中執(zhí)行,當(dāng)我們定制的代碼執(zhí)行完后,并不是在函數(shù)內(nèi)部調(diào)用原目標(biāo)函數(shù),而是調(diào)用一個(gè)叫Trampoline的函數(shù),Trampoline的任務(wù)就是平衡堆棧,然后執(zhí)行原目標(biāo)函數(shù), 最后將結(jié)果返回給調(diào)用程序 例如: edi, edi addr1 customFunction ; //執(zhí)行完地址的邏輯后,跳到調(diào)用Trampoline函數(shù) ebp < ==== JUMP ====> addr2 ecx,ecx ebp, esp addr2 ecx, ecx Trampoline函數(shù): edi, edi ebp { 平衡堆棧 } ebp, esp addr2 ===> 然后跳到原函數(shù)某地址執(zhí)行 這種方法相對(duì)于第一種方式來(lái)說(shuō),安全了很多至少在多線程的環(huán)境下 ,一般trampoline跳轉(zhuǎn)函數(shù)都是被標(biāo)記為naked的,很多情況都是通過(guò)匯編實(shí)現(xiàn),由自己編碼控制堆棧。 2. 實(shí)戰(zhàn),X64下Hook NtOpenProcess 本demo中使用的hook引擎是MHook,Mhook是開源的,采用的是第二種hook方式,相比于MinHook/easyHook來(lái)說(shuō),使用簡(jiǎn)單,只導(dǎo)出兩個(gè)函數(shù): //安裝Hook//ppSystemFunction,原函數(shù)地址//pHookFunction ,定制的函數(shù)地址BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction);//卸載HOOK//ppHookedFuntion.原函數(shù)地址BOOL Mhook_Unhook(PVOID *ppHookedFunction); a. 下載Mhook,新建一個(gè)win32 DLL工程,名稱:HookNtOpenProcessLib b. 聲明NtOpenProcess函數(shù)和我們定制的Hook_NtOpenProcess ![]() 定制的Hook_NtOpenProcess,設(shè)置進(jìn)程句柄為NULL,保護(hù)所有進(jìn)程,針對(duì)某個(gè)進(jìn)程保護(hù),請(qǐng)通過(guò)ProcesssHandler獲取PID然后做比較 //===========================================================//定制我們自己的NtOpenProcessULONG WINAPI Hook_pfnNtOpenProcess( __out PHANDLE ProcessHandle, __in ACCESS_MASK AccessMask , __in PVOID ObjectAttributes, __in PCLIENT_ID ClientId){ //ULONG result = _NtOpenProcess( ProcessHandle,AccessMask,ObjectAttributes,ClientId); //DWORD pid = GetProcessIdByHandle( ProcessHandle); //通過(guò)進(jìn)程句柄獲取PID,然后驗(yàn)證 //if(gProtectProcessID == pid ){ // return STATUS_ACCESS_DENIED; //} //return result; //=================================== //簡(jiǎn)單處理,直接設(shè)置ProcessHandle,保護(hù)所有 ProcessHandle = NULL; return _NtOpenProcess( ProcessHandle, AccessMask,ObjectAttributes,ClientId); } c. 安裝消息鉤子,跟一篇博文一樣,通過(guò)SetWindowsHookEx,只是在調(diào)用該函數(shù)時(shí)候需要注意區(qū)別下32位系統(tǒng)和64位系統(tǒng)情況,例如: extern "C" __declspec(dllexport) BOOL InstallHook(DWORD pid) { BOOL bResult=FALSE; //這里需要注意X86和X64下處理是不一樣的 #ifdef _M_IX86 glhHook = SetWindowsHookEx(WH_SHELL,ShellHookProc,glhInstance, 0); #elif defined _M_X64 glhHook = SetWindowsHookEx(WH_SHELL,ShellHookProc,0, 0); //第三參數(shù)為0,而不是當(dāng)前模塊的實(shí)例句柄 #endif if(glhHook!=NULL) { gProtectProcessID = pid; bResult=TRUE; } return bResult; } d.C#調(diào)用HOOKNtOpenProcessLib.dll ![]() e.程序運(yùn)行效果讀,第一張通過(guò)ARK工具查看inline hook了NtOpenProcess,NtOpenProcess進(jìn)入內(nèi)核后是通過(guò)調(diào)用ZwOpenProcess的,所以我們看到ZwOpenProcess也被Inline hook了,第2張通過(guò)任務(wù)管理器結(jié)束進(jìn)程 關(guān)于程序中一些注意問(wèn)題: 1. 編譯HookNtOpenProcessLib.dll時(shí)候,如果要運(yùn)行在64位系統(tǒng)下,通過(guò)“配置管理器”設(shè)置下平臺(tái)X64(也就是你要編譯兩份DLL,一份是平臺(tái)為win32的,一個(gè)是X64下的) 2. 附件中C#程序,如果希望運(yùn)行在32位和64位系統(tǒng)上,請(qǐng)通過(guò)“配置管理器”設(shè)置平臺(tái)為“AnyCPU” 3. 將編譯好的dll(32位和64位)的放在測(cè)試demo下,測(cè)試demo在調(diào)用InstallHook時(shí)候,內(nèi)部SetWindowsHookEx會(huì)根據(jù)當(dāng)前平臺(tái)調(diào)用對(duì)應(yīng)的HookNtOpenProcessLib.dll |
|