Pue*_*tis 2 c++ hook winapi keyhook
目前,我在使全局 keyhook 在 Windows 7 64 位操作系统上运行时遇到一些麻烦。现在我知道 Stackoverflow 上已经有很多关于这个主题的帖子,但没有一个能给我一个可以使用的答案,或者我不太明白这些帖子中的问题是如何解决的。
因此,我将尝试解释我所遇到的困难,并希望任何人都可以帮助我或为我指明正确的方向。
基本上我的目标是拦截 CTRL+C 和 CTRL+V 键盘输入以实现某种剪贴板管理器。因此,我当前的尝试是注册一个系统范围的WH_KEYBOARD挂钩,根据我的需要处理截获的击键。
我在 64 位 Windows 7 操作系统上运行钩子,这就是问题开始的地方。对我来说很明显,32 位 Hook DLL 会给 64 位进程带来问题,反之亦然。因此,我生成了包含该钩子的库的 x86 和 x64 版本,以及该钩子的调用者(调用 的那个SetWindowsHookEx()),两者都具有不同的文件名,如文档所示。
但现在怎么办?如果我将 64 位 DLL 设置为系统范围的挂钩,则所有 32 位应用程序在聚焦时按下某个键后就会开始挂起。同样的事情,当我应用 32 位挂钩时,我的 Windows 几乎无法使用,因为它explorer.exe是 64 位的。如果我设置了两个钩子,我的系统就会有效地处于停滞状态,进行全局“bitness”战斗。
现在我假设问题是由 64 位挂钩 DLL 试图注入 32 位进程等引起的,这当然是无意义的。但对于这种情况,文档SetWindowsHookEx()说明如下:
由于挂钩在应用程序的上下文中运行,因此它们必须与应用程序的“位数”匹配。如果 32 位应用程序在 64 位 Windows 上安装全局挂钩,则该 32 位挂钩将被注入到每个 32 位进程中(适用通常的安全边界)。在 64 位进程中,线程仍标记为“挂钩”。但是,由于 32 位应用程序必须运行挂钩代码,因此系统会在挂钩应用程序的上下文中执行挂钩;具体来说,在调用 SetWindowsHookEx 的线程上。这意味着挂钩应用程序必须继续发送消息,否则可能会阻止 64 位进程的正常运行。
我不完全理解文本的粗体部分,但我将其解释为如果挂钩目标的“位数”与挂钩的“位数”不同,则它会在实际设置挂钩的线程上执行,因此它完全可以被执行。此外,这意味着该线程必须仍然处于活动状态并且可能正在运行某种消息循环。它是否正确?还是我完全不喜欢这个?该文档似乎还给出了有关如何处理我的场景的确切说明:
要挂钩 64 位 Windows 安装的桌面上的所有应用程序,请安装一个 32 位全局挂钩和一个 64 位全局挂钩,每个挂钩都来自适当的进程,并确保在挂钩应用程序中不断泵送消息,以避免阻止正常运行发挥作用。
但我仍然不明白在我的实施中必须做什么。为了最终展示一些代码,让我们看一下尝试设置系统范围的 keyhook 的基本示例。我想线程的创建代码应该是无关紧要的:
volatile static bool runThread = false;
DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));
runThread = true;
while(runThread) {
// Message pump? Yes? No? How?
Sleep(10);
}
UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
钩子本身非常简单 - 它足以在跨越位数时导致挂起问题:
extern "C" LRESULT hookProc(int code, WPARAM wParam, LPARAM lParam) {
if(code == HC_ACTION) {
}
return CallNextHookEx(nullptr, code, wParam, lParam);
}
Run Code Online (Sandbox Code Playgroud)
我想有些人现在可能会把手举过头顶,你可以看出我很少使用钩子;)
但这正是我问的原因:)
简而言之:如果有人能告诉我如何更改上面的示例以使系统范围的 keyhook 在 64 位 Windows 上工作,我将不胜感激。我的问题是,某些具有除钩子之外的“位数”的应用程序开始挂起,我不知道如何解决该问题。
很感谢任何形式的帮助!
感谢致敬
普洱诺提斯
好吧,我知道问题出在哪里了。也许我的解决方案可以帮助遇到同样问题的其他人:如上所述,文档明确指出
[...]系统在(32位)挂钩应用程序的上下文中执行(32位)挂钩;具体来说,在调用 SetWindowsHookEx 的线程上。这意味着挂钩应用程序必须继续发送消息,否则可能会阻止 64 位进程的正常运行。
我所经历的是提到的阻塞行为,应该通过消息泵来克服。在我上面的代码示例中,正是缺少这个机制,因为我不知道这应该是一个简单的 Windows 消息循环(我之前不知道术语“泵”)。我的初始代码中的最终代码片段必须如下所示:
volatile static bool runThread = false;
DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));
MSG msg;
runThread = true;
while(runThread) {
// Keep pumping...
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
TranslateMessage(&msg);
DispatchMessage(&msg);
Sleep(10);
}
UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,我使用非阻塞PeekMessage()而不是GetMessage(),因为我希望我的线程不断检查它的终止标志。
通过此实现,我的挂钩在所有本机 64 位或 WOW64 进程中按预期工作,没有应用程序在挂钩后立即挂起。消息泵是唯一缺少的部分。
经过所有这些实验,我得出以下结论 - 如果我错了,请在评论中纠正我:
当安装系统范围的钩子时,它会尝试将给定的 Hook-DLL 注入到每个正在运行的进程中。如果进程的“位数”与 Hook-DLL 中的“位数”相匹配,则挂钩过程将作为目标进程中的远程线程执行(就像正常的注入器工作一样)。如果“位数”不同,Windows 会回退到最初调用的进程和线程SetWindowsHookEx()(在我的示例中是代码片段中的线程),并充当与不匹配的进程的执行代理。 “位”。因此,该线程需要不断处理传入消息,否则将不会为原始目标进程处理任何事件,从而开始挂起。
再次强调,如果有不完全正确、不完整的地方,或者有任何进一步的注释需要添加,请在下面发表评论。谢谢!:)