全局SetWindowsHookEx向所有线程注入DLL的工作机制解析

核心原因
你观察到的差异本质是WH_KEYBOARD_LL这类带LL后缀的低层钩子,和常规全局钩子的实现机制完全不同,它根本不需要做DLL注入。
两类全局钩子的实现差异
- 注入式全局钩子:你安装的针对任务栏线程的
WH_MOUSE就属于这类,同类的还有不带LL后缀的全局WH_KEYBOARD等。这类钩子的回调函数要求在接收输入事件的目标进程上下文内执行,因此Windows必须把包含钩子逻辑的自定义DLL加载到所有符合触发条件、且加载了user32.dll的进程地址空间中,这也是你能在Process Explorer中看到explorer.exe被注入DLL的原因。这类钩子的注册信息确实会和user32.dll的加载逻辑绑定,新进程启动加载user32.dll时会自动加载注册过的钩子DLL,你之前的猜测对这类钩子是成立的。 - 非注入式低层钩子:
WH_KEYBOARD_LL和WH_MOUSE_LL是Windows专门设计的特殊钩子类型,完全不遵循上述注入逻辑。
WH_KEYBOARD_LL的具体工作逻辑 这类钩子的回调函数全程在安装钩子的进程自身的上下文中执行,不需要进入其他进程的地址空间:
- 调用
SetWindowsHookEx安装WH_KEYBOARD_LL全局钩子时,Windows只会把钩子的注册信息、回调所在的线程ID记录在内核层的全局输入处理链表中,不会对任何其他进程执行DLL注入操作,自然不会在Process Explorer里留下注入痕迹。 - 内核在收到原始键盘输入事件时,不会第一时间把事件投递给最终接收输入的目标窗口,而是先通过线程消息队列,给安装钩子的线程发送特殊通知,唤起你写的钩子回调函数执行。
- 等你的钩子回调处理完成、返回放行/拦截的结果后,内核才会把输入事件继续往下传递,投递到目标进程的消息队列。
对应现象的解释
- 新启动的进程也能被钩子捕获:因为钩子注册信息保存在内核全局链表里,和进程是否已经启动无关,只要有键盘输入产生,内核就会优先走钩子通知流程,和目标进程什么时候启动、有没有加载
user32.dll没有关系。 - 为什么安装LL钩子的线程必须有消息循环:因为内核给钩子线程发通知是走标准线程消息机制的,如果安装钩子的线程不运行
GetMessage/PeekMessage驱动的消息循环,就收不到内核的通知,钩子直接失效。这是LL钩子独有的要求,注入式全局钩子不需要安装线程常驻跑消息循环。 - 这类钩子因为不需要跨进程注入,权限要求比注入式钩子更低,也很少触发安全软件的注入告警,是绝大多数全局输入监控、全局快捷键功能优先选择的实现方案。
内容的提问来源于stack exchange,提问作者ScienceDiscoverer
相关产品推荐
相关产品推荐

