日记记录挂钩的挂钩过程永远不会被调用

时间:2019-09-20 20:28:47

标签: c++ windows winapi hook setwindowshookex

我正在尝试创建一个简单的应用程序来记录和回放一系列键盘和鼠标命令(宏)。阅读文档并得出结论,最合适的实现(如果不是唯一的实现)将是设置Windows日记记录挂钩(WH_JOURNALRECORD)并以日记回放一个(WH_JOURNAL_PLAYBACK)进行回放。

根据文档,这些挂钩不需要驻留在DLL中,而是可以位于可执行文件(应用程序)中。因此,我让Visual Studio为我创建了一个简单的Win32应用程序。这是一个非常经典的应用程序,它注册一个窗口类,创建窗口并运行消息循环。该文档还提到WH_JOURNALRECORD / WH_JOURNAL_PLAYBACK挂钩的挂钩过程在设置它们的线程上下文中运行。但是,它没有具体提到此线程应该做什么,例如运行消息循环,以可警报状态休眠或做什么。因此,我只是设置了钩子并运行了消息循环-它是应用程序的主线程,也是唯一的线程。这是我发现的一些代码示例,尽管它们似乎已经很旧了,尽管它们似乎现在还无法运行,并且一些Windows安全更新使事情变得更加困难。

我相信我已经采取了一些示例和帖子中发现的所有必要步骤:

  • 将清单选项“ UAC执行级别”设置为“ requireAdministrator(/ level ='requireAdministrator')”和“ UAC绕过UI保护” >”改为“ 是(/ uiAccess ='true')”。
  • 创建并安装了证书-构建后将使用该应用程序对其进行签名。
  • 将可执行文件复制到System32(受信任的文件夹),然后从那里“作为管理员”运行。 如果没有上述操作,则挂钩的安装将失败,错误代码为5(访问被拒绝)。

我设法成功(?)安装了WH_JOURNALRECORD钩子(SetWindowsHookEx()返回了非零句柄),但是未调用该钩子过程。

下面是我的代码(我省略了窗口类注册,窗口创建,窗口过程和“关于”对话框的内容,因为其中没有什么有趣或特别的地方,它们只是准系统):

// Not sure if these are needed, found it in some code samples
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
HHOOK hhJournal = NULL;
#pragma data_seg()

// Not sure if the Journal proc needs to be exported
__declspec(dllexport) LRESULT CALLBACK _JournalRProc(_In_ int code, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
    Beep(1000, 30); // Clumsy way to trace the JournalRProc calls

    return CallNextHookEx(NULL, code, wParam, lParam);
}

void AddKMHooks(HMODULE _hMod)
{
    if (hhJournal) return;
    MessageBox(NULL, "Adding Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
    hhJournal = SetWindowsHookEx(WH_JOURNALRECORD, _JournalRProc, _hMod, 0);
    if (!hhJournal)
    {
        CHAR s[100];
        wsprintf(s, "Record Journal Hook Failed!\nThe Error-Code was %d", GetLastError());
        MessageBox(NULL, s, szTitle, MB_OK | MB_ICONSTOP | MB_TASKMODAL);
    }
}

void RemoveKMHooks()
{
    if (!hhJournal) return;
    MessageBox(NULL, "Removing Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
    UnhookWindowsHookEx(hhJournal);
    hhJournal = NULL;
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_KMRECORD, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow)) return FALSE;

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KMRECORD));

    AddKMHooks(hInstance);
    // Calling AddKMHooks(GetModuleHandle(NULL)) instead, delivers the same results

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // Once the hook has is set the GetMessage() call above 
    // always returns a WM_TIMER message with a timer ID of 1,
    // posted to the queue with the PostMessage() function,
    // as the Spy++ tool reports

    RemoveKMHooks();

    return (int) msg.wParam;
}

我使用Spy ++工具监视了该应用程序,发现设置了挂钩之后,应用程序会接收到一系列连续的WM_TIMER计时器消息,其计时器ID为1,并与{ {1}}函数(在Spy ++中为P)。据报告,这些消息的窗口句柄与主窗口属于同一应用程序和线程,并且其类名称为“ UserAdapterWindowClass”。我的代码既不会创建计时器,也不会显式创建此类的任何窗口,因此显然它们是由系统创建的。此外,还有另一条ID为0x0060(未知!)的消息,在第一个PostMessage()或第二个WM_TIMER之后,仅一次发布到同一窗口。

应用程序似乎以某种方式“锁定”了系统(记录,等待资源或什么?),直到我按Alt + Ctrl + Del,该文档说它停止记录(我尚未实现某种机制来随意停止录制/卸载该挂钩,所以这是我到目前为止使用的内容)。挂钩过程似乎从未被调用过,这是我目前面临的问题。我已经考虑过在钩子过程中检查code参数并采取相应的措施,但是我什至还没有这样做,因为从未调用过该过程-因此它没有任何作用-所以我只调用{{ 1}}在其中(并假设它会很好地工作)。

注释:

  • hook过程有时可能仅被调用一次,但是它很少见(几乎不可重现)并且绝对不一致,所以我认为我不能依靠它。 CallNextHookEx()参数为0(code)。尽管对其进行了测试,并且返回零或非零(无论是否调用HC_ACTION)都没有任何区别。
  • 我还尝试将钩子设置在另一个线程(在主线程开始处理消息后创建)中,然后该线程也运行一个消息循环,但是我得到的行为完全相同。

有人可以在这里解释什么地方出问题吗?还检查了其他一些帖子,尤其是这些帖子:SetWindowsHookEx for WH_JOURNALRECORD fails under Vista/Windows 7WH_JOURNALRECORD hook in Windows (C++) - Callback never called. ,但是找不到解决方案,我必须注意使用条件也有所不同。我遇到的情况与此SetWindowsHookEx(WH_JOURNALRECORD, ..) sometimes hang the system最接近,尽管就我而言,这总是发生,而不仅仅是“有时”。

非常感谢您的帮助。

我已上载了解决方案(源+ VS文件,但未上载.exe,.obj。,。pch,.pdb等文件,因此需要重新构建)here(如果有人想使用)一看。

提前谢谢


编辑:

在不同配置下测试了应用程序。

  • 最初,该应用程序是在Visual Studio 2017中创建的,并在Windows 10 Pro 32位版本1803、2核AMD处理器计算机(此计算机上安装了VS2017)下进行了测试。得到了上述结果。
  • 然后在Windows 10 Pro 64位版本1903、4核AMD处理器计算机上进行了测试。这台计算机安装了非常旧的Visual Studio版本(尽管它不以任何方式参与开发和测试)。安装证书并运行应用程序(均在另一台计算机上创建)。最初的结果是相同的(hook proc从未调用过)。
  • 尝试从“ PrivateCertStore”存储位置删除证书,仅将副本保留在“受信任的根证书颁发机构”下(这是我编写的批处理文件(称为makecert和certmgr的存储证书的方式,即在以上位置)。出乎意料,但是它起作用了,当我移动鼠标时发出了多次哔声!再次进行了测试,添加/删除/移动了证书,该行为是完全可复制的:只需将证书安装在“受信任的根证书颁发机构”下,钩子就可以工作。
  • 然后在32位计算机上重复上述测试。它在那里不起作用,该应用程序像以前一样被“冻结”(仅获取WM_TIMER消息)。
  • 在64位计算机上,卸载了旧的VS版本并在那里安装了许多Windows SDK版本,并安装了VS 2019 Community Edition。在VS2019中重建应用程序(重新创建并安装证书,并对可执行文件进行签名)。现在,该应用程序在任何一台机器上都不起作用(再次,挂钩创建成功,但是没有调用hook proc)。也许是VS2019安装,或者某些Windows Update导致了此问题。
  • 同样,在32位计算机上创建的应用程序/证书现在无法在任何一台计算机上运行。

因此,这可能是Windows模块或对我不满足的证书的某些要求导致的。 Seacrhed在Internet上使用了很多,但是找不到证书必须符合的规格。例如,对格式,私钥或用途有什么要求(现在我已经检查了所有用途,尽管我还尝试仅检查代码签名,但没有区别)。现在已不建议使用makecert实用程序(该文档建议使用PowerShell代替创建证书),但我不知道这是否与我的问题有关。

我只有两个结论: -证书必须安装在“ PrivateCertStore”下,以便可以构建应用程序(否则签名失败)。可能是因为我在脚本中设置了/ s PrivateCertStore选项。但, -必须将证书安装在“受信任的根证书颁发机构”下,才能运行该应用程序(否则执行失败,并出现访问被拒绝的错误)。

0 个答案:

没有答案