SetWindowsHookEx将32位DLL注入到64位进程中,反之亦然

我一直在处理一个需要在另一个进程上监视线程特定的鼠标活动( WH_MOUSE )的应用程序,并遇到一些非常好奇的事情。

如果我不想使用WH_MOUSE_LL ,而且我需要一个本机DLL导出来将其注入到目标进程中,我发现通过独占托pipe代码是不可能的 ,然后根据什么在C ++中设置并创build它我可以find关于这个主题的分散的文档,然后尝试使用它来挂钩到记事本。

虽然根据GetLastWin32Error注入成功,我没有得到鼠标事件的通知。 几乎放弃了去低层次的全局钩子选项之后,我重新阅读了这篇文章的“备注”部分, 这让我怀疑这个问题可能是由于我的代码与记事本的“小小”

一个32位的DLL不能被注入一个64位的进程,并且一个64位的DLL不能被注入一个32位的进程。 如果应用程序需要在其他进程中使用钩子,则需要32位应用程序调用SetWindowsHookEx将32位DLL注入到32位进程中,而64位应用程序调用SetWindowsHookEx来注入64位DLL转换成64位进程。

但是,我的原生DLL和托pipe应用程序都编译为64位,我试图挂钩到64位版本的记事本,所以它应该工作得很好,但我在黑暗中reflection,并进入SysWOW64文件夹,并从那里打开32位的记事本,尝试再次挂钩,这一次的钩子工作得很漂亮!

奇怪的是,我然后重新编译我的本机DLL和托pipe的应用程序为x86,并testing它对32位记事本,它没有工作,但它在我正常的64位记事本上工作!

我怎么可能似乎能够注入一个32位DLL到64位进程,反之亦然!

虽然我原来的问题已经解决了,我可以继续我的应用程序的开发,但是为什么我从SetWindowsHookEx观察到这种奇怪的反向行为的好奇心让我觉得很疯狂,所以我真的希望有人能够对此有所了解。

我知道这是很多的谈话,没有代码,但即使是一个示例应用程序的代码是相当大的,并提供了托pipe和非托pipe口味,但是我会及时张贴任何您认为可能相关的代码。

我还创build了一个示例应用程序,以便您可以自己testing此行为。 这是一个简单的WinForms应用程序,它尝试挂入记事本并显示其鼠标事件:

http://saebamini.com/HookTest.zip

它包含一个x86版本和一个x64版本。 在我的机器上(我在64位Windows 7上),x86版本只适用于64位记事本,而x64版本只适用于32位记事本(来自SysWOW64)。

更新 – 相关的代码位:

C#调用非托pipe库:

 public SetCallback(HookTypes type) { _type = type; _processHandler = new HookProcessedHandler(InternalHookCallback); SetCallBackResults result = SetUserHookCallback(_processHandler, _type); if (result != SetCallBackResults.Success) { this.Dispose(); GenerateCallBackException(type, result); } } public void InstallHook() { Process[] bsProcesses = Process.GetProcessesByName("notepad"); if(bsProcesses.Length == 0) { throw new ArgumentException("No open Notepad instance found."); } ProcessThread tmp = GetUIThread(bsProcesses[0]); if (!InitializeHook(_type, tmp.Id)) { throw new ManagedHooksException("Hook initialization failed."); } _isHooked = true; } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr procid); // 64-bit version [DllImport("SystemHookCore64.dll", EntryPoint = "InitializeHook", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern bool InitializeHook(HookTypes hookType, int threadID); [DllImport("SystemHookCore64.dll", EntryPoint = "SetUserHookCallback", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern SetCallBackResults SetUserHookCallback(HookProcessedHandler hookCallback, HookTypes hookType); 

C ++:

 HookProc UserMouseHookCallback = NULL; HHOOK hookMouse = NULL; HINSTANCE g_appInstance = NULL; MessageFilter mouseFilter; bool InitializeHook(UINT hookID, int threadID) { if (g_appInstance == NULL) { return false; } if (hookID == WH_MOUSE) { if (UserMouseHookCallback == NULL) { return false; } hookMouse = SetWindowsHookEx(hookID, (HOOKPROC)InternalMouseHookCallback, g_appInstance, threadID); return hookMouse != NULL; } } int SetUserHookCallback(HookProc userProc, UINT hookID) { if (userProc == NULL) { return HookCoreErrors::SetCallBack::ARGUMENT_ERROR; } if (hookID == WH_MOUSE) { if (UserMouseHookCallback != NULL) { return HookCoreErrors::SetCallBack::ALREADY_SET; } UserMouseHookCallback = userProc; mouseFilter.Clear(); return HookCoreErrors::SetCallBack::SUCCESS; } return HookCoreErrors::SetCallBack::NOT_IMPLEMENTED; } int FilterMessage(UINT hookID, int message) { if (hookID == WH_MOUSE) { if(mouseFilter.AddMessage(message)) { return HookCoreErrors::FilterMessage::SUCCESS; } else { return HookCoreErrors::FilterMessage::FAILED; } } return HookCoreErrors::FilterMessage::NOT_IMPLEMENTED; } static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam) { if (code < 0) { return CallNextHookEx(hookMouse, code, wparam, lparam); } if (UserMouseHookCallback != NULL && !mouseFilter.IsFiltered((int)wparam)) { UserMouseHookCallback(code, wparam, lparam); } return CallNextHookEx(hookMouse, code, wparam, lparam); } 

我对你的问题最好的猜测:

Windows钩子系统能够从任何位置钩住32位和64位应用程序。 正如你所指出的那样,你不能将一个DLL注入到一个错误的应用程序中。 为了使这个工作,Windows将正常注入DLL,如果它可以,但如果它不能,它将设置一个回调,使用挂钩应用程序消息循环。 由于消息循环是由OS处理的,因此用于从不同的位进行呼叫。

在你的情况下,唯一的工作是消息循环的方式。 有一个很好的理由:你的64到64和32到32的调用没有机会成功,因为挂钩在注入的DLL中,也就是说,在一个不同于你的应用程序的过程中。

在你的情况下没有任何反应,因为你的UserMouseHookCallback保持为NULL。 事实上,调用SetUserHookCallback()是在应用程序DLL实例中完成的,但是在目标DLL实例中UserMouseHookCallback没有改变。 一旦被注入,DLL就处于不同的进程中,应该被视为这样。 你必须找到另一种方式来回调应用程序(也许发布一条消息,就像在32到64的情况下,和/或使用共享部分)。

为了测试这个,把MessageBox()放在InternalMouseHookCallback() 。 该框甚至应该出现在64到64和32到32之间。