Windows系統中鉤子具有相當強大的功能,透過這種技術可以對幾乎所有的Windows 系統中的訊息進行攔截、監視、處理。這種技術可以廣泛應用於各種軟體,尤其是需要有監控、自動記錄等對系統進行監控功能的軟體。本文針對這個專題進行了探討,希望可以為讀者朋友們起到拋磚引玉的作用。
一、鉤子的機制及類型
Windows的應用程式都是基於訊息驅動的,應用程式的操作都依賴它所得到的訊息的類型及內容。鉤子與Dos中斷截獲處理機制有類似之處。鉤子(Hook)是Windows訊息處理機制的一個平台,透過安裝各種鉤子,應用程式可以在上面設定子程式以監視指定視窗的某種訊息,並且當訊息到達目標視窗之前處理它。
在Windows中,鉤子有兩種,一種是系統鉤子(RemoteHook),它對訊息的監視是整個系統範圍,另一種是線程鉤子(LocalHook),它的攔截範圍只有進程內部的消息。對於系統鉤子,其鉤子函數(HookFunction)應在Windows系統的動態連結庫(DLL)中實現,而對於執行緒鉤子來說,鉤子函數可以在DLL之中實現,也可以在對應的應用程式之中實現。這是因為當開發人員創建一個鉤子時,Windows先在系統內存中創建一個數據結構,該數據結構包含了鉤子的相關信息,然後把該結構體加到已經存在的鉤子鍊錶中去,並且新的鉤子將排在老的鉤子的前面。當一個事件發生時,如果安裝的是局部鉤子,則當前進程中的鉤子函數將被呼叫。如果是遠端鉤子,系統就必須把鉤子函數插入到其它進程的位址空間,要做到這一點就要求鉤子函數必須在一個動態連結庫中,所以如果想要使用遠端鉤子,就必須把該鉤子函數放到動態連結庫中去。對於鉤子所監視的訊息類型來說,Windws一共提供瞭如下幾種類型:如表1所示:
表一、Windows訊息類型
訊息類型常數標識 | 值 | 訊息類型 | 適用範圍 |
WH_CALLWNDPROC | 4 | 發送給視窗的訊息 | 線程或系統 |
WH_CALLWNDPROCRET | 12 | 視窗返回的訊息 | 線程或系統 |
WH_CBT | 5 | 視窗變化、焦點設定等訊息 | 線程或系統 |
WH_DEBUG | 9 | 是否執行其它Hook的Hook | 線程或系統 |
WH_FOREGROUNDIDLE | 11 | 前台程式空閒 | 線程或系統 |
WH_GETMESSAGE | 3 | 投放至訊息佇列中的消息 | 線程或系統 |
WH_JOURNALPLAYBACK | 1 | 將所記載的訊息進行回放 | 系統 |
WH_JOURNALRECORD | 0 | 監視並記錄輸入訊息 | 系統 |
WH_KEYBOARD | 2 | 鍵盤訊息 | 線程或系統 |
WH_MOUSE | 7 | 滑鼠訊息 | 線程或系統 |
WH_MSGFILTER | -1 | 選單滾動條、對話框訊息 | 線程或系統 |
WH_SHELL | 10 | 外殼程式的訊息 | 線程或系統 |
WH_SYSMSGFILTER | 6 | 所有執行緒的選單滾動條、對話框訊息 | 系統 |
二、VB編程中鉤子的實現
(一)鉤子函數(HOOK Function)的格式。 Hook Function實際上是一個函數,如果是系統鉤子,則該函數必須放在動態連結庫中。此函數有一定的參數格式,在VB中如下:
Private Function HookFunc(ByVal nCode As Long,ByVal wParam As Long,ByVal lParam As Long)As Long |
其中,nCode代表是什麼情況之下所產生的鉤子,隨鉤子的不同而有不同組的可能值;參數wParam,lParam傳回值包括了所監視到的消息內容,它隨Hook所監視消息的種類和nCode的值不同而不同。對於VB所設定的鉤子函數,一般的框架形式如下:
Private Function HookFunc(ByVal nCode As Long,ByVal wParam As Long,ByVal lParam As Long)As Long Select case of nCode case ncode<0:hookfunc=callnexthookex(hHookFunc,nCode,wParam,lParam) case值1:處理過程1:HookFunc=X1 case2:處理過程2:HookFunc=X1 …… end select end Function |
函數的傳回值,如果訊息要被處理,則傳0,否則傳1,吃掉訊息。
(二)鉤子的安裝及執行。鉤子的安裝要用到幾個API函數:可以使用API函數SetWindowsHookEx()把一個應用程式定義的鉤子子程安裝到鉤子鍊錶中。 SetWindowsHookEx()函數的宣告如下:
Declare function SetWindowsHookEx Lib user32 Alias SetWindowsHookExA(ByVal idHook As Long,ByVal lpfn As Long,ByVal hmod As Long,ByVal dwThreadId As Long)As Long |
idHook值為它處理的訊息類型;lpfn值為鉤子子程式的位址指標。如果dwThreadId參數為0或是一個由別的程序所建立的執行緒的標識,lpfn必須指向DLL中的鉤子子程。除此之外,lpfn可以指向目前行程的一段鉤子子程程式碼。 hMod值為應用程式的句柄,標識包含lpfn所指的子程的DLL。如果dwThreadId標識目前程序所建立的一個線程,而且子程碼位於目前進程,hMod必須為0。 dwThreadId值為與安裝的鉤子子程相關聯的執行緒的標識符,如果為0,鉤子子程與所有的執行緒關聯。鉤子安裝成功則傳回鉤子子程的句柄,失敗回0。
另外,一般應在鉤子子程中呼叫CallNextHookEx()函數以執行鉤子鍊錶所指的下一個鉤子子程,否則安裝了別的鉤子的應用程式就會收不到鉤子通知,從而產生錯誤的結果。 CallNextHookEx()函數的宣告如下:
Declare Function CallNextHookEx Libuser32 Alias CallNextHookEx(ByVal hHook As Long,ByVal ncode As Lonog, ByVal wParam As Long,lParam As Any)As Long |
hHook值是SetWindowsHookEx()的傳回值,nCode、wParam、lParam則是Hook函數中的三個參數。在程式終止之前,必須呼叫UnhookWindowsHookEx()函數釋放與鉤子關聯的系統資源。 UnhookWindowsEx()函數宣告如下:
Declare Function Unhook WindowsHookEx Lib user32 Alias Unhook WindowsHookEx(ByVal hHook As Long)As Long |
hHook為安裝鉤子時的回傳值,即鉤子子程的句柄。
(三)VB中鉤子安裝應注意的問題。 lpfn參數是一個HookFunc的位址,VB規定必須將HookFunc程式碼放到標準的.BAS模組中,並以Address Of HookFunc傳入,而不可以將其放到類別模組中,也不能將其附加到窗體上。而對於RemoteHook來說,HookFunc應包含在動態連結庫中,因此如果在VB中使用RemoteHook,則也要用到GetModuleHandle()、GetProcAddress()兩個API函數,它們的宣告如下:
Declare Function GetModuleHandle Libkernel32 Alias GetModuleHandleA(ByVal lpModuleName As String)As Long Declare Function GetProcAddress Lib kernel32 Alias GetProcAddress(ByVal hModule As Long,ByVal lpProcName As String)As Long |
hmod值是含鉤子過程的模組名柄,如果是LocalHook,該值可以是Null(VB中傳0),而如果是RemoteHook,則可以使用GetModuleHandle(name.dll)來傳入。