鉤子是 Windows 消息處理機制的擴展點,允許應用程序安裝回調函數監視、修改或攔截特定類型的消息/事件。鉤子形成鏈表,消息到達目標窗口前依次經過鏈上各鉤子處理程序。每個鉤子可選擇:處理並傳遞給下一個、修改消息後傳遞、或直接丟棄消息阻止繼續傳遞。
HookProc。LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)。nCode 指示處理階段(如 HC_ACTION 表示需處理),負值時必須調用 CallNextHookEx 且不得修改消息。HHOOK SetWindowsHookEx(
int idHook, // 鉤子類型:WH_KEYBOARD, WH_MOUSE, WH_GETMESSAGE 等
HOOKPROC lpfn, // 回調函數地址(全局鉤子必須在 DLL 中)
HINSTANCE hMod, // 包含回調函數的 DLL 實例句柄(線程鉤子可為 NULL)
DWORD dwThreadId // 0=全局鉤子,非0=指定線程鉤子
);
BOOL UnhookWindowsHookEx(HHOOK hhk); // 卸載
LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam); // 傳遞給下一個
關鍵規則:全局鉤子(dwThreadId=0)回調必須位於 DLL,系統會將該 DLL 注入所有相關進程地址空間;線程鉤子回調可在 EXE 內部。
Win32 進程間全局數據默認不共享。全局鉤子需跨進程共享 HHOOK 句柄等數據,解決方案:
#pragma data_seg("Shared") HHOOK hHook=NULL; #pragma data_seg() #pragma comment(linker,"/section:.Shared,rws")CreateFileMapping/MapViewOfFile 實現跨進程共享內存。未初始化的變量會被放入 .bss 段,無法共享,必須顯式初始化。
| 類型 | 用途 | 備註 |
|---|---|---|
| WH_KEYBOARD / WH_KEYBOARD_LL | 鍵盤消息 (WM_KEYDOWN/UP) | LL 版本為低級鉤子,不需 DLL,效率較高 |
| WH_MOUSE / WH_MOUSE_LL | 鼠標消息 | 同理有 LL 版本 |
| WH_GETMESSAGE | GetMessage/PeekMessage 返回的消息 | 可監視所有線程消息隊列 |
| WH_CALLWNDPROC / WH_CALLWNDPROCRET | 窗口過程調用前/後 | 後者可獲取返回值 (CWPRETSTRUCT) |
| WH_CBT | 窗口創建/激活/銷毀/移動/縮放等 | 返回值決定是否允許操作 |
| WH_SHELL | 頂層窗口創建/銷毀、任務欄通知等 | 需先調用 SystemParametersInfo 註冊 |
| WH_JOURNALRECORD / WH_JOURNALPLAYBACK | 記錄/回放輸入事件 | 全局鉤子,禁用正常輸入 |
| WH_MSGFILTER / WH_SYSMSGFILTER | 模態循環中菜單/滾動條/對話框消息 | 前者限當前應用,後者系統級 |
| WH_FOREGROUNDIDLE | 前台線程空閒時執行低優先級任務 | |
| WH_DEBUG | 調試其他鉤子調用 | 可決定是否允許調用其他鉤子 |
UnhookWindowsHookEx 必須在模塊卸載前調用,否則系統自動清理但可能導致目標進程崩潰。WH_KEYBOARD_LL、WH_MOUSE_LL 不需 DLL 注入,適合進程內監控。nCode < 0 時必須直接調用 CallNextHookEx 返回,不得修改消息。MessageBox、文件 I/O、鎖等待)。WH_KEYBOARD_LL)WH_GETMESSAGE)WH_CBT/WH_SHELL)WH_JOURNALRECORD/PLAYBACK)Windows 鉤子機制強大但雙刃劍:正確使用可實現系統級擴展,濫用會導致性能下降、穩定性風險甚至安全漏洞。開發者應充分理解鏈表調用順序、DLL 注入機制、跨進程數據共享、以及各鉤子類型的適用邊界,遵循「最小權限、最短停留、及時清理」原則。