首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

Windows 鉤子機制完整指南:從原理到實戰範例

     編輯:組合語言

什麼是 Windows 鉤子?

Windows 鉤子是一種強大的系統機制,允許應用程式攔截並處理系統或特定執行緒的事件訊息(如鍵盤、滑鼠、視窗訊息等)。透過「掛鉤」,開發者可以在事件到達目標視窗程序前進行過濾、修改、記錄甚至阻止,實現全域熱鍵、輸入法編輯器、自動化測試、安全監控等功能。

鉤子分類與特性

類型範圍特點典型用途
局部鉤子僅限當前進程無需 DLL,效能較好應用程式內部訊息處理
遠程鉤子(執行緒)指定進程的特定執行緒需 DLL 注入針對特定應用程式監控
遠程鉤子(系統全域)所有進程所有執行緒需 DLL,影響系統效能全域熱鍵、記錄器、安全軟體

⚠ 效能與責任警示

  • 系統全域鉤子會顯著降低系統回應速度,因每個相關事件都會觸發回呼。
  • 鉤子函數若發生死循環或死結,將導致整個系統無回應。
  • 原則:用完立即卸載,能用局部不用全域,能用執行緒鉤子不用系統鉤子。

核心 API 與資料結構

安裝鉤子:SetWindowsHookEx

HHOOK 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      // 訊息相關參數(通常為結構體指標)
);

傳遞給下一個鉤子:CallNextHookEx

LRESULT CallNextHookEx(
  HHOOK hhk,      // 當前鉤子句柄
  int   nCode,    // 原樣傳遞
  WPARAM wParam,  // 原樣傳遞
  LPARAM lParam   // 原樣傳遞
);

鉤子鏈採「後進先出」順序,新安裝的鉤子在鏈頭。必須呼叫 CallNextHookEx 以維持鏈完整,除非明確要攔截並丟棄事件。

卸載鉤子:UnhookWindowsHookEx

BOOL UnhookWindowsHookEx(HHOOK hhk);

14 種標準鉤子類型一覽

  • WH_KEYBOARD / WH_MOUSE:鍵盤/滑鼠訊息(經 GetMessage/PeekMessage 時觸發)
  • WH_CALLWNDPROC / WH_CALLWNDPROCRET:SendMessage 呼叫前/後
  • WH_GETMESSAGE:GetMessage/PeekMessage 取得訊息時
  • WH_CBT:電腦輔助訓練事件(視窗建立/銷毀/啟用/移動/大小變更等)
  • WH_SHELL:Shell 事件(任務列按鈕重繪等)
  • WH_JOURNALRECORD / WH_JOURNALPLAYBACK:硬體輸入記錄/回放(特殊:回呼必須在安裝執行緒,不需 DLL)
  • WH_DEBUG:除錯用
  • 其他:WH_MSGFILTER、WH_SYSMSGFILTER、WH_HARDWARE、WH_FOREGROUNDIDLE

關鍵技術細節:DLL 共享資料段

遠程鉤子的 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

DLL 關鍵碼

; 共享資料段
.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 結構

MOUSEHOOKSTRUCT STRUCT
    pt            POINT <>   ; 螢幕座標
    hwnd          DWORD ?    ; 接收訊息的視窗句柄
    wHitTestCode  DWORD ?    ; 擊測代碼 (WM_NCHITTEST 值)
    dwExtraInfo   DWORD ?    ; 額外資訊
MOUSEHOOKSTRUCT ENDS

常見陷阱與最佳實務

  1. 鉤子函數中禁用 SendMessage:會導致跨進程死結,必須用 PostMessage 或非同步機制。
  2. DLL 實例句柄更新問題:若 DLL 重新載入位址改變,`hInstance` 需在 `DLL_PROCESS_ATTACH` 時重新獲取,否則鉤子會退化為局部鉤子。
  3. 最小化鉤子處理時間:鉤子函數執行期間系統訊息泵被阻塞,複雜邏輯請丟到工作執行緒或用 PostMessage 通知主程式處理。
  4. 正確處理鉤子鏈:除非明確攔截,否則務必呼叫 `CallNextHookEx`。
  5. 64 位元相容性:32 位元鉤子無法攔截 64 位元進程,需分別編譯 32/64 位元 DLL。

總結

Windows 鉤子是雙面刃:功能強大但責任重大。掌握其分類、API 流程、DLL 共享資料段、鉤子鏈傳遞機制,並遵循「最小權限、最小範圍、最短停留」原則,才能安全高效地發揮其價值。本文提供的 MASM 完整範例可作為進一步開發的基礎骨架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved