【问题标题】:In c++'s SetWindowsHookEx function在 c++ 的 SetWindowsHookEx 函数中
【发布时间】:2020-10-23 01:49:39
【问题描述】:

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

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的参数值输入有误,于是对比,不断的检查,也没有发现有什么不同。于是,我用下面SetWindowsHookEx的返回值尝试了GetLastError(),但返回值为0,错误码为87(“参数不正确”)。

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

【问题讨论】:

  • 欢迎来到 Stack Overflow;我建议检查GetProcAddress 的结果以确保您确实找到了该函数:如果它为NULL 怎么办?此外,建议您在调用SetWindowHookEx 之后立即 捕获最后一个错误号,因为后续调用(可能执行I/O)可能会覆盖最后一个错误并混淆报告。在设置窗口挂钩后立即执行const DWORD err = GetLastError() 会捕获这一点。
  • 您为getProcessId 发布的代码没有使用进程名称(“notepad.exe”),但我认为这是粘贴示例时的拼写错误。我建议您检查您是否实际上找到了一个有效的线程 ID 并检查它:只有在收到非零响应时才调用 SetWindowsHookEx
  • 哦,我认为“notepad.exe”是 getProcessId 中的拼写错误。用真实的代码写得很好。
  • 请编辑您的示例代码以便编译;很明显,getProcessId 期望得到一个字符串参数 (process_name)。然后我们可以更仔细地观察它。
  • 对不起;;它被改变了。 GetProcAddress 函数似乎工作正常。当我像这样手动运行thread_func()函数时,消息框正常出现在窗口中。

标签: c++ winapi


【解决方案1】:

根据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 之间的区别:

编辑

根据document

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

可以参考以下代码:

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中每个值的消息。

【讨论】:

  • 呃!非常感谢你。这工作正常,现在我没有收到任何错误。但是它运行良好并且没有错误,但是dll没有运行。为什么呢?使用名为 Process Explorer 的应用程序进行搜索时,该 dll 不在记事本中。
  • 原因可能是Process Explorer无法检测到链接的dll。您可以参考Checking if an API is monitored (hooked?)。如果这个原始答案对您有帮助,请随时标记它以帮助遇到相同问题的人,如果您有任何问题,请告诉我。谢谢。
  • 所以... Process Explorer 检测不到 dll 文件?没有其他问题,没有错误,似乎工作正常,但结果注入成功似乎没有运行。
  • 所以.... 将dll插入记事本后,无法运行?只要通过 loadlibrary 函数将 dll 加载到 main 中,上述修改就会运行。
  • 非常感谢。多亏了你,几乎一切似乎都成功了^^。但是,在上面的视频和我的代码中,我遇到了一个问题,即窗口出现了两次,并且没有执行 injectSuccess 函数的其他操作或输出。我该如何解决这个问题?完成后,我接受您的帖子并关闭问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多