Windows 鉤子是一種強大的系統機制,允許應用程式攔截並處理系統或特定執行緒的事件訊息(如鍵盤、滑鼠、視窗訊息等)。透過「掛鉤」,開發者可以在事件到達目標視窗程序前進行過濾、修改、記錄甚至阻止,實現全域熱鍵、輸入法編輯器、自動化測試、安全監控等功能。
| 類型 | 範圍 | 特點 | 典型用途 |
|---|---|---|---|
| 局部鉤子 | 僅限當前進程 | 無需 DLL,效能較好 | 應用程式內部訊息處理 |
| 遠程鉤子(執行緒) | 指定進程的特定執行緒 | 需 DLL 注入 | 針對特定應用程式監控 |
| 遠程鉤子(系統全域) | 所有進程所有執行緒 | 需 DLL,影響系統效能 | 全域熱鍵、記錄器、安全軟體 |
SetWindowsHookExHHOOK SetWindowsHookEx(
int idHook, // 鉤子類型:WH_KEYBOARD, WH_MOUSE, WH_CALLWNDPROC 等
HOOKPROC lpfn, // 鉤子回呼函數位址(遠程鉤子必須在 DLL 中)
HINSTANCE hMod, // 包含回呼函數的 DLL 實例句柄(局部鉤子為 NULL)
DWORD dwThreadId // 目標執行緒 ID:0=系統全域,當前進程執行緒=局部,其他進程執行緒=遠程
);
LRESULT CALLBACK HookProc(
int nCode, // 處理動作代碼:HC_ACTION(處理), HC_NOREMOVE(檢視不移除)
WPARAM wParam, // 訊息相關參數
LPARAM lParam // 訊息相關參數(通常為結構體指標)
);
CallNextHookExLRESULT CallNextHookEx(
HHOOK hhk, // 當前鉤子句柄
int nCode, // 原樣傳遞
WPARAM wParam, // 原樣傳遞
LPARAM lParam // 原樣傳遞
);
鉤子鏈採「後進先出」順序,新安裝的鉤子在鏈頭。必須呼叫 CallNextHookEx 以維持鏈完整,除非明確要攔截並丟棄事件。
UnhookWindowsHookExBOOL UnhookWindowsHookEx(HHOOK hhk);
遠程鉤子的 DLL 會被映射到每個目標進程的位址空間。預設情況下,代碼段共享,資料段各進程獨立複製。但鉤子函數常需在進程間共享狀態(如鉤子句柄 `hHook`、目標視窗句柄 `hWnd`),必須將資料段設為共享:
; MASM 連結器選項
/SECTION:.bss,S ; 將 .bss(未初始化資料段)設為共享
; 或
/SECTION:.data,S ; 將 .data(已初始化資料段)設為共享
範例 Makefile 片段:
NAME=mousehook
$(NAME).dll: $(NAME).obj
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib $(NAME).obj
本範例包含兩個模組:主程式(GUI 對話框)與鉤子 DLL。
.data
HookFlag dd FALSE
hHook dd ?
hInstance dd ?
.code
; 安裝鉤子
invoke InstallHook, hDlg
.if eax != NULL
mov HookFlag, TRUE
invoke SetDlgItemText, hDlg, IDC_HOOK, addr UnhookText
.endif
; 協定自訂訊息 WM_MOUSEHOOK (WM_USER+6)
; 接收 DLL 送來的視窗句柄 (wParam),查詢類別名、視窗程序位址並顯示
.elseif uMsg == WM_MOUSEHOOK
invoke GetClassName, wParam, addr buffer, 128
invoke SetDlgItemText, hDlg, IDC_CLASSNAME, addr buffer
invoke GetClassLong, wParam, GCL_WNDPROC
invoke wsprintf, addr buffer, addr template, eax
invoke SetDlgItemText, hDlg, IDC_WNDPROC, addr buffer
; 共享資料段
.data?
hHook dd ?
hWnd dd ?
.code
MouseProc proc nCode:DWORD, wParam:DWORD, lParam:DWORD
; 1. 必須先傳遞給下一個鉤子
invoke CallNextHookEx, hHook, nCode, wParam, lParam
; 2. 從 MOUSEHOOKSTRUCT 取得螢幕座標
mov edx, lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint, [edx].pt.x, [edx].pt.y ; 取得座標下的視窗句柄
; 3. 發送自訂訊息給主程式(絕對不可用 SendMessage,會造成死結!)
invoke PostMessage, hWnd, WM_MOUSEHOOK, eax, 0
assume edx:nothing
xor eax, eax
ret
MouseProc endp
InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx, WH_MOUSE, addr MouseProc, hInstance, NULL ; NULL = 系統全域
mov hHook, eax
ret
InstallHook endp
MOUSEHOOKSTRUCT STRUCT
pt POINT <> ; 螢幕座標
hwnd DWORD ? ; 接收訊息的視窗句柄
wHitTestCode DWORD ? ; 擊測代碼 (WM_NCHITTEST 值)
dwExtraInfo DWORD ? ; 額外資訊
MOUSEHOOKSTRUCT ENDS
Windows 鉤子是雙面刃:功能強大但責任重大。掌握其分類、API 流程、DLL 共享資料段、鉤子鏈傳遞機制,並遵循「最小權限、最小範圍、最短停留」原則,才能安全高效地發揮其價值。本文提供的 MASM 完整範例可作為進一步開發的基礎骨架。