【问题标题】:Bizarre hook behavior 32/64 bit奇怪的钩子行为 32/64 位
【发布时间】:2012-08-28 11:16:36
【问题描述】:

我正在使用带有 ms 字 (OpusApp) 的本地挂钩 (WH_KEYBOARD)。好吧,据我所知,带有32bit DLL 的 32 位应用程序只能与32bit target applications 一起使用。奇怪的是该程序仅适用于 64 位应用程序!!!就是这样,只有64bits APPS!例如,it works with IE 64 but not with IE 32! 该应用程序和dll 是用radstudio XE2 编译的32 位,我在PE 标头中确认了版本。 在 32 位操作系统中,应用程序和 dll 不起作用。

我在网上没有找到解决方案,也没有找到解决这个奇怪问题的起点。

DLL 代码:

// Exported functions

extern "C" __declspec(dllexport)bool __stdcall InstallMouseHook(unsigned long, void *);

extern "C" __declspec(dllexport)bool __stdcall RemoveMouseHook();

// Callback Procedure Declaration

LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam);

// Global variables

HHOOK HookHandle;
HINSTANCE DllInstance;
typedef void (__stdcall *CALLIT)(int,WPARAM,LPARAM);
CALLIT callIt = NULL;

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
  DllInstance=hinst;
  return 1;
}

bool __stdcall InstallMouseHook(unsigned long pid, void *function)
{

     callIt = ( CALLIT ) function;

     if (function == NULL) {

        ShowMessage("function is null!");

     } else if (callIt == NULL) {

        ShowMessage("callIt is null!");

     }  

     HookHandle=SetWindowsHookEx(WH_KEYBOARD ,reinterpret_cast<HOOKPROC> (HookProc),DllInstance,pid);

    if (HookHandle==NULL)return false;

    else return true;

}

bool __stdcall  RemoveMouseHook()
{
  if(UnhookWindowsHookEx(HookHandle)==0)
  {
    return false;
  }
  else return true;
}

LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (code<0) {
        return CallNextHookEx(HookHandle,code,wParam,lParam);
    }

    if (callIt != NULL) {
        callIt(code,wParam,lParam);
    } else {
        ShowMessage("HookProc - no function to execute OR 32/64 bits problem!");
    }

  //Call the next hook in the chain
  return CallNextHookEx(HookHandle,code,wParam,lParam);
}

EXE调用代码:

void __fastcall TfrmMouseHook::btnHookAppDllClick(TObject *Sender)
{
    HWND hWindow;
    unsigned long pid;

    String s = "MouseHookDLL.dll";
    DllHandle=LoadLibrary(s.w_str());
    MOUSEHOOKFCT_2 InstHook=reinterpret_cast<MOUSEHOOKFCT_2> (GetProcAddress(DllHandle,"InstallMouseHook"));

    hWindow = FindWindow(ComboBox1->Text.w_str(),NULL);

    if (!hWindow) {
        msg("hWindow fail");
        return;
    }

    pid = GetWindowThreadProcessId(hWindow ,0);
    if (!pid) {
       msg("pid fail");
       return;
    }

    if(!InstHook(pid, (void *) callIt )) {
        msg("Unable to install  hook!");
    } else {
        msg(" #### hook INSTALLED! ####");
    }


}

CALLIT callIt(code,wParam,lParam) {
    frmMouseHook->msg("hook callit: code="+IntToStr(code) +" wparam="+IntToStr(wParam)+" lparam="+IntToStr(lParam) );
}



   Call IT is a function pointer to a hooker app function.

    Any ideas will be very wellcome!

【问题讨论】:

  • 显然操作系统很生气,因为您为键盘挂钩编写了 MouseProc() 并决定反转所有内容。
  • 好吧,我也是个怪人!我会尝试向操作系统道歉,但我认为它不会解决问题。
  • 您需要显示更多代码。您对 SetWindowsHookEx() 的调用在哪里?它是在应用程序内部还是在 DLL 内部?你传递给它什么参数?
  • 您的代码中有一些错误。您需要将HHOOK 句柄存储在共享内存块中。您可以为此使用CreateFileMapping()MapViewOfFile()。但更重要的是,应用程序中的 callIt() 回调没有使用 __stdcall 调用约定,但 DLL 期望它使用。如果您的代码是类型安全的,您就会发现该错误。 InstallMouseHook()function参数需要声明为CALLIT而不是void*
  • 另一个问题是您正在安装一个回调函数指针,该指针属于一个进程,但可能由被挂钩的进程执行。更好的解决方案是使用HWND 代替函数指针,并让钩子向窗口发送消息,而不是直接调用函数指针。这样会更安全。

