64位下Hook NtOpenProcess的實現進程保護 + 源碼 (升級篇 )
【PS: 如果在64位系統下,出現調用測試demo,返回false的情況下,請修改Hook Dll的代碼】
glhHook = SetWindowsHookEx(WH_SHELL,ShellHookProc, 0 , 0); //改成跟X86下一樣的 glhHook = SetWindowsHookEx(WH_SHELL,ShellHookProc,glhInstance, 0);
2013.09.11代碼修改, 可以針對指定的進程進行保護( 編譯DLL和測試DEMO的時候,請注意目標平台,X86還是X64)
=> 工程給出的是X86下的,X64請自行編譯一份Hook dll然后改下名字在測試demo中調用 ,C#中隊X86和X64的判斷可以通過
Environment.Is64BitOperatingSystem
2013.09.11 HookNtOpenProcessLib Souces
在我的上一篇文章中【Hook技術】實現從"任務管理器"中保護進程不被關閉 + 附帶源碼 + 進程保護知識擴展 介紹了X86下基於IAT Hook技術,通過Hook OpenProcess來實現從任務管理器中保護進程,看到一些評論,有朋友問該代碼是否適用於64位系統? 答案是肯定的,需要修改兩個地方:
a. 編譯時候,設置平台為X64
b.修改SetWindowsHookEx第三個參數為0 (ps:該設置不是絕對的,如果你發現X64下該方法不成效,請嘗試使用X86下的方法),關於這點,是因為64為下和32為下調用SetWindowsHookEx略有不同,官方關於該函數的有一段解析為:
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下通過HOOK技術實現進程的保護,不推薦Hook OpenProcess,可以考慮通過Hook Ntdll的函數(NtOpenProcess)實現,這也是樓主寫此文的目的的,介紹通過MHook實現對NtOpenProcess的hook實現進程的保護.
1. Hook技術基礎知識(Inline Hook)
在開始本文的實例前想先通過對Hook技術做些簡單的介紹,幫助大家更好的理解所謂的Hook技術,所謂的hook技術其實就是修改函數行為的一種技術,通過對函數行為的修改可以實現例如:文件的監控/保護 , 進程的監控/隱藏等,例如大多數的安全軟件都是基於這種技術實現的,還有些病毒木馬技術也會涉及到hook技術。一般的hook函數的流程:
{ 目標API } ----- 【 Hook API 】 ------- 修改函數入口前幾個字節,添加跳轉指令 類似JMP DWORD Ptr Address
\
{ jump 我們的代碼處 }
|- a. 執行我們代碼
|- b. 修復Hook
|- c. 調用目標API
通過修改目標函數的前幾個字節,然后跳轉到我們的代碼執行,等執行完我們定制的代碼后,在調用真實的目標API返回結果給調用程序.
大多數的系統API,前幾個字節都是對堆棧的操作,而hook技術就是利用了這幾個字節實現jmp的,偽代碼描述:
目標函數:
mov edi, edi // HOOK Jmp DWORD PTR MyFunctionAddress jump后 MyFunctionAddress內部邏輯處理 push ebp // 堆棧操作 < ==========> xor ecx ,exc <=========> 修復原函數堆棧操作 { mov edi,edi push ebp,...} mov ebp, esp // 跳到原函數執行JMP 【ADDRESS】 xor ecx, ecx ==> 標記地址為【ADDRESS】
這是一種比較常用的HOOK思路,還有修改函數中部或者尾部的,思路相同,patch的位置不同
還有中Hook的思路是通過“跳板”函數實現,定制函數和目標函數之間的關系
{ Hook Api }
\
{ Jump 定制函數 }
\
{ jump Trampoline(跳板函數) }
\ ______ { Call 目標函數 } ------ { 結果返回給調用程序 }
成功Hook函數后,跳轉我們定制的函數中執行,當我們定制的代碼執行完后,並不是在函數內部調用原目標函數,而是調用一個叫Trampoline的函數,Trampoline的任務就是平衡堆棧,然后執行原目標函數, 最后將結果返回給調用程序
例如:
mov edi, edi addr1 jmp customFunction ; //執行完地址的邏輯后,跳到調用Trampoline函數 push ebp < ==== JUMP ====> addr2 xor ecx,ecx mov ebp, esp addr2 xor ecx, ecx
Trampoline函數:
mov edi, edi push ebp { 平衡堆棧 } mov ebp, esp jmp addr2 ===> 然后跳到原函數某地址執行
這種方法相對於第一種方式來說,安全了很多至少在多線程的環境下 ,一般trampoline跳轉函數都是被標記為naked的,很多情況都是通過匯編實現,由自己編碼控制堆棧。
2. 實戰,X64下Hook NtOpenProcess
本demo中使用的hook引擎是MHook,Mhook是開源的,采用的是第二種hook方式,相比於MinHook/easyHook來說,使用簡單,只導出兩個函數:
//安裝Hook //ppSystemFunction,原函數地址 //pHookFunction ,定制的函數地址 BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction); //卸載HOOK //ppHookedFuntion.原函數地址 BOOL Mhook_Unhook(PVOID *ppHookedFunction);
a. 下載Mhook,新建一個win32 DLL工程,名稱:HookNtOpenProcessLib
b. 聲明NtOpenProcess函數和我們定制的Hook_NtOpenProcess

