SetWindowsHookEx正在将32位DLL注入64位进程,反之亦然
SetWindowsHookEx is injecting 32-bit DLL into 64-bit process and vice versa
我一直在开发一个应用程序,该应用程序需要监视另一个进程上的线程特定的鼠标活动(WH_MOUSE
),但遇到了一些非常奇怪的事情。
在发现如果我不想使用WH_MOUSE_LL
,并且我需要一个本地DLL导出来将自己注入到目标进程中,那么通过独占托管代码是不可能做到这一点之后,我根据我能找到的关于该主题的零散文档,在C++中设置并创建了它,然后尝试使用它连接到记事本中。
尽管根据GetLastWin32Error
,注射成功了,但我没有收到鼠标事件的通知。在几乎放弃并选择低级别全局挂钩选项后,我重读了这篇文章的"备注"部分,这让我怀疑问题可能是因为我的代码与记事本的"比特性":
32位DLL不能注入64位进程,而64位DLL无法注入32位进程。如果应用程序需要在其他进程中使用钩子32位应用程序调用SetWindowsHookEx将32位DLL注入32位进程,以及一个64位应用程序调用SetWindowsHookEx将64位DLL注入64位进程。
然而,我的原生DLL和托管应用程序都被编译为x64,我正试图挂接64位版本的记事本,所以它应该工作得很好,但我还是在黑暗中尝试了一下,进入SysWOW64
文件夹,从那里打开了32位记事本,再次尝试挂接,这次挂接效果很好!
奇怪的是,我随后将我的原生DLL和托管应用程序重新编译为x86,并在32位记事本上进行了测试,但它没有工作,但在我的普通64位记事本上工作!
我怎么可能将32位DLL注入64位进程,反之亦然!
尽管我最初的问题已经解决,我可以继续我的应用程序开发,但我对为什么从SetWindowsHookEx
中观察到这种奇怪的反向行为的好奇让我抓狂,所以我真的希望有人能对此有所了解。
我知道这件事很多,但没有代码,但即使是一个示例应用程序的代码也相当大,有托管和非托管两种风格,但我会立即发布您认为可能相关的任何代码。
我还创建了一个示例应用程序,以便您可以自己测试这种行为。这是一个简单的WinForms应用程序,它试图挂接到记事本并显示鼠标事件:
http://saebamini.com/HookTest.zip
它同时包含x86版本和x64版本。在我的机器上(我使用的是64位Windows 7),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,但如果不能,它会设置一个使用挂钩应用程序消息循环的回调。由于消息循环由操作系统处理,因此它用于从不同的位进行调用。
在您的情况下,唯一有效的方法是消息循环方式。这有一个很好的理由:64到64和32到32的调用没有成功的机会,因为钩子在注入的DLL中,也就是说,在与应用程序不同的进程中。
在您的情况下不会发生任何事情,因为您的UserMouseHookCallback
保持为NULL。实际上,对SetUserHookCallback()
的调用是在应用程序DLL实例中完成的,但UserMouseHookCallback
在目标DLL实例中没有变化。一旦被注入,DLL就处于不同的过程中,应该被视为这样。您必须找到另一种方法来调用应用程序(可能会像32-to-64的情况一样发布消息,和/或使用共享部分)。
要对此进行测试,请在InternalMouseHookCallback()
中放入类似MessageBox()
的内容。即使是64比64和32比32,这个框也应该出现。
- 64 位进程中的 AnyCPU C# DLL 无法引用 64 位C++ DLL(给出错误:无法加载文件或程序集)
- sizeof() 在 32 位和 64 位进程之间的行为不同
- boost::OSX 上 32 位和 64 位程序之间共享内存中的进程间同步机制(互斥体、条件)
- 如何测量 64 位程序中 32 位进程的内存使用情况
- 如何使用在窗口中使用C++从CreateToolhelp32Snapshot获取64/32位进程详细信息和进程信息
- 如何在C++中以编程方式检索 64 位进程的详细信息
- 从 32 位应用程序挂接 64 位进程
- 创建进程失败,错误代码'740' Windows7 64 位
- 如何从wow64进程中检索特定内核对象的64位地址
- 我应该使用(未记录的?)MINIDUMP_EXCEPTION_INFORMATION64结构来创建64位进程的转储吗
- 64位进程上的ReadProcessMemory总是返回错误299
- 将 32 位 dll 注入 64 位进程 - Autoit 使之成为可能
- 如何在 64 位进程中拦截 API 方法调用
- boost::32位和64位进程之间的进程间共享内存
- SetWindowsHookEx正在将32位DLL注入64位进程,反之亦然
- 32位- 64位进程间通信
- 从32位进程获取64位进程内存的入口点
- 奇怪的 malloc 行为不允许在 64 位进程上分配超过 2GB 的内存
- 如何从64位进程或64位dll访问32位dll
- 使用Module32First/Next从64位进程中枚举32位进程模块