hook的使用

Hook的中文含義是“鉤子”,與消息有着非常密切的聯繫。hook是消息處理中的一個環節,用於監控消息在系統中的傳遞,並在這些消息到達最終的消息處理過程前,處理某些特定的消息。它將自身的代碼融進目標進程裏,目標進程的某一調用將優先調用你的hook dll,然後再由hook dll調用目標進程想調用的函數或者方法;


 api hook是什麼?

在windows系統下編程,應該會接觸到api函數的使用,常用的api函數大概有2000個左右。

今天隨着控件,stl等高效編程技術的出現,api的使用概率在普通的用戶程序上就變得越來越小了。

當諸如控件這些現成的手段不能實現的功能時,我們還需要藉助api。

最初有些人對某些api函數的功能不太滿意,就產生了如何修改這些api,使之更好的服務於程序的想法,這樣api hook就自然而然的出現了。

我們可以通過api hook,改變一個系統api的原有功能。基本的方法就是通過hook“接觸”到需要修改的api函數入口點,改變它的地址指向新的自定義的函數。

api hook並不屬於msdn上介紹的13類hook中的任何一種。所以說,api hook並不是什麼特別不同的hook,它也需要通過基本的hook提高自己的權限,跨越不同進程間訪問的限制,達到修改api函數地址的目的。對於自身進程空間下使用到的api函數地址的修改,是不需要用到api hook技術就可以實現的。



Hook的幾種方式:

1. 使用註冊表HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

AppInit_DLLs這個項的值本應該是個空值。註冊表的系統設置項“AppInit_DLLs”可以爲任一個進程調用一個dll列表,是早期的進程插入式木馬的伎倆,通過修改註冊表中的KEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs來達到插入進程的目的。缺點是不實時,修改註冊表後需要重新啓動才能完成進程插入。AppInit_DLLs這個項目,正常情況下它的值應該是空的。這個項目表示每當一個程序啓動的時候,AppInit_DLLs中的dll文件就會注入到該程序裏面去啓動。

比如:如果AppInit_DLLs=qhbpri.dll,那麼在登錄windows後,系統加載了explorer.exe,那麼qhbpri.dll也會出現在其中。dll文件雖然不能自己運行,但等他注入到應用程序中,其中代碼還是會被執行,一些qq密碼盜竊木馬就是用上面的原理工作的。由於dll是寄生在exe中運行,在進程管理器中是看不到它的。

但是此種方法,應該會被當前的大多數殺毒軟件警告;


2.調用SetWindowsHookEx(WH_GETMESSAGE, …, 0)

SetWindowsHookEx(WH_GETMESSAGE, …, 0) 是個全局的消息鉤子,雖然可能你的程序並不用到消息鉤子,但是鉤子的一個副作用是會將對應的DLL加載到所有的GUI線程之中(無疑會拖慢程序的運行)。類似的,只有用到GUI的進程纔會被掛接。雖然有這種限制,這種方法仍然是最常用的掛接進程的方法。


3.使用CreateRemoteThread函數在目標進程中創建遠程線程
這種方法可以在任意的目標進程中創建一個遠程線程,遠程線程中可以執行任意代碼,這樣便可以做到把我們的代碼Inject到目標進程中。這種方法具有最大的靈活性,但是難度也最高:
  a) 遠程線程代碼必須可以自重定位
  b) 要能夠監視進程的啓動和結束,這樣纔可以掛接到所有進程
此種方式,着重介紹下:

1. 找到你要掛載的進程;

2.得到要注入的進程的句柄,OpenProcess(...);

3.爲後面的dll分配內存,::VirtualAllocEx(...);

4.將dll載入申請的內存中,WriteProcessMemory(...);

5.獲得LoadLibraryA在內核中的具體位置,GetProcAddress(...);

6.只要調用了LoadLibraryA都創建遠程線程加載dll,CreateRemoteThread(...);

7.等待遠程線程終止,WaitForSingleObject(...);

8.關閉句柄,釋放申請的遠程線程中的分配的空間;

但是此種方法只能是在目標先啓動後,然後才能成功的掛載我們的hook程序,那如何檢測目標程序是否已經開啓了呢?

下面做詳細闡述:首先的第一想法,是Hook ZwCreateProcess,結果調試的時候發現,很多創建進程的動作,並沒有通過這個API執行,所以自然就是沒辦法監控進程的創建,於是回到本質,從創建進程的動作過程來分析,創建新的進程,其大致要經歷以下步驟:

(1)打開可執行文件,以FILE_EXECUTE權限打開;

(2)將可執行文件加載到內存空間;

(3)進程的活動結構將被創建,如(EPROCESS,KPROCESS和PEB結構);

(4)爲新創建的進程分配地址空間;

(5)爲進程的主線程創建線程活動結構,如(ETHREAD,KTHREAD和TEB結構);

(6)主線程的棧將會被分配;

(7)進程的主線程的上下文將被創建;

(8)通知windows子系統;

以上總結下來,無非有下面幾種辦法獲取進程創建的消息:

(1)HOOK ZeCreateSection,創建虛擬內存塊的時候,根據傳入的文件句柄,獲取句柄對應的文件名是否爲exe可執行文件;

(2)Hook NtReadVirtualMemory,爲新創建的進程分配地址空間等操作時,需要讀取進程空間,這樣捕獲,就能夠獲取進程的創建動作;

(3)通過windows提供的回調函數,註冊回調事件;

方法對比:

(1)該方法能夠準確的獲取進程創建的操作,但是由於此時進程並沒有創建完畢,一些進程的基本結構還沒有創建,所以進程ID等信息無法獲取;

(2)該方法能夠獲取進程的創建操作,但不準確。因爲除了進程的創建會調用此操作外,人爲的一些操作,例如某外部應用程序想讀取另一個進程的內存空間,也會調用這個函數,這時候也會有事件響應,因此結果不準確;

(3)第三種方法更直觀和簡單。因爲採用的回調事件,並不直接HOOK API,因此更穩定。

重點分析第三種回調方法。

註冊回調事件,是通過PsSetCreateProcessNotifyRoutine來實現的,其函數原型如下:

NTSTATUS PsSetCreateProcessNotifyRoutine( 
IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine, 
IN BOOLEAN Remove 
);

NotifyRoutine就是註冊的回調函數,當有進程創建的時候,就會調用這個NotifyRoutine對應的函數,其函數定義原型如下:

VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE) ( 
IN HANDLE ParentId, 
IN HANDLE ProcessId, 
IN BOOLEAN Create 
); 

其中,ParentId是父進程ID,ProcessId爲子進程ID,而Create表示是創建進程還是結束進程,其中True表示創建進程,False表示結束進程。

通過這個函數,我們就能夠完成進程創建和退出的監控,首先調用PsSetCreateProcessNotifyRoutine註冊進程監控回調函數,然後在回調函數裏面,判斷Create參數,分別處理進程創建和退出操作。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章