《Windows 核心程式設計》介紹三種代碼注入法,全域訊息鉤子是其中之一,普遍認為必須依賴 DLL。實則四種特殊鉤子可在安裝執行緒內文脈執行,無需 DLL:
WH_JOURNALPLAYBACKWH_JOURNALRECORDWH_KEYBOARD_LL (低層鍵盤)WH_MOUSE_LL (低層滑鼠)MSDN 說明:「This hook is called in the context of the thread that installed it.」 即鉤子程序在安裝執行緒上下文執行,無法達到代碼注入效果,故不需 DLL。
#define _WIN32_WINNT 0x0400
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
DWORD g_main_tid = 0;
HHOOK g_kb_hook = 0;
BOOL CALLBACK ConsoleHandler(DWORD) {
PostThreadMessage(g_main_tid, WM_QUIT, 0, 0);
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int code, WPARAM w, LPARAM l) {
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;
const char *info = NULL;
if (w == WM_KEYDOWN) info = "key dn";
else if (w == WM_KEYUP) info = "key up";
else if (w == WM_SYSKEYDOWN) info = "sys key dn";
else if (w == WM_SYSKEYUP) info = "sys key up";
printf("%s - vkCode [%04x], scanCode [%04x]\n", info, p->vkCode, p->scanCode);
return CallNextHookEx(g_kb_hook, code, w, l);
}
int main() {
g_main_tid = GetCurrentThreadId();
SetConsoleCtrlHandler(ConsoleHandler, TRUE);
g_kb_hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, GetModuleHandle(NULL), 0);
if (!g_kb_hook) {
fprintf(stderr, "SetWindowsHookEx failed: %d\n", GetLastError());
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) { // 訊息迴圈必要 (MSDN)
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(g_kb_hook);
return 0;
}
SetWindowsHookEx 第 3 參數 不可為 NULL (需 GetModuleHandle(NULL)),否則失敗GetMessage 迴圈),否則鉤子不會被呼叫WH_KEYBOARD_LL 可攔截系統層級鍵盤事件,適用於熱鍵、鍵盤記錄器等場景WH_MOUSE_LL 可實現免 DLL 全域滑鼠鉤子