簡體   English   中英

本地鍵盤掛鈎終止目標進程

[英]Local keyboard hook terminates the target process

我正在嘗試使用來自托管 C# 代碼的 C++ DLL 將本地鍵盤掛鈎安裝到進程中,如下所示:

public class KeyboardHook
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("DLL.dll", CallingConvention = CallingConvention.Cdecl)]
    protected static extern IntPtr Install(int idHook, IntPtr windowHandle, HookCallback callback);

    private IntPtr instance;
    private HookCallback handler;

    public KeyboardHook()
    {
        instance = IntPtr.Zero;
        handler = Callback;
    }

    public void Install(Process process)
    {
        instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);
    }

    public void Uninstall()
    {
        UnhookWindowsHookEx(instance);
    }

    private IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        // TODO Use hook data here

        return CallNextHookEx(instance, nCode, wParam, lParam);
    }
}

C++ DLL 代碼應該足以將掛鈎數據分派到 C# 的Callback function,如下所示:

// dll.h
#pragma data_seg(".foo")
HOOKPROC _hookCallback = NULL;
#pragma comment(linker, "/SECTION:.foo,RWS")
#pragma data_seg()

static HINSTANCE _moduleHandle = NULL;

extern "C" __declspec(dllexport)
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback);

extern "C" __declspec(dllexport)
LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam);

// dll.cpp
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
    auto processId = 0ul;
    auto threadId = GetWindowThreadProcessId(window, &processId);

    _hookCallback = hookCallback;
    _hookCallback(-1, NULL, NULL); // Test callback (works)

    return SetWindowsHookExA(idHook, HookProc, _moduleHandle, threadId);
}

LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
    // The following line terminates the target process
    return _hookCallback(code, wParam, lParam);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            _moduleHandle = hModule;
            break;
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
    }

    return TRUE;
}

The local hook is successfuly installed as the DLL KeyboardProc function is triggered, however, calling the C# delegate from the C++ DLL terminates the application. 為什么?

筆記:

  1. DLL 和應用程序都是 32 位
  2. _hookCallback_在觸發 HookProc 時不是HookProc (雖然我不確定它是否指向有效的 memory 地址)
  3. KeyboardProc::handler不應被垃圾回收,因為 KeyboardProc 實例的存在時間與 C# 應用程序一樣長
  4. 在 DLL 的Install function 中使用_hookCallback function 指針可以完美地工作,但在HookProc ZC1C425268E68385D1AB5074C17A9 中使用時會終止進程。
  5. 沒有例外或任何情況,該過程只是突然終止

還嘗試過什么:

使HookCallback成為UnmanagedFunctionPointer ,以及使用Marshal.GetFunctionPointerForDelegate並使用GCHandle.Alloc()GC.KeepAlive()告訴垃圾收集器不要收集handler屬性:

public class KeyboardHook
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("DLL32.dll", CallingConvention = CallingConvention.Cdecl)]
    protected static extern IntPtr Install(int idHook, IntPtr windowHandle, IntPtr delegatePointer);

    // ...
    
    protected readonly GCHandle garbageCollectorHandle;
    
    public KeyboardHook()
    {
        instance = IntPtr.Zero;
        handler = new HookCallback(Callback);
        garbageCollectorHandle = GCHandle.Alloc(handler); // Or GC.KeepAlive(handler)
    }
    
    ~KeyboardHook()
    {
        garbageCollectorHandle.Free();
    }
    
    public void Install(Process process)
    {
        IntPtr delegatePointer = Marshal.GetFunctionPointerForDelegate(handler);
        
        instance = Install(WH_KEYBOARD, process.MainWindowHandle, delegatePointer);
    }
    
    // ...
}

直接在SetWindowsHookExA (C++) 中使用handler

HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
    auto processId = 0ul;
    auto threadId = GetWindowThreadProcessId(window, &processId);

    _hookCallback = hookCallback;

    return SetWindowsHookExA(idHook, hookCallback, _moduleHandle, threadId);
}

C# 代碼調用 DLL 的Install() function 並翻轉了第二個和第三個參數。

更改此行:

instance = Install(WH_KEYBOARD, handler, process.MainWindowHandle);

對此:

instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);

更新:此外,您沒有顯示 C# 代碼對 DLL 的Install() function 的聲明。 但是在 C++ 代碼中, Install() function 沒有指定調用約定,因此它(可能,取決於編譯器配置)默認為__cdeclHookProc()使用的是__stdcall調用約定)。 因此,請確保 C# 代碼在導入Install() function 時指定了正確的調用約定。 C# delegate默認使用__stdcall

該進程因訪問無效的 memory 地址而終止。

每個 Windows 進程都有不同的虛擬 memory區域。 In other words, the 0x1234 memory address within the process A does not point to the same value/function as 0x1234 memory address within the process B , since 0x1234 is a virtual memory address bound to it's corresponding process.

為了實現 C++ DLL 和 C# 應用程序(任何不同的進程)之間的通信,需要進程間通信(IPC)。

對於那些對這個特定案例感興趣的人,我最終創建了一個不可見的虛擬 window作為通過來自 DLL 的SendMessage調用接收消息的中心點。

暫無
暫無

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

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