首页 > 解决方案 > 在 c++ 的 SetWindowsHookEx 函数中

问题描述

我是韩国学生。现在这是我注册后的第一个问题。

DWORD getProcessId() {
    PROCESSENTRY32 process_infor;
    process_infor.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snap_handle = CreateToolhelp32Snapshot(
        TH32CS_SNAPALL, //스냅 단계
        NULL //스냅할 pid
    );

    if (snap_handle != INVALID_HANDLE_VALUE) {
        Process32First(snap_handle, &process_infor);

        do {
            wchar_t* temp = process_infor.szExeFile;
            wstring ws(temp);
            string name(ws.begin(), ws.end());

            if (name == "notepad.exe") {
                cout << name << " : " << process_infor.th32ProcessID << endl;
                return process_infor.th32ProcessID;
            }

        } while (Process32Next(snap_handle, &process_infor));
    }

    CloseHandle(snap_handle);
    return FALSE;
}

BOOL inject() {
    HMODULE dll_handle;
    HOOKPROC func;
    HHOOK process_hook;

    dll_handle = LoadLibrary(L"hello.dll");
    func = (HOOKPROC) GetProcAddress(dll_handle, "injectSuccess");

    cout << "handle : " << dll_handle << endl;
    cout << "pid : " << getProcessId() << endl;

    process_hook = SetWindowsHookEx(
        WH_KEYBOARD,
        func,
        dll_handle,
        getProcessId()
    );

    cout << "pook : " << process_hook << endl;
    cout << "err : " << GetLastError() << endl;
    FreeLibrary(dll_handle);

    return FALSE;
}

SetWindowsHookEx在这种情况下,注入功能似乎存在问题。dll文件加载好,injectSuccess里面的函数也好抓取。(我试过运行它,但它起作用了)而且我想知道是否输入了错误的参数值SetWindowsHookEx,所以我比较并不断检查它们,但我没有发现任何差异。所以,我尝试GetLastError()SetWindowsHookEx下面的返回值,但返回值为0,错误代码为87(“参数不正确”)。

所以我搜索了,但我英语说得不好,而且我是初学者,所以我不确定。

标签: c++winapi

解决方案


根据SetWindowsHookExW

dwThreadId

包含 lpfn 参数指向的钩子过程的 DLL 句柄。如果 dwThreadId 参数指定由当前进程创建的线程并且挂钩过程在与当前进程关联的代码内,则 hMod 参数必须设置为 NULL。

所以SetWindowsHookExW需要的是线程ID,而你传入notepad.exe的进程ID,所以参数错误。

我创建一个示例并测试以下代码:

BOOL inject() {
HMODULE dll_handle;
HOOKPROC func;
HHOOK process_hook;

dll_handle = LoadLibrary(L"hello.dll");
if (dll_handle) func = (HOOKPROC)GetProcAddress(dll_handle, "injectSuccess");
else return FALSE;
cout << "handle : " << dll_handle << endl;
cout << "pid : " << getProcessId() << endl;
HWND h = FindWindow(L"notepad", NULL);
DWORD pid;
threadID = GetWindowThreadProcessId(h, NULL);
cout << "threadID = " << threadID  << endl;
process_hook = SetWindowsHookEx(
    WH_KEYBOARD,
    func,
    dll_handle,
    threadID
);

cout << "pook : " << process_hook << endl;
cout << "err : " << GetLastError() << endl;
if(dll_handle) FreeLibrary(dll_handle);
return FALSE;
}

这个例子对我有用,你可以看到 pid 和 threadID 之间的区别:

在此处输入图像描述

编辑

根据文件

应用程序通过在调用 SetWindowsHookEx 函数时指定 WH_KEYBOARD 挂钩类型和指向挂钩过程的指针来安装挂钩过程。这个钩子可以在安装它的线程的上下文中被调用。通过向安装钩子的线程发送消息来进行调用。因此,安装钩子的线程必须有一个消息循环。所以如果你想运行这个函数,你需要添加一个消息循环。

您可以参考以下代码:

int main()
{
    inject();
    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
}

当您按下按钮时,将弹出消息框:

在此处输入图像描述

因为每次按键按下和释放都有两条消息,所以func每次触发两次。如果只想在每次按下按钮时触发,可以修改如下代码:

if ((0x80000000 & lParam) == 0)//This means that when the key is pressed
{
    MessageBox(NULL, L"Success (dll injection)", L"Window", MB_OK);
}

您可以参考KeyboardProc来查看 lParam 中每个值的消息。


推荐阅读