64位Windows上的Global Keyhook

时间:2013-03-21 08:43:13

标签: c++ winapi hook keyhook

我目前在使用Windows 7 64位O / S上运行全​​局keyhook时遇到一些麻烦。现在我知道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位。如果我设置两个钩子,我的系统就会停滞不前,进行全局“比特”斗争。

现在我认为这个问题来自于试图注入32位进程的64位挂钩DLL等等,这当然是无稽之谈。但是对于这种情况,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;
}

钩子本身保持非常微不足道 - 它足以在穿过钻头时引起挂起问题:

extern "C" LRESULT hookProc(int code, WPARAM wParam, LPARAM lParam) {
    if(code == HC_ACTION) {
    }

    return CallNextHookEx(nullptr, code, wParam, lParam);
}

我认为有些人现在可能会把手放在他们的头上,而且你可以说我很少使用钩子;)

但这正是我要问的原因:)

为了简短起见:如果有人能告诉我如何更改上面的示例以使64位Windows上的系统范围的键钩工作,我将不胜感激。我的问题是某些应用程序与其他“位数”比挂钩开始挂起,我不知道如何解决问题。

非常感谢任何帮助!

谢谢和问候

PuerNoctis

2 个答案:

答案 0 :(得分:4)

好的,我弄清楚问题是什么。也许我的解决方案可以帮助其他人遇到同样的问题:如上所述,文档明确指出

  

[...]系统执行(32位)挂钩应用程序中的(32位)挂钩   上下文;特别是在调用SetWindowsHookEx的线程上。   这意味着挂钩应用程序必须继续泵送消息   或者它可能会阻止64位进程的正常运行。

我所遇到的是所提到的阻塞行为,应该通过消息泵来克服。在上面的代码示例中,正好缺少这个机制,因为我不知道这应该是一个简单的Windows消息循环(我不知道术语' pump&# 39;之前)。我的初始代码中的最终代码片段必须如下所示:

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;
}

在此示例中,我使用非阻止PeekMessage()而不是GetMessage(),因为我希望我的线程不断检查它的终止标记。

通过这个实现,我的钩子在所有本机64位或WOW64进程中按预期工作,没有应用程序挂起后就会挂起。消息泵是唯一丢失的部分。

在完成所有这些实验之后,我得出以下结论 - 如果我错了,请在评论中纠正我:

当安装系统范围的挂钩时,会尝试将给定的Hook-DLL注入到每个运行的进程中。如果" bitness"如果进程与Hook-DLL中的进程匹配,则挂钩过程作为目标进程中的远程线程执行(就像普通的Injectors工作一样)。如果" bitness"不同的是,Windows将回退绕回到最初调用SetWindowsHookEx()的进程和线程(在我的示例中为代码片段中的线程),并作为不匹配的进程的执行代理" bitness"。出于这个原因,此线程需要不断处理传入的消息,否则将不会处理原始目标进程的事件,而原始目标进程又会开始挂起。

同样,如果这不完全正确,不完整,或者如果还有其他补充说明,请在下面发表评论。谢谢! :)

答案 1 :(得分:0)

我的解决方案是将钩子应用程序(在其中调用了SetWindowsHookEx()和DLL(回调钩子函数所在的位置)都编译为x86和x64版本。 因此,我们有两个EXE(xxx-x86.exe和xxx-x64.exe)和两个DLL(xxx-x86.dll和xxx-x64.dll)。

然后实施复杂的IPC协议,以在x86应用程序和x64应用程序之间同步数据。它看起来很有效,不会阻止“位”不匹配的另一个进程。但是很难处理精确的事件序列,它只能作为事件的粗略指标。

此解决方案非常难看,但是我没有更好的方法...