//=========================================================
// struct CLIENT_ID
typedef struct _CLIENT_ID {
DWORD_PTR UniqueProcess;
DWORD_PTR UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
//===========================================================
// NtOpenProcess
typedef ULONG (WINAPI *pfnNtOpenProcess)(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK AccessMask ,
__in PVOID ObjectAttributes,
__in PCLIENT_ID ClientId);
pfnNtOpenProcess _NtOpenProcess = (pfnNtOpenProcess)GetProcAddress( GetModuleHandle(L"ntdll"),"NtOpenProcess");
定制的Hook_NtOpenProcess,設置進程句柄為NULL,保護所有進程,針對某個進程保護,請通過ProcesssHandler獲取PID然后做比較
//=========================================================== //定制我們自己的NtOpenProcess ULONG 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); //通過進程句柄獲取PID,然后驗證 //if(gProtectProcessID == pid ){ // return STATUS_ACCESS_DENIED; //} //return result; //=================================== //簡單處理,直接設置ProcessHandle,保護所有 ProcessHandle = NULL; return _NtOpenProcess( ProcessHandle, AccessMask,ObjectAttributes,ClientId); }
c. 安裝消息鈎子,跟一篇博文一樣,通過SetWindowsHookEx,只是在調用該函數時候需要注意區別下32位系統和64位系統情況,例如:
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); //第三參數為0,而不是當前模塊的實例句柄 #endif if(glhHook!=NULL) { gProtectProcessID = pid; bResult=TRUE; } return bResult; }
d.C#調用HOOKNtOpenProcessLib.dll

public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.Text = "=> HookNtOpenProcess <= ";
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e) {
Label lb = new Label{ Width = 200 , Height = 50 , ForeColor = System.Drawing.SystemColors.GrayText };
lb.Text = "By Andy, FZ " + Environment.NewLine + "專注於: Net分布式技術,移動服務端架構及系統安全學習及研究";
_btnHook = new Button { Text = "sTarthOok" };
_btnHook.Click += (sende, ex) => {
bool result = false;
if (_btnHook.Text.Equals("sTarthOok")) {
result = NativeAPI.InstallHook(System.Diagnostics.Process.GetCurrentProcess().Id);
_btnHook.Text = "sTophOok";
}
else if (_btnHook.Text.Equals("sTophOok")) {
NativeAPI.UninstallHook();
_btnHook.Text = "sTarthOok";
}
//MessageBox.Show(result.ToString());
};
this.Controls.Add( _btnHook );
_btnHook.SetBounds( 0 , 5 , 350,40 );
this.Controls.Add( lb);
lb.SetBounds( 0 , 50 , 350,40 );
}
class NativeAPI{
[DllImport("HookNtOpenProcessLib.dll",CallingConvention=CallingConvention.Cdecl)]
public extern static bool InstallHook( int pid );
[DllImport("HookNtOpenProcessLib.dll",CallingConvention=CallingConvention.Cdecl)]
public extern static bool UninstallHook();
}
private Button _btnHook = null;
}
e.程序運行效果讀,第一張通過ARK工具查看inline hook了NtOpenProcess,NtOpenProcess進入內核后是通過調用ZwOpenProcess的,所以我們看到ZwOpenProcess也被Inline hook了,第2張通過任務管理器結束進程
關於程序中一些注意問題:
1. 編譯HookNtOpenProcessLib.dll時候,如果要運行在64位系統下,通過“配置管理器”設置下平台X64(也就是你要編譯兩份DLL,一份是平台為win32的,一個是X64下的)
2. 附件中C#程序,如果希望運行在32位和64位系統上,請通過“配置管理器”設置平台為“AnyCPU”
3. 將編譯好的dll(32位和64位)的放在測試demo下,測試demo在調用InstallHook時候,內部SetWindowsHookEx會根據當前平台調用對應的HookNtOpenProcessLib.dll
如果在使用中還存在任何問題,可以給我留言或者通過我的郵箱wygandy1987@gamil.com
{ 后面有時間,將發表一些關於Mhook實現的文章和關於內核Hook的文章,游戲hook等 敬請關注 }
專注於: Net分布式技術,移動服務端架構及系統安全學習及研究 by Andy