使用 UnhookWindowsHookEx() 取消挂钩时,多个程序崩溃
Posted
技术标签:
【中文标题】使用 UnhookWindowsHookEx() 取消挂钩时,多个程序崩溃【英文标题】:Several programs crash when unhooking with UnhookWindowsHookEx() 【发布时间】:2015-11-14 22:07:49 【问题描述】:我正在做一个全局挂钩来将我的 DLL 添加到挂钩链中:
HHOOK handle = SetWindowsHookEx(WH_CALLWNDPROC, addr, dll, 0);
在我的 DLL 中,我使用 Detours 来拦截几个 WINAPI 函数调用。一切正常,除了 WaitForSingleObject 调用。每当我将 WaitForSingleObject 添加到迂回函数时,当我解开我的 DLL(Chrome、Skype、...)时,几个程序会崩溃。 以下是 DLL 的外观:
DWORD (WINAPI* Real_WaitForSingleObject)( HANDLE hHandle, DWORD dwMilliseconds) = WaitForSingleObject;
DWORD WINAPI Mine_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
switch(Reason)
case DLL_PROCESS_ATTACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)Real_WaitForSingleObject, Mine_WaitForSingleObject);
DetourTransactionCommit();
break;
case DLL_PROCESS_DETACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)Real_WaitForSingleObject, Mine_WaitForSingleObject);
DetourTransactionCommit();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
return TRUE;
DWORD WINAPI Mine_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
return Real_WaitForSingleObject(hHandle, dwMilliseconds);
extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(NULL, code, wParam, lParam);
有人可以帮助我了解为什么会发生这种情况以及如何解决这个问题吗?谢谢!
【问题讨论】:
如此令人震惊的黑客行为会导致问题也就不足为奇了。 大概,当您删除挂钩时,DLL 会被卸载。您删除了绕行,但这不会阻止已经在绕行调用中间的线程崩溃。 WaitForSingleObject 特别容易导致崩溃的唯一原因是它往往会长时间阻塞。 (其他迂回函数只会偶尔崩溃,因为大多数时候它们不会在 DLL 卸载的确切点运行。) 有没有办法安全地卸载 DLL 而不会导致迂回调用崩溃? DetourDetach() 不是应该防止此类崩溃吗? 我不明白它是怎么做到的,尤其是在这种情况下。但是你应该查阅文档。 你的所作所为是出人意料的。你不应该期望能够做到这一点。你怎么能期望代码在你卸载后继续执行呢?怎么可能安全卸载代码? 【参考方案1】:我认为这是因为许多程序(Chrome、Skype、...)都有一个线程池,其中后台线程正在等待 WaitForSingleObject() 等待它们发生的有趣事情,以及何时发生,该线程[s] 醒来并做某事。
因此,您的线程 A 正在调用 DetourDetach,而同一进程的另一个线程 B 当前位于 Mine_WaitForSingleObject() 中,然后 DLL 卸载,一切都崩溃了。您可以使用调试器进行验证,附加到有问题的进程,在 DLL_PROCESS_DETACH 中设置断点,当断点命中时,查看其他线程的堆栈以查找 Mine_WaitForSingleObject。
我不确定如何解决。 但是,您可以尝试一种方法 — 枚举线程,并为进程的每个线程调用 DetourUpdateThread()。这样一来,也许 Detours 会做点什么。
【讨论】:
DetourUpdateThread
重写指令指针“在目标函数或蹦床函数中的重写代码内”。换句话说,如果它即将删除蹦床,但线程正在执行蹦床,那么它重定向线程执行原始函数中的相应指令。它不会展开堆栈(无论如何它不会知道如何展开),所以如果Mine_WaitForSingleObject
在堆栈上,那么在你更新线程后它仍然会在里面。【参考方案2】:
您正在绕行一个几乎所有进程都使用的函数。而且它特别很危险,因为这样的进程很可能调用了那个激活的函数。几乎在任何情况下都是阻塞调用。一旦它解除阻塞,代码将恢复到您不再存在的弯路。
卡布姆。
实际上,卸载绕行的唯一方法是注销,这样本可以绕行的每个进程都不再运行。
【讨论】:
以上是关于使用 UnhookWindowsHookEx() 取消挂钩时,多个程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章
Hook函数三步走(SetWindowsHookExUnhookWindowsHookExCallNextHookEx)
Hook函数三步走(SetWindowsHookExUnhookWindowsHookExCallNextHookEx)