概述
HOOK
技术(钩子技术)是Windows
操作系统中的一种核心机制,允许程序拦截并处理系统事件、消息或函数调用- 通过
HOOK
,开发者可以监控或修改系统的行为,例如键盘输入、鼠标点击、窗口创建/销毁等HOOK
技术广泛应用于安全防护、自动化工具、调试分析等领域
原理
消息机制与HOOK
Windows
是一个基于消息驱动的系统,用户输入(如键盘、鼠标)、窗口事件等均以消息形式传递HOOK
本质是在消息传递链中插入一个回调函数(钩子函数),在消息到达目标窗口前或后拦截处理
HOOK类型
- 按作用范围
- 全局钩子(
Global Hook
):影响所有进程(需通过DLL
注入实现) - 线程级钩子(
Thread-Specific Hook
):仅监控特定线程的消息
- 全局钩子(
- 按消息类型
- 键盘钩子(
WH_KEYBOARD/WH_KEYBOARD_LL
) - 鼠标钩子(
WH_MOUSE/WH_MOUSE_LL
) - 窗口消息钩子(
WH_CALLWNDPROC
) - 系统消息钩子(
WH_SYSMSGFILTER
) - 调试钩子(
WH_DEBUG
)等
- 键盘钩子(
DLL注入
- 全局钩子需要通过
DLL
注入到目标进程的地址空间 - 当钩子触发时,系统会将DLL加载到目标进程内存中,并在目标进程中执行回调函数
底层原理
- 消息传递链
- 当事件发生时,系统将消息依次传递给:
- 系统队列 → 线程队列 → 窗口过程(
Window Procedure
) HOOK
在此链的某个节点插入拦截逻辑
- 回调函数执行环境
- 全局钩子的回调函数在目标进程的上下文中执行,需通过
DLL
注入 - 低级钩子(如
WH_KEYBOARD_LL
)在发送消息的线程上下文中执行,无需DLL
注入
- 全局钩子的回调函数在目标进程的上下文中执行,需通过
- 系统兼容性
64
位系统对全局钩子的限制更严格,需编译64
位DLL
实现步骤
核心API
函数
- 安装钩子
idHook
: 钩子类型(如WH_KEYBOARD_LL
)lpfn
: 回调函数指针hMod
: 包含回调函数的DLL
句柄(全局钩子必需)dwThreadId
: 目标线程ID
(0
表示全局钩子)
1 |
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId); |
- 卸载钩子
1 |
UnhookWindowsHookEx |
- 将消息传递给下一个钩子或默认处理
1 |
CallNextHookEx |
示例低级键盘钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <Windows.h> HHOOK g_hHook; // 回调函数 LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) { KBDLLHOOKSTRUCT* pKeyInfo = (KBDLLHOOKSTRUCT*)lParam; if (wParam == WM_KEYDOWN) { // 处理按键按下事件 if (pKeyInfo->vkCode == VK_ESCAPE) { // 阻止ESC键传递 return 1; } } } return CallNextHookEx(g_hHook, nCode, wParam, lParam); } int main() { // 安装全局低级键盘钩子 g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0); // 消息循环(必须存在,否则钩子失效) MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 卸载钩子 UnhookWindowsHookEx(g_hHook); return 0; } |
注意事项
- 全局钩子需要
DLL
:若使用WH_KEYBOARD
等非低级钩子,需将回调函数放在DLL
中 - 权限要求:低级钩子(如
WH_KEYBOARD_LL
)需要程序以管理员权限运行 - 性能影响:全局钩子可能导致系统变慢,需谨慎设计
DLL
注入失败:目标进程可能被保护(如杀毒软件)- 防
HOOK
机制:部分程序主动检测HOOK
并阻止(如游戏反作弊系统)
DLL注入
什么是DLL注入
DLL
注入(Dynamic-Link Library Injection
)是将自定义的DLL
文件加载到目标进程的内存空间中,使目标进程执行DLL
中的代码- 这是实现全局钩子(如
WH_KEYBOARD
)、进程监控、内存修改等技术的核心手段
常见注入方法
方法 | 原理 | 适用场景 |
SetWindowsHookEx |
通过钩子机制强制目标进程加载DLL | 全局钩子(如键盘、鼠标监控) |
CreateRemoteThread |
在目标进程中创建远程线程,执行LoadLibrary 加载DLL |
通用注入(需进程权限) |
注册表注入 | 修改注册表键值(如AppInit_DLLs ),系统启动时自动加载DLL到所有进程 |
开机自启的全局监控(已逐渐被淘汰) |
APC注入 | 利用异步过程调用(APC)在目标线程中插入DLL加载代码 | 针对特定线程的注入 |
CreateRemoteThread
- 编写一个等待注入的
DLL
- 编译为
MyHookDll.dll
- 编译为
1 2 3 4 5 6 7 8 9 10 |
// MyHookDll.cpp #include <Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { if (reason == DLL_PROCESS_ATTACH) { // DLL被加载时执行 MessageBoxA(NULL, "DLL注入成功!", "提示", MB_OK); } return TRUE; } |
- 注入器程序
EXE
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
// Injector.cpp #include <Windows.h> #include <TlHelp32.h> // 获取目标进程ID(以进程名为例) DWORD GetProcessIdByName(const char* processName) { PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(snapshot, &entry)) { do { if (_stricmp(entry.szExeFile, processName) == 0) { CloseHandle(snapshot); return entry.th32ProcessID; } } while (Process32Next(snapshot, &entry)); } CloseHandle(snapshot); return 0; } // 主注入函数 bool InjectDll(DWORD pid, const char* dllPath) { // 1. 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) return false; // 2. 在目标进程中分配内存 LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE); if (!remoteMem) { CloseHandle(hProcess); return false; } // 3. 写入DLL路径到目标进程内存 WriteProcessMemory(hProcess, remoteMem, dllPath, strlen(dllPath) + 1, NULL); // 4. 获取LoadLibraryA函数地址(在Kernel32.dll中) LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); // 5. 创建远程线程执行LoadLibraryA HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, remoteMem, 0, NULL); if (!hThread) { VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hProcess); return false; } // 6. 等待线程执行完成 WaitForSingleObject(hThread, INFINITE); // 7. 清理资源 VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return true; } int main() { // 示例:向记事本(notepad.exe)注入DLL DWORD pid = GetProcessIdByName("notepad.exe"); if (pid == 0) { printf("未找到目标进程!\n"); return 1; } const char* dllPath = "C:\\path\\to\\MyHookDll.dll"; if (InjectDll(pid, dllPath)) { printf("注入成功!\n"); } else { printf("注入失败!\n"); } return 0; } |
应用场景
- 快捷键监听
- 实现全局热键(如截图工具、音量控制)
- 安全防护
- 监控恶意键盘输入(如键盘记录器防御)
- 自动化与
UI
测试- 模拟用户操作(如自动填写表单)
- 调试与逆向
- 分析程序行为(如拦截
API
调用)
- 分析程序行为(如拦截
- 输入法、辅助工具
- 实现输入法候选窗口、屏幕取词等
理解与疑问
低级键盘钩子和键盘钩子
- 作用范围
类型 | 作用范围 | 是否需要DLL注入 |
低级键盘钩子 | 全局(所有进程的键盘输入) | 不需要(代码可直接在EXE中实现) |
全局键盘钩子 | 全局(所有进程的键盘输入) | 必须通过DLL注入到目标进程 |
- 低级键盘钩子实现方式
- 实现代码可直接写在
EXE
中,无需单独编译DLL
- 回调函数在发送消息的线程上下文中执行(通常为安装钩子的线程)
- 实现代码可直接写在
1 2 |
// 直接在EXE中编写回调函数 HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0); |
- 全局键盘钩子实现方式
- 必须将回调函数封装在
DLL
中,由系统注入到其他进程 - 回调函数在目标进程的上下文中执行(例如被监控的记事本进程)
- 必须将回调函数封装在
1 2 3 4 5 6 7 |
// 在DLL中导出回调函数 __declspec(dllexport) LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam); // EXE中安装钩子时需加载DLL HINSTANCE hDll = LoadLibrary(L"KeyboardHook.dll"); HOOKPROC proc = (HOOKPROC)GetProcAddress(hDll, "KeyboardProc"); HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD, proc, hDll, 0); |
- 权限
特性 | 低级键盘钩子 | 全局键盘钩子 |
管理员权限 | 需要(以管理员身份运行) | 通常不需要 |
64/32位兼容性 | 无需处理,天然支持跨位操作 | 需编译对应位数的DLL(如监控64位程序需64位DLL) |
- 数据隔离性
- 低级钩子:检测用户按下
Ctrl+C
并记录到日志文件 - 全局钩子:拦截
Ctrl+V
并修改目标进程的剪贴板内容
- 低级钩子:检测用户按下
类型 | 数据访问能力 | 风险 |
低级键盘钩子 | 只能访问 原始输入数据(如虚拟键码) | 无法直接读写目标进程内存 |
全局键盘钩子 | 可直接操作 目标进程内存(通过DLL注入) | 易引发安全漏洞或进程崩溃 |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ WinDbg相关01/12
- ♥ Spy++相关08/18
- ♥ Soui应用 动画一06/24
- ♥ 动态库10/02
- ♥ Windows核心编程_必备知识04/27
- ♥ Windows系统学习一03/21