从 Windows 键盘挂钩程序返回后崩溃
Crash after returning from Windows keyboard hook procedure
安装了这样的键盘挂钩:
s_hKeyboardHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, nullptr, ::GetCurrentThreadId());
(这是一个插件,它想要拦截发送到其主机(64 位)的键盘事件,即使主机没有以正常方式向其插件提供键盘事件。我不有宿主的源码,虽然我有插件的源码。)
键盘钩子程序成功 运行s 和 returns 后,程序崩溃。崩溃发生在 Windows' ZwCallbackReturn()
内部,执行 syscall
指令。例外是 0XC0000005
(访问冲突)。仅当按下触发某些特定逻辑的特定键时才会发生崩溃。
我无法诊断这次崩溃,真的需要一些帮助。我确信问题出在 hook proc 中的这一大段代码中。我遇到的问题是了解崩溃发生的位置以及基本上在哪里放置断点以抢占它。
附加信息:
1) hook 过程真的很重,有很多阻塞,i/o 和内存使用(在快速机器上几秒钟内完成)。也许这就是问题的一部分。
2) 如果编译为 32 位,崩溃后的堆栈看起来更有趣,但我怀疑它是否可信:
2a71f510() Unknown
ExecuteHandler2@20() Unknown
ExecuteHandler@20() Unknown
_RtlDispatchException@8() Unknown
_KiUserExceptionDispatcher@8() Unknown
2a10f24a() Unknown
_DispatchHookW@16() Unknown
_CallHookWithSEH@16() Unknown
___fnHkINDWORD@4() Unknown
_KiUserCallbackDispatcher@12() Unknown
_LdrAddLoadAsDataTable@20() Unknown
AfxInternalPumpMessage() Line 153 C++
AfxWinMain(0x00000000, 0x00000020, 0x00000001, 1638280) Line 47 C++
@BaseThreadInitThunk@12() Unknown
前 5 行重复多次。
这是我到目前为止所尝试的。据我了解, syscall
指令本身不会产生异常:寄存器看起来很正常,我猜如果它崩溃了,堆栈会保持不变。所以我认为在这条指令开始转换回内核模式后,从 "user callback"(钩子过程调用)的起源,内核继续到 运行 就好了。最终它应该 return 控制回用户空间 -GetMessage()
我想)。然后,我认为,堆栈被破坏并且程序崩溃。但不幸的是,在堆栈损坏之前,我无法指示我的 Visual C++ 调试器在执行的第一条用户模式指令处中断。我尝试在 TranslateMessage()
和 DispatchMessage()
中安装条件断点,它们最有可能在 GetMessage()
之后 运行,但它们不会在最后一个良好的用户模式指令之间触发和崩溃。
发生崩溃是因为键盘挂钩过程不是挂钩链中的第一个。它是通过 CallNextHookEx()
从挂钩链中的前一个挂钩调用的。之前的钩子是由一个 DLL 注册的,该 DLL 在 "our" 键盘钩子中被卸载。
因此,在所有的钩子最终都被调用之后,控制权又回到了第一个钩子过程,这个过程已经不存在了。崩溃是试图执行无效地址。
安装了这样的键盘挂钩:
s_hKeyboardHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, nullptr, ::GetCurrentThreadId());
(这是一个插件,它想要拦截发送到其主机(64 位)的键盘事件,即使主机没有以正常方式向其插件提供键盘事件。我不有宿主的源码,虽然我有插件的源码。)
键盘钩子程序成功 运行s 和 returns 后,程序崩溃。崩溃发生在 Windows' ZwCallbackReturn()
内部,执行 syscall
指令。例外是 0XC0000005
(访问冲突)。仅当按下触发某些特定逻辑的特定键时才会发生崩溃。
我无法诊断这次崩溃,真的需要一些帮助。我确信问题出在 hook proc 中的这一大段代码中。我遇到的问题是了解崩溃发生的位置以及基本上在哪里放置断点以抢占它。
附加信息:
1) hook 过程真的很重,有很多阻塞,i/o 和内存使用(在快速机器上几秒钟内完成)。也许这就是问题的一部分。
2) 如果编译为 32 位,崩溃后的堆栈看起来更有趣,但我怀疑它是否可信:
2a71f510() Unknown
ExecuteHandler2@20() Unknown
ExecuteHandler@20() Unknown
_RtlDispatchException@8() Unknown
_KiUserExceptionDispatcher@8() Unknown
2a10f24a() Unknown
_DispatchHookW@16() Unknown
_CallHookWithSEH@16() Unknown
___fnHkINDWORD@4() Unknown
_KiUserCallbackDispatcher@12() Unknown
_LdrAddLoadAsDataTable@20() Unknown
AfxInternalPumpMessage() Line 153 C++
AfxWinMain(0x00000000, 0x00000020, 0x00000001, 1638280) Line 47 C++
@BaseThreadInitThunk@12() Unknown
前 5 行重复多次。
这是我到目前为止所尝试的。据我了解, syscall
指令本身不会产生异常:寄存器看起来很正常,我猜如果它崩溃了,堆栈会保持不变。所以我认为在这条指令开始转换回内核模式后,从 "user callback"(钩子过程调用)的起源,内核继续到 运行 就好了。最终它应该 return 控制回用户空间 -GetMessage()
我想)。然后,我认为,堆栈被破坏并且程序崩溃。但不幸的是,在堆栈损坏之前,我无法指示我的 Visual C++ 调试器在执行的第一条用户模式指令处中断。我尝试在 TranslateMessage()
和 DispatchMessage()
中安装条件断点,它们最有可能在 GetMessage()
之后 运行,但它们不会在最后一个良好的用户模式指令之间触发和崩溃。
发生崩溃是因为键盘挂钩过程不是挂钩链中的第一个。它是通过 CallNextHookEx()
从挂钩链中的前一个挂钩调用的。之前的钩子是由一个 DLL 注册的,该 DLL 在 "our" 键盘钩子中被卸载。
因此,在所有的钩子最终都被调用之后,控制权又回到了第一个钩子过程,这个过程已经不存在了。崩溃是试图执行无效地址。