SetWindowsHookEx 将 32 位 DLL 注入 64 位进程,反之亦然
SetWindowsHookEx is injecting 32-bit DLL into 64-bit process and vice versa
我一直在开发一个需要监控的应用程序 线程特定 鼠标 activity (WH_MOUSE
) 在另一个进程中,遇到了一些非常奇怪的事情。
在发现 this is not possible via exclusively managed code 如果我不想使用 WH_MOUSE_LL
并且我需要一个本机 DLL 导出以将其自身注入目标进程后,我开始并创建了根据我能找到的有关该主题的分散文档,它是用 C++ 编写的,然后尝试使用它连接到记事本中。
虽然根据 GetLastWin32Error
the injection succeeded, I was not getting notified of mouse events. After nearly giving up and going for the low level global hook option, I re-read the "Remarks" section of this article 这让我怀疑问题可能是因为我的代码与记事本的 "bitness":
A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit
DLL cannot be injected into a 32-bit process. If an application
requires the use of hooks in other processes, it is required that a
32-bit application call SetWindowsHookEx to inject a 32-bit DLL into
32-bit processes, and a 64-bit application call SetWindowsHookEx to
inject a 64-bit DLL into 64-bit processes.
然而,我的本机 DLL 和托管应用程序都被编译为 x64,并且我试图连接到 64 位版本的记事本,所以它应该可以正常工作,但我在黑暗中试了一下无论如何,进入 SysWOW64
文件夹并从那里打开 32 位记事本,再次尝试挂钩,这次挂钩效果很好!
奇怪的是,我随后将我的本机 DLL 和托管应用程序重新编译为 x86,并针对 32 位记事本对其进行了测试,但它没有工作,但它在我的普通 64 位记事本上工作!
我怎么可能将 32 位 DLL 注入 64 位进程,反之亦然!
虽然我原来的问题已经解决,我可以继续我的应用程序的开发,但好奇 为什么 我从 SetWindowsHookEx
观察到这种奇怪的逆行为快把我逼疯了,所以我真的希望有人能够对此有所启发。
我知道这有很多讨论,但没有代码,但即使是示例应用程序的代码也相当大,并且有托管和非托管两种风格,但我会立即 post您认为可能相关的代码。
我还创建了一个示例应用程序,因此您可以自己测试此行为。这是一个简单的 WinForms 应用程序,它尝试连接到记事本并显示其鼠标事件:
http://saebamini.com/HookTest.zip
它同时包含一个x86 版本和一个x64 版本。在我的机器上(我使用的是 64 位 Windows7),x86 版本仅适用于 64 位记事本,而 x64 版本仅适用于 32 位记事本(来自 SysWOW64)。
更新 - 相关代码位:
对非托管库的 C# 调用:
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 实例中完成的,但 UserMouseHookCallback
在目标 DLL 实例中没有发生变化。注入后,DLL 处于不同的进程中,因此应被视为这样。你必须找到另一种方法来回调应用程序(可能 post 一条消息,就像在 32 到 64 的情况下,and/or 使用共享部分)。
要对此进行测试,请在 InternalMouseHookCallback()
中放入类似 MessageBox()
的内容。即使在 64 到 64 和 32 到 32 中,该框也应出现。
我一直在开发一个需要监控的应用程序 线程特定 鼠标 activity (WH_MOUSE
) 在另一个进程中,遇到了一些非常奇怪的事情。
在发现 this is not possible via exclusively managed code 如果我不想使用 WH_MOUSE_LL
并且我需要一个本机 DLL 导出以将其自身注入目标进程后,我开始并创建了根据我能找到的有关该主题的分散文档,它是用 C++ 编写的,然后尝试使用它连接到记事本中。
虽然根据 GetLastWin32Error
the injection succeeded, I was not getting notified of mouse events. After nearly giving up and going for the low level global hook option, I re-read the "Remarks" section of this article 这让我怀疑问题可能是因为我的代码与记事本的 "bitness":
A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes.
然而,我的本机 DLL 和托管应用程序都被编译为 x64,并且我试图连接到 64 位版本的记事本,所以它应该可以正常工作,但我在黑暗中试了一下无论如何,进入 SysWOW64
文件夹并从那里打开 32 位记事本,再次尝试挂钩,这次挂钩效果很好!
奇怪的是,我随后将我的本机 DLL 和托管应用程序重新编译为 x86,并针对 32 位记事本对其进行了测试,但它没有工作,但它在我的普通 64 位记事本上工作!
我怎么可能将 32 位 DLL 注入 64 位进程,反之亦然!
虽然我原来的问题已经解决,我可以继续我的应用程序的开发,但好奇 为什么 我从 SetWindowsHookEx
观察到这种奇怪的逆行为快把我逼疯了,所以我真的希望有人能够对此有所启发。
我知道这有很多讨论,但没有代码,但即使是示例应用程序的代码也相当大,并且有托管和非托管两种风格,但我会立即 post您认为可能相关的代码。
我还创建了一个示例应用程序,因此您可以自己测试此行为。这是一个简单的 WinForms 应用程序,它尝试连接到记事本并显示其鼠标事件:
http://saebamini.com/HookTest.zip
它同时包含一个x86 版本和一个x64 版本。在我的机器上(我使用的是 64 位 Windows7),x86 版本仅适用于 64 位记事本,而 x64 版本仅适用于 32 位记事本(来自 SysWOW64)。
更新 - 相关代码位:
对非托管库的 C# 调用:
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 实例中完成的,但 UserMouseHookCallback
在目标 DLL 实例中没有发生变化。注入后,DLL 处于不同的进程中,因此应被视为这样。你必须找到另一种方法来回调应用程序(可能 post 一条消息,就像在 32 到 64 的情况下,and/or 使用共享部分)。
要对此进行测试,请在 InternalMouseHookCallback()
中放入类似 MessageBox()
的内容。即使在 64 到 64 和 32 到 32 中,该框也应出现。