SetWindowsHookEx是DLL注入的“合法后门”,因其调用系统机制自动将指定DLL映射到所有符合条件的目标进程地址空间,无需远程线程、内存写入或APC操作,满足hMod有效、全局钩子类型(如WH_KEYBOARD_LL)、目标为交互式桌面进程三大条件即可触发系统级DLL加载。

为什么 SetWindowsHookEx 是 DLL 注入的“合法后门”
因为 SetWindowsHookEx 要求钩子过程必须在 DLL 中,而 Windows 会自动把该 DLL 映射到目标进程地址空间——这本质上就是一次受系统认可的、跨进程的 DLL 加载。它不依赖写内存、远程线程或 APC,绕过了部分 EDR 对 CreateRemoteThread 的监控。
但注意:这个“合法”仅指调用方式合规,不代表免杀或合规于安全策略;现代 Defender 或火绒仍可能基于行为(如全局钩子 + 非系统 DLL)拦截或报毒。
必须满足的三个硬性条件
缺一不可,否则 SetWindowsHookEx 返回 NULL,且 GetLastError() 常是 1428(ERROR_HOOK_NOT_INSTALLED):
-
hMod参数必须是非 NULL 的 HMODULE,且该模块必须已由目标进程加载——所以你要先确保 DLL 已被注入(矛盾?见下一条) - 钩子类型要是“全局钩子”,即
WH_GETMESSAGE、WH_KEYBOARD_LL等以_LL结尾或要求跨进程的类型;WH_CALLWNDPROC这类线程级钩子无法触发 DLL 映射 - 必须是“桌面交互式”进程才能接收全局钩子:目标进程不能是服务(Session 0)、不能以
CREATE_SUSPENDED启动且未调用SetThreadDesktop,也不能运行在无桌面会话中(如计划任务默认环境)
怎么绕过“DLL 还没进进程,怎么传 hMod?”这个死循环
答案是:不用手动 LoadLibrary,靠系统自动映射。关键在于调用时机和 DLL 属性:
立即学习“C++免费学习笔记(深入)”;
直播话术巡检系统(Windows版) — 实时语音转写、关键词检测、场景自动切换、告警推送、大屏可视化。支持 Windows VB-Cable 虚拟声卡音频采集,阿里云 ASR 实时识别。一键启动脚本,自动安装环境。
- 你的 DLL 必须导出一个符合钩子函数签名的函数,比如
LowLevelKeyboardProc,且该函数不能是 static 或匿名 namespace 内的 - 调用
SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)MyProc, hMod, 0)时,hMod应填你自己的 DLL 的 HMODULE(来自GetModuleHandle(NULL)或LoadLibrary当前进程的副本),不是目标进程的——系统会拿这个句柄作为“模板”,在目标进程中重新LoadLibrary同名 DLL - 因此 DLL 文件名必须存在于目标进程能访问的路径中(推荐放
%APPDATA%或当前目录),且不能带版本号或随机后缀(否则系统找不到同名文件) - 必须关闭 DLL 的 ASLR(在链接器设置里加
/DYNAMICBASE:NO)——否则不同进程加载基址不同,系统可能拒绝映射(尤其 Win10 1809+)
常见失败现象和对应检查点
SetWindowsHookEx 返回 NULL,但 GetLastError 是 5(拒绝访问)?大概率是权限问题:你的进程没开 SE_DEBUG_PRIVILEGE,或目标进程是更高完整性级别(如管理员启动的 Chrome)。此时换用 WH_KEYBOARD_LL 更稳妥,因为它不要求权限,只依赖消息循环存在。
钩子函数在本进程执行了,但目标进程没加载 DLL?检查:GetModuleFileName(GetModuleHandle(NULL), ...) 输出的路径是否含空格或 Unicode;是否用了相对路径;DLL 是否被杀软锁定(尝试复制到 %TEMP% 后再试)。
注入成功但钩子不触发?确认目标窗口有消息循环(GUI 线程),且没调用 PeekMessage(..., PM_NOREMOVE) 后长期不 DispatchMessage;控制台程序默认无消息循环,需手动加 GetMessage 循环才生效。
最易忽略的一点:DLL 入口函数 DllMain 中禁止调用 LoadLibrary、CreateThread、WaitForSingleObject 等可能引发锁或重入的 API——否则会导致目标进程卡死在 LdrpCallInitRoutine。初始化逻辑应延迟到钩子函数首次被调用时做。

















