64位下Hook NtOpenProcess的實現進程保護 + 源碼 (升級篇 )


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

  View Code

//=========================================================
// 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

  View Code

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等 敬請關注  }

  【舊源碼】HookNtOpenProcess Source 

 

專注於: Net分布式技術,移動服務端架構及系統安全學習及研究  by Andy


 

 

 

 

 

 

分類:  System Hacker


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM