Глобальный Keyhook на 64-битной Windows

В настоящее время у меня есть некоторые проблемы, чтобы заставить работать глобальную связку ключей на 64-битной операционной системе Windows 7. Теперь я знаю, что в Stackoverflow уже есть много тем на эту тему, но ни одна из них не дает мне ответа, с которым я могу работать, или я не совсем понимаю, как эта проблема решается в этих темах.

Поэтому я постараюсь объяснить, с чем я борюсь, и надеюсь, что кто-нибудь может мне помочь или указать мне правильное направление.

По сути, моя цель — перехватить ввод с клавиатуры CTRL + C и CTRL + V для своего рода диспетчера буфера обмена. По этой причине моя текущая попытка состоит в том, чтобы зарегистрировать общесистемную WH_KEYBOARD крючок, который имеет дело с перехваченными нажатиями клавиш для моих потребностей.

Я использую 64-разрядную версию Windows 7 O / S, и именно здесь начинаются проблемы. Для меня очевидно, что 32-битная библиотека Hook вызывает проблемы для 64-битных процессов, и наоборот. По этой причине я сгенерировал версию моей библиотеки для x86 и x64, содержащую ловушку, а также для вызывающего ловушку (тот, кто вызывает SetWindowsHookEx()), оба с разными именами файлов, как предполагает документация.

А что теперь? Если я установлю свою 64-битную DLL как общесистемный крюк, все 32-битные приложения начинают зависать, как только я нажимаю клавишу, когда они находятся в фокусе. То же самое, когда я применяю 32-битный хук, моя Windows практически не работает, потому что explorer.exe 64-битный Если я установлю оба хука, моя система будет бездействующей, имея глобальную битвную битву.

Теперь я предполагаю, что проблема возникает, например, из 64-битная DLL-библиотека перехвата, пытающаяся внедриться в 32-битный процесс и т. д., что, конечно, чепуха. Но для этого случая документация SetWindowsHookEx() говорит следующее:

Поскольку хуки выполняются в контексте приложения, они должны соответствовать
«битность» приложения. Если 32-разрядное приложение устанавливает
глобальный хук в 64-битной Windows, 32-х битный хук вводится в каждый
32-битный процесс (применяются обычные границы безопасности). В 64-битном
процесс, потоки все еще помечены как «подключенные». Тем не менее, потому что
32-битное приложение должно запускать код подключения, система выполняет
подключить в контексте приложения перехвата; в частности, в теме
называется SetWindowsHookEx. Это означает, что приложение для перехвата должно
продолжать качать сообщения, или это может заблокировать нормальное функционирование
64-битные процессы.

Я не полностью понимаю жирную часть текста, но я интерпретирую это следующим образом: если битность цели перехвата отличается от таковой у перехвата, она выполняется в потоке, который фактически установил перехват так это может быть выполнено вообще. Кроме того, это означает, что этот поток все еще должен быть активным и, вероятно, выполнять своего рода цикл сообщений. Это правильно? Или я совсем не согласен с этим? Документация также дает точные инструкции о том, что делать для моего сценария:

Чтобы подключить все приложения на рабочем столе 64-битной Windows
установка, установка 32-битного глобального хука и 64-битного глобального хука,
каждый из соответствующих процессов, и обязательно продолжайте прокачивать сообщения
в приложении подключения, чтобы избежать блокирования нормального функционирования.

Но все же я не понимаю, что должно быть сделано в моей реализации. Чтобы наконец показать некоторый код, давайте возьмем этот базовый пример попытки установить общесистемный keyhook. Я предполагаю, что создание кода для потока не имеет значения:

volatile static bool runThread = false;

DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));

runThread = true;
while(runThread) {
// Message pump? Yes? No? How?
Sleep(10);
}

UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}

Сам хук поддерживается довольно тривиально — этого достаточно, чтобы вызвать проблемы с зависанием при пересечении битности:

extern "C" LRESULT hookProc(int code, WPARAM wParam, LPARAM lParam) {
if(code == HC_ACTION) {
}

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

Я предполагаю, что некоторые люди могут бросить туда руки над головой прямо сейчас, и что вы можете сказать, что я редко работал с крючками;)

Но именно поэтому я и спрашиваю 🙂

Короче говоря: я был бы признателен, если бы кто-нибудь сказал мне, как изменить приведенный выше пример, чтобы получить общесистемный ключ-хук, работающий на 64-битной Windows. Моя проблема в том, что некоторые приложения с другой «битностью», кроме хука, начинают зависать, и я не знаю, как решить проблему.

Любая помощь очень ценится!

Спасибо и всего наилучшего

PuerNoctis

1

Решение

Хорошо, я понял, в чем проблема. Возможно, мое решение поможет другим людям, испытывающим ту же проблему: как уже упоминалось выше, в документации прямо говорится, что

[…] система выполняет (32-битную) перехват в (32-битной) перехватывающей программе
контекст; в частности, в потоке, который называется SetWindowsHookEx.
Это означает, что приложение перехвата должно продолжать качать сообщения
или это может заблокировать нормальное функционирование 64-битных процессов.

То, что я испытывал, было упомянутым поведением блокировки, которое, как предполагается, должно быть преодолено насосом сообщений. В приведенном выше примере кода отсутствовал именно этот механизм, так как я не знал, что это должен быть простой цикл сообщений Windows (раньше я не знал термина «насос»). Окончательный фрагмент кода из моего исходного кода должен выглядеть примерно так:

volatile static bool runThread = false;

DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));

MSG msg;
runThread = true;
while(runThread) {
// Keep pumping...
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
TranslateMessage(&msg);
DispatchMessage(&msg);
Sleep(10);
}

UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}

В этом примере я использую неблокирующую PeekMessage() вместо GetMessage(), так как я хочу, чтобы моя тема постоянно проверяла свой флаг завершения.

В этой реализации мой хук работает, как и ожидалось, во всех собственных 64-битных процессах или процессах WOW64, ни одно приложение не зависает, как только они подключаются. Насос сообщения был единственной пропавшей частью.

После всего этого эксперимента я прихожу к следующему выводу — и если я ошибаюсь, пожалуйста, исправьте меня в комментариях:

Когда установлен общесистемный хук, он пытается внедрить данную Hook-DLL в каждый запущенный процесс. Если «битность» процесса совпадает с «битностью» процесса из библиотеки Hook-DLL, процедура перехвата выполняется как удаленный поток в целевом процессе (как работают обычные инжекторы). В случае, если «битность» отличается, Windows возвращает запасной путь назад к процессу и потоку, который первоначально вызывал SetWindowsHookEx() (в моем примере поток во фрагменте кода), и служит прокси-сервером выполнения для процесса, который не соответствует «разрядности». По этой причине требуется, чтобы этот поток постоянно обрабатывал входящие сообщения, иначе никакие события не будут обрабатываться для исходного целевого процесса, который в свою очередь начинает зависать.

Опять же, если это не совсем верно, неполно или есть какие-то дополнительные примечания, добавьте комментарий ниже. Спасибо! 🙂

4

Другие решения

Мое решение состоит в том, чтобы скомпилировать приложение хука (в котором SetWindowsHookEx() была вызвана) и DLL (где расположена функция ловушки обратного вызова) для версий как x86, так и x64.
Таким образом, у нас есть два EXE-файла (xxx-x86.exe & xxx-x64.exe) и две библиотеки DLL (xxx-x86.dll) & ххх-x64.dll).

Затем реализуйте сложный протокол IPC для синхронизации данных между приложением x86 и приложением x64. Это выглядит работать, не будет блокировать другой процесс, который «битность» не имеет себе равных. Но трудно обрабатывать точные последовательности событий, это может служить лишь приблизительным индикатором событий.

Это решение довольно некрасиво, но у меня нет лучшего способа …

0

По вопросам рекламы [email protected]