标签: dll hook c++builder 32bit-64bit setwindowshookex


【解决方案1】:

32 位应用程序安装 32 位挂钩 DLL 并让它在 64 位进程中执行在物理上是不可能的。 32 位 DLL 根本无法注入 64 位进程。时期。 MSDN 在多个地方都提到了这一点,包括SetWindowsHookEx() documentation

SetWindowsHookEx 可用于将 DLL 注入另一个进程。一种 32位DLL不能注入64位进程,64位DLL 不能注入到 32 位进程中。如果应用程序需要 在其他进程中使用钩子,需要一个 32 位的 应用程序调用 SetWindowsHookEx 将 32 位 DLL 注入 32 位 进程,以及一个 64 位应用程序调用 SetWindowsHookEx 来注入一个 将 64 位 DLL 转换为 64 位进程。 32 位和 64 位 DLL 必须具有 不同的名字。

因为钩子在应用程序的上下文中运行,它们必须匹配 应用程序的“位数”。如果 32 位应用程序安装了 64 位 Windows 上的全局钩子,32 位钩子被注入到每个 32 位进程(通常的安全边界适用)。在 64 位 进程中,线程仍被标记为“已挂钩”。然而,因为一个 32位应用程序必须运行钩子代码,系统执行 在钩子应用程序的上下文中钩子;具体来说,在线程上 称为 SetWindowsHookEx。这意味着挂钩应用程序必须 继续泵送​​消息,否则可能会阻止正常运行 64 位进程。

如果 64 位应用程序在 64 位 Windows 上安装全局挂钩,则 64 位钩子被注入到每个 64 位进程中,而所有 32 位 进程使用对挂钩应用程序的回调。

您说您的应用程序和 DLL 不能在 32 位操作系统版本上运行这一事实表明您的挂钩代码一开始就有缺陷。但是您没有显示足够的代码来诊断这种或另一种方式。

【讨论】:

  • 我能说什么,上面的代码(使用 rad studio 编译为 32 位)仅适用于 IE64,不适用于 IE32(与其他应用程序相同)。函数 SetWindowsHookEx 在 DLL 中。
  • IE64 绝对不可能执行您的 32 位挂钩 DLL。这在物理上是不可能的。只有 32 位进程可以调用 32 位挂钩。期间。
  • 感谢雷米的回答。 callIt 在 typedef void (__stdcall *CALLIT)(int,WPARAM,LPARAM) 中使用 __stdcall;我知道 32 位进程不可能使用 32 位 dll 挂钩 64 位应用程序。但是,除非 radstudio 使用 64 位构建(在我的版本中,只有为 32 位 Windows 构建的选项)或 Internet Explorer(64 位)快捷方式指向 32 位版本,否则这种奇怪的事情正在发生。看桌面图片link其实在另一个进程中调用函数指针是很奇怪的……不过这还是第一次尝试……
  • 在EXE代码中,你将callIt()回调声明为CALLIT callIt(code,wParam,lParam),这是完全错误的。它需要被声明为void __stdcall callIt(int code, WPARAM wParam, LPARAM lParam)。同样,如果您一开始就编写类型安全的代码,您的编译器就会发现这一点。
  • C++Builder 还不支持开发 64 位可执行文件。我向你保证 64 位 IE 不会调用你的钩子。它不能。它是一个 64 位进程,一个 64 位进程不能调用 32 位钩子。确保您实际上是在“挂钩”IE 64。例如,当我在 64 位 Win7 机器上运行 IE 时,它实际上运行的是 32 位版本的 IE。
【解决方案2】:

会发生什么?除了MSDN或者其他人说的,XE6有一些bug,在较新版本的IDE中编译DLL使这种行为消失了,实际上新的DLL崩溃了,hook什么都没有。

正如 Remy 所说,通过测试,我向 DLL 传递了一个函数指针,这是一件错误的事情,但是当加上 Embarcadero 所做的错误事情时,它就可以正常工作了。

到现在为止,我知道人们会生气,我将这两种方法(错误和正确的钩子)放在同一个 DLL 和我的应用程序中,然后......变得疯狂......可以挂钩到 32 位和 64 位只有一个 DLL 的应用程序。

不相信?安装 XE6 并尝试!

在 Windows 10 中也可以在 Windows 7 中使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-20
    • 2019-04-01
    • 2021-04-14
    • 2014-11-17
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多