簡體   English   中英

如何從另一個進程中解開全局鈎子?

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

這是我當前的設置:我有一個C ++ DLL,該DLL全局地將其功能之一掛接到計算機上運行的每個進程。 該掛鈎是使用SetWindowsHookEx winapi函數在DLLMain完成的,我已經掛鈎到WH_CBTWH_SHELL事件。 我也有一個C#應用程序,該應用程序通過p / invoke( LoadLibrary() )加載DLL,從而觸發從DLLMain安裝鈎子。 DLL中的處理程序通過命名管道將事件信息發送到C#應用程序。

根據我在microsoft紀錄片上所讀的內容 ,這些事件將在目標進程的線程上處理,並且必須由獨立的C ++ DLL安裝(與WH_MOUSE_LL和WH_KEYBOARD_LL可以由任何應用程序安裝,甚至可以直接從使用p / invoke的C#應用 )。

到目前為止,一切正常。 托管應用正在接收數據。 當我關閉應用程序時,會出現問題,因為處理程序仍處於掛接狀態,因此DLL文件正在使用中,無法刪除。

由於該處理程序不在我的應用程序中運行,而是注入了我的計算機上運行的其他進程中,因此C#應用程序不能只是簡單地調用UnhookWindowsHookExFreeLibrary ,因為事件處理程序的指針屬於其他進程。

問題:

我該如何從托管應用程序中觸發一個解除掛鈎例程,以確保該DLL不再被任何進程使用?

這是我嘗試過的:

我想提出的唯一解決方案是創建一個退出事件(使用CreateEvent ),並且每次處理程序收到WH_CBTWH_SHELL消息時,它都會檢查是否設置了退出事件,在這種情況下,它會將自己從流程中解脫出來。它屬於並在處理消息之前返回。

這種方法的問題在於,在關閉應用程序並卸載DLL之后,我必須等到其余進程至少接收到一次WH事件,以便屬於它們的處理程序才能將自身脫鈎。

這是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;
}

托管C#應用程序的源代碼沒有什么特別的,因為它唯一要做的就是它在啟動時調用LoadLibrary ,處理來自管道的消息,並最終在其他p / invoke調用時設置exit事件。需要。

掛鈎在DLLMain中完成 ”-這是完全錯誤的地方。

每次將DLL加載到新進程中時,它將安裝一組新的Shell / CBT鈎子,您不希望/不需要發生這些鈎子。 您只需要1套。

正確的解決方案是讓您的DLL導出其InstallHooks()RemoveHooks()函數,然后在將DLL加載到自身之后,僅讓您的C#應用​​調用它們。 那套掛鈎將根據需要處理將DLL加載到所有正在運行的進程中,而無需每次調用SetWindowsHookEx()

另外,請勿從掛鈎回調本身內部調用UnhookWindowsHookEx() 在C#應用程序退出之前,它應該調用RemoveHooks() ,然后可以在調用UnhookWindowsHookEx()之前發出hTerm事件信號。 如果Continue()返回false,則僅應退出回調。 但是,即使Continue()返回false,也不要跳過調用CallNextHookEx() ,因為其他應用程序可能還會安裝其他掛鈎,並且您也不想破壞它們。

嘗試類似這樣的方法:

#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;
}

然后,您的C#應用​​程序可以簡單地加載DLL,並在需要時調用InstallHooks()RemoveHooks() 例如,分別在應用啟動和關閉時使用PInvoke調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM