Крюковая процедура для Journal Record Hook никогда не вызывается - PullRequest
0 голосов
/ 20 сентября 2019

Я пытаюсь создать простое приложение для записи и воспроизведения серии команд клавиатуры и мыши (макросы).Прочитайте документацию и пришли к выводу, что наиболее подходящей реализацией (если не единственной) было бы установить хук записи журнала Windows (WH_JOURNALRECORD) и воспроизвести его с воспроизведением журнала (WH_JOURNAL_PLAYBACK).

Согласно документации, эти хуки не обязательно должны находиться в DLL, вместо этого они могут находиться в исполняемом файле (приложении).Итак, Visual Studio создала для меня простое приложение Win32.Это очень классическое приложение, регистрирующее класс окна, создающее окно и запускающее цикл обработки сообщений.В документации также упоминается, что процедуры ловушек для WH_JOURNALRECORD / WH_JOURNAL_PLAYBACK ловушек выполняются в контексте потока, который их устанавливает.Тем не менее, в нем конкретно не упоминается, что должен делать этот поток, например, запустить цикл обработки сообщений, спящий режим в состоянии оповещения или что.Поэтому я просто установил ловушку и запустил цикл сообщений - это основной и единственный поток приложения.Это то, что некоторые примеры кода, которые я нашел, также делают, хотя они, кажется, не работают на данный момент, поскольку они довольно старые, и некоторые обновления безопасности Windows сделали вещи намного более сложными.

Я полагаю, что у меня естьпредпринял все необходимые шаги, которые я нашел в некоторых примерах и сообщениях:

  • Установите для параметров манифеста " Уровень выполнения UAC " значение " requireAdministrator (/ level = 'requireAdministrator"')"и" Защита UAC Bypass UI Protection"до" Да (/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сообщения с идентификатором таймера 1 отправляются в очередь с помощью функции PostMessage() (P в Spy ++).Сообщается, что дескриптор окна для этих сообщений принадлежит тому же приложению и потоку, что и основное окно, а его имя класса - «UserAdapterWindowClass».Мой код не создает таймеры и явно не создает никаких окон этого класса, поэтому, очевидно, они создаются системой.Кроме того, есть еще одно сообщение с идентификатором 0x0060 (Неизвестно!), Размещенное в том же окне только один раз, после 1-го или 2-го WM_TIMER.

Приложение, похоже, каким-то образом «блокирует»система (запись, ожидание ресурса или что?), пока я не нажму Alt + Ctrl + Del, что, как сказано в документации, останавливает запись (я еще не реализовал какой-либо механизм для остановки записи / удаления ловушки по желанию, так что эточем я пока пользуюсь).Процедура ловушки, кажется, никогда не вызывается, и это проблема, с которой я сталкиваюсь в данный момент.Я рассмотрел проверку параметра code в процедуре подключения и действую соответствующим образом, но я даже не близок к этому, поскольку процедура никогда не вызывается - и, следовательно, она не будет иметь никакого эффекта - поэтому я просто вызываю CallNextHookEx() втам (и предположим, что это будет работать хорошо).

Примечания:

  • Процедура перехвата может иногда вызываться только один раз, но это довольно редко (почти не- воспроизводимо) и определенно не соответствует, поэтому я не могу на это полагаться;параметр code равен 0 (HC_ACTION).Протестировал это, хотя и возвращая либо ноль, либо ненулевое значение, либо вызывая CallNextHookEx(), либо нет, вообще не имеет значения.
  • Я также пытался установить ловушку в другом потоке (созданном после того, как основной начал обрабатывать сообщения), который затем также запускает цикл обработки сообщений, но я получаю точно такое же поведение.

Может кто-нибудь объяснить, что здесь может быть не так?Также проверил некоторые другие посты, особенно эти: Сбой SetWindowsHookEx для WH_JOURNALRECORD в Vista / Windows 7 и WH_JOURNALRECORD в Windows (C ++) - Обратный вызов никогда не вызывался., однако не смог найти решение, и я должен отметить, что условия использования тоже отличаются.То, что я испытываю, ближе всего к этому SetWindowsHookEx (WH_JOURNALRECORD, ..) иногда зависает система , хотя в моем случае это происходит всегда, а не просто "иногда".

Я был бы очень признателенлюбая помощь.

Я загрузил решение (исходные файлы + файлы VS, но не файлы .exe, .obj., .pch, .pdb и т. д., поэтому требуется перестройка) здесь , если кто-то хотел бы взглянуть.

Заранее спасибо

...