Отключение набора хуков с помощью SetWindowsHookEx несколько раз приводит к сбою перехваченного процесса - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь перехватить события клавиатуры и мыши целевого приложения. Я следовал за вопросом SO Как перехватить внешний процесс с помощью SetWindowsHookEx и WH_KEYBOARD , и перехватчики правильно устанавливаются и удаляются с первого раза. Однако после того, как я удаляю ловушку один раз, а затем устанавливаю ее снова, попытка удалить ловушку во второй раз приводит к сбою целевого приложения. Цель ловушки - отслеживать время простоя приложения, чтобы я мог выполнять некоторые задачи во время простоя приложения. Я прошу прощения за длину вопроса, но я попытался указать все детали, которые могут помочь Спасибо

Мне нужно иметь возможность устанавливать и удалять хуки, основываясь на командах меню из иконки в системном трее. У меня есть консольное приложение [HookApp], которое вызывает методы установки и удаления в DLL [HookDLL]. Консольное приложение также создает окно для обработки событий меню. Я использую поток окна, чтобы фактически установить и удалить хуки, потому что тот же поток, который установил хук, должен удалить его. Консоль и окно должны быть невидимыми. Должен быть виден только значок на панели задач и соответствующее меню. Я запускаю приложение для перехвата из командной строки с параметрами я. ProcessId целевого процесса II. Имя процесса III. Простой [в секундах] - Простой перед запуском действия в DLL Мне интересно, если таймер, который я запускаю в HookProcs, отвечает за сбой.

Журналы просмотра событий Неверное имя приложения: notepad.exe, версия: 10.0.17134.1, отметка времени: 0x9d4727c2 Неверное имя модуля: HookDLL_x64.dll_unloaded, версия: 0.0.0.0, отметка времени: 0x5c31aabd Код исключения: 0xc0000005 Смещение ошибки: 0x00000000000ba505 Идентификатор ошибочного процесса: 0x2bac

Кажется, что код исключения предполагает нарушение доступа к памяти.

Я пробовал следующее Я. а. Вызовите API SendMessageTimeout с HWND_BROADCAST из HookApp б. Вызовите API SendMessageTimeout с HWND_BROADCAST из HookDLL на основе Выгрузка DLL из всех процессов после отсоединения глобальной ловушки CBT

II. а. Вызовите FreeLibraryAndExitThread API в методе DLLMain для DLL_PROCESS_DETACH AND DLL_THREAD_DETACH - вместе и поодиночке на основании http://www.rohitab.com/discuss/topic/42505-unloading-dll-crashes-exe/

III. Сделали консоль HookApp и окно видимыми и скрытыми, чтобы понять, имеет ли это какое-то значение

IV. Не удалось попробовать следующее из-за 64-битной архитектуры https://www.unknowncheats.me/forum/programming-for-beginners/73377-unload-injected-dll-thread-process.html

Другие ссылки, на которые я ссылался а. Сбой нескольких программ при отсоединении от UnhookWindowsHookEx () б. Как правильно использовать SetWindowsHookEx & CallNextHookEx с. Выгрузка внедренной DLL д. FreeLibraryAndExitThread завершает работу программы при выгрузке внедренной DLL

//Code in the DLL
extern "C" __declspec(dllexport) void install(unsigned long threadID,int _nIdleTime) {
    nIdleTime = _nIdleTime;
    Log(L"install proc called from app: _nIdleTime", _nIdleTime);
    hhKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hinst, threadID);
    hhMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, hinst, threadID);
    logger->Log("Install");
    logger->Log("Keyboard", (LONG)hhKeyboard);
    logger->Log("Mouse", (LONG)hhMouse);

}

//Uninstall the dll from the Target Process
DWORD WINAPI UnInjectDLLFromTarget() {
    uninstall();
    //DWORD_PTR dwResult = 0;
    //SendMessageTimeout(HWND_BROADCAST, WM_NULL, 0, 0, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, &dwResult);
    return TRUE;
}

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {
    if (code < 0) {

        return CallNextHookEx(0, code, wParam, lParam);
    }
    StartTimer();
    Beep(1000, 20);


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

LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
    if (code < 0) {

        return CallNextHookEx(0, code, wParam, lParam);
    }
    //  StartTimer();
        //Beep(1000, 20);

    return CallNextHookEx(hhMouse, code, wParam, lParam);
}
BOOL WINAPI DllMain(__in HINSTANCE hinstDLL, __in  DWORD fdwReason, __in  LPVOID lpvReserved) {


    if (fdwReason == DLL_PROCESS_DETACH || fdwReason==DLL_THREAD_DETACH) {
        FreeLibraryAndExitThread(hinstDLL, 0);
        return 0;
    }
}

//Code in the Application [HookApp]
Similar to  [But not exactly the same - It is a bit more involved because of the need to create the message pump and the menu event handling window]
 HINSTANCE hinst = LoadLibrary(_T("MyDLL.dll")); 

    if (hinst) {
        typedef void (*Install)(unsigned long);
        typedef void (*Uninstall)();

        Install install = (Install) GetProcAddress(hinst, "install");
        Uninstall uninstall = (Uninstall) GetProcAddress(hinst, "uninstall");

        install(threadID);

        Sleep(20000);

        uninstall();
    }
  1. SendMessageTimeout в методе DLL [HookDLL] Удаление приводит к немедленному завершению работы приложения
  2. SendMessageTimeout в приложении [HookApp], похоже, не делает ничего полезного.
  3. FreeLibraryAndExitThread, похоже, не делает ничего полезного. Тем не менее, с 2 и 3, я думаю, что я могу установить крюк во второй раз. Без них первый unhook вылетает приложение.
  4. Мне не удалось попробовать предложение с использованием встроенного языка ассемблера из-за 64-битной архитектуры.

Пожалуйста, помогите.

1 Ответ

0 голосов
/ 06 января 2019

для деинсталляции ловушки и выгрузки dll всего, что вам нужно - звоните UnhookWindowsHookEx для каждого дескриптора ловушки, полученного предыдущим вызовом SetWindowsHookEx. все. Вам не нужно звонить FreeLibrary[AndExitThread] самостоятельно. Системный автоматический вызов FreeLibrary на вашем перехвате dll после UnhookWindowsHookEx вызова, когда первое (любое) сообщение будет получено потоком целевого приложения, и этот поток вызовет GetMessage или PeekMessage. так что просто выгрузите свою dll после нескольких UnhookWindowsHookEx - вам нужно опубликовать сообщение в ветке, для которой вы устанавливаете ловушку. если вы делаете это для конкретного отдельного потока (dwThreadId) - вам нужно PostThreadMessageW(dwThreadId, WM_NULL, 0, 0); - система сама вызовет FreeLibrary (вызывается из user32!__ClientFreeLibrary, который вызывается из ntdll!KiUserCallbackDispatcher - вызывается из GetMessage или PeekMessage при выполнении потока (или это окно) получил какое-либо сообщение)

вызов FreeLibrary из dll для себя - всегда ошибка - если предположить, что dll будет выгружен этим вызовом - куда мы вернемся после вызова? в незагруженное место. Звоните FreeLibraryAndExitThread Существуют смысл в некоторых случаях, но только из потока, который вы создаете сами. вызовите FreeLibraryAndExitThread из точки входа dll при любой фатальной ошибке - вы убиваете не сам поток, поток, который вы убиваете - удерживаете критическую секцию (блокировку загрузчика) внутри которой называется точка входа dll - такая фатальная ошибка. и вообще абсолютно бессмысленный вызов здесь - если вы говорите, что получили DLL_PROCESS_DETACH, это означает, что dll уже находится в процессе выгрузки. DLL_THREAD_DETACH - произвольная точка, почему и сколько раз вы пытаетесь здесь вызвать unload для себя? выходите из резьбы, удерживая широкий критический участок процесса без его освобождения фатальная ошибка.

также , поскольку тот же поток, который установил хук, должен удалить его. - это не так. другой поток от также может сделать это.

также StartTimer(); в вашем коде выглядит подозрительно. что делает этот код? есть и где вы отменяете таймер?

...