简体   繁体   English

如何从另一个进程中解开全局钩子?

[英]How do I unhook a global hook from another process?

Here's my current setup: I have a C++ DLL that hooks one of its functions globally to every process running on the computer. 这是我当前的设置:我有一个C ++ DLL,该DLL全局地将其功能之一挂接到计算机上运行的每个进程。 The hooking is done in DLLMain using the SetWindowsHookEx winapi function, and I'm hooking to the WH_CBT and WH_SHELL events. 该挂钩是使用SetWindowsHookEx winapi函数在DLLMain完成的,我已经挂钩到WH_CBTWH_SHELL事件。 I also have a C# application that loads the DLL with p/invoke ( LoadLibrary() ) which triggers the installation of the hooks from DLLMain . 我也有一个C#应用程序,该应用程序通过p / invoke( LoadLibrary() )加载DLL,从而触发从DLLMain安装钩子。 The handlers in the DLL are sending event information to the C# app through a named pipe. DLL中的处理程序通过命名管道将事件信息发送到C#应用程序。

Based on what I've read on the microsoft documentary , these events will be handled on the target process's thread, and have to be installed by a standalone C++ DLL (unlike WH_MOUSE_LL and WH_KEYBOARD_LL that can be installed by any application, even straight from a C# app using p/invoke ). 根据我在microsoft纪录片上所读的内容 ,这些事件将在目标进程的线程上处理,并且必须由独立的C ++ DLL安装(与WH_MOUSE_LL和WH_KEYBOARD_LL可以由任何应用程序安装,甚至可以直接从使用p / invoke的C#应用 )。

So far everything works fine; 到目前为止,一切正常。 the managed app is receiving data as it should. 托管应用正在接收数据。 The problem arises when I shut down the application, because the handlers are still hooked, and therefore the DLL file is in use and can't be deleted. 当我关闭应用程序时,会出现问题,因为处理程序仍处于挂接状态,因此DLL文件正在使用中,无法删除。

Since the handler is not running in my application, but instead it's injected into other processes running on my computer, the C# app can't just simply call UnhookWindowsHookEx or FreeLibrary , because the pointer of the event handler belongs to other processes. 由于该处理程序不在我的应用程序中运行,而是注入了我的计算机上运行的其他进程中,因此C#应用程序不能只是简单地调用UnhookWindowsHookExFreeLibrary ,因为事件处理程序的指针属于其他进程。

The question: 问题:

How can I trigger an unhook routine from the managed application that makes sure that the DLL is not in use anymore by any process? 我该如何从托管应用程序中触发一个解除挂钩例程,以确保该DLL不再被任何进程使用?

Here's what i've tried: 这是我尝试过的:

The only solution that I wan able to come up with is to create an exit event (with CreateEvent ) and every time the handler receives WH_CBT or WH_SHELL message, it checks if the exit event is set, in which case it unhooks itself from the process it belongs to and returns before processing the message. 我想提出的唯一解决方案是创建一个退出事件(使用CreateEvent ),并且每次处理程序收到WH_CBTWH_SHELL消息时,它都会检查是否设置了退出事件,在这种情况下,它会将自己从流程中解脱出来。它属于并在处理消息之前返回。

The problem with this approach is that after I shut down my application and unload the DLL, I have to wait until the remaining processes receive an WH event at least once, so the handler belonging to them can unhook itself. 这种方法的问题在于,在关闭应用程序并卸载DLL之后,我必须等到其余进程至少接收到一次WH事件,以便属于它们的处理程序才能将自身脱钩。

Here's the code of the DLL: 这是DLL的代码:

#include <windows.h>
#include <sstream>

HANDLE hTERM;
HHOOK hCBT;
HHOOK hShell;

void __declspec(dllexport) InstallHooks(HMODULE h);
void __declspec(dllexport) RemoveHooks();

int Continue()
{
    return WAIT_TIMEOUT == WaitForSingleObject(hTERM, 0);
}

LRESULT FAR PASCAL _cbtProc(int c, WPARAM w, LPARAM l)
{
    if (!Continue()) { RemoveHooks(); return 0; }   
    // Handling the message ...
    return CallNextHookEx(0, c, w, l);
}

LRESULT FAR PASCAL _shellProc(int c, WPARAM w, LPARAM l)
{
    if (!Continue()) { RemoveHooks(); return 0; }
    // Handling the message ...
    return CallNextHookEx(0, c, w, l);
}

void InstallHooks(HMODULE h)
{
    hTERM = OpenEvent(EVENT_ALL_ACCESS, 0, __TEXT("{0C3ED513-F38C-4996-8130-F9A3C93D890B}"));
    if (!Continue())
        return;
    hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, h, 0);
    hShell = SetWindowsHookEx(WH_SHELL, _shellProc, h, 0);
}

void RemoveHooks()
{
    UnhookWindowsHookEx(hCBT);
    UnhookWindowsHookEx(hShell);
    if (hTERM) CloseHandle(hTERM); hTERM = 0;
}

int FAR PASCAL DllMain(HMODULE h, DWORD r, void* p)
{
    switch (r)
    {
        case DLL_PROCESS_ATTACH: InstallHooks(h); break;
        case DLL_PROCESS_DETACH: RemoveHooks(); break;
        default: break;
    }
    return 1;
}

There is nothing special about the source code of the managed C# app, because the only thing it does is that it calls LoadLibrary on start, handles the messages coming from the pipe, and finally sets the exit event with an other p/invoke call when needed. 托管C#应用程序的源代码没有什么特别的,因为它唯一要做的就是它在启动时调用LoadLibrary ,处理来自管道的消息,并最终在其他p / invoke调用时设置exit事件。需要。

" The hooking is done in DLLMain " - that is the completely wrong place to handle this. 挂钩在DLLMain中完成 ”-这是完全错误的地方。

Every time the DLL is loaded into a new process, it is going to install a new set of Shell/CBT hooks, which you DO NOT want/need to happen. 每次将DLL加载到新进程中时,它将安装一组新的Shell / CBT钩子,您不希望/不需要发生这些钩子。 You only need 1 set. 您只需要1套。

The correct solution is to have your DLL export its InstallHooks() and RemoveHooks() functions, and then have ONLY your C# app call them AFTER it has loaded the DLL into itself. 正确的解决方案是让您的DLL导出其InstallHooks()RemoveHooks()函数,然后在将DLL加载到自身之后,仅让您的C#应用​​调用它们。 That single set of hooks will handle loading the DLL into all running processes as needed WITHOUT you having to call SetWindowsHookEx() each time. 那套挂钩将根据需要处理将DLL加载到所有正在运行的进程中,而无需每次调用SetWindowsHookEx()

Also, DO NOT call UnhookWindowsHookEx() from inside the hook callbacks themselves. 另外,请勿从挂钩回调本身内部调用UnhookWindowsHookEx() Before the C# app exits, it should call RemoveHooks() , which can then signal the hTerm event before calling UnhookWindowsHookEx() . 在C#应用程序退出之前,它应该调用RemoveHooks() ,然后可以在调用UnhookWindowsHookEx()之前发出hTerm事件信号。 The callbacks should simply exit if Continue() returns false, nothing more. 如果Continue()返回false,则仅应退出回调。 But DO NOT skip calling CallNextHookEx() even if Continue() returns false, as there may be additional hooks installed by other apps, and you DO NOT want to break them. 但是,即使Continue()返回false,也不要跳过调用CallNextHookEx() ,因为其他应用程序可能还会安装其他挂钩,并且您也不想破坏它们。

Try something more like this instead: 尝试类似这样的方法:

#include <windows.h>

HMODULE hModule = NULL;
HANDLE hTERM = NULL;
HHOOK hCBT = NULL;
HHOOK hShell = NULL;

static bool Continue()
{
    return (WAIT_TIMEOUT == WaitForSingleObject(hTERM, 0));
}

LRESULT CALLBACK _cbtProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (Continue()) {
        // Handle the message ...
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}

LRESULT CALLBACK _shellProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (Continue()) {
        // Handle the message ...
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}

__declspec(dllexport) BOOL WINAPI InstallHooks()
{
    if (!Continue())
        return FALSE;

    if (!hCBT)
        hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, hModule, 0);

    if (!hShell)
        hShell = SetWindowsHookEx(WH_SHELL, _shellProc, hModule, 0);

    return ((hCBT) && (hShell)) ? TRUE : FALSE;
}

__declspec(dllexport) void WINAPI RemoveHooks()
{
    if (hTERM)
        SetEvent(hTERM);

    if (hCBT) {
        UnhookWindowsHookEx(hCBT);
        hCBT = NULL;
    }

    if (hShell) {
        UnhookWindowsHookEx(hShell);
        hShell = NULL;
    }
}

BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, void* lpvReserved)
{
    hModule = hinstDLL;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            hTERM = CreateEvent(NULL, TRUE, FALSE, TEXT("{0C3ED513-F38C-4996-8130-F9A3C93D890B}"));
            if (!hTERM) return FALSE;
            break;

        case DLL_PROCESS_DETACH:
            if (hTERM) {
                CloseHandle(hTERM);
                hTERM = NULL;
            }
            break;
    }

    return TRUE;
}

Then, your C# app can simply load the DLL and call InstallHooks() and RemoveHooks() when needed. 然后,您的C#应用​​程序可以简单地加载DLL,并在需要时调用InstallHooks()RemoveHooks() For instance, using PInvoke calls at app startup and shutdown, respectively. 例如,分别在应用启动和关闭时使用PInvoke调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM