Здравствуйте, Хабровчане.
Решил написать программный логгер клавиатуры на C++ с использованием WinAPI. Не могу сказать, что преследовал какую-нибудь шпионскую цель, когда писал его, скорее знакомился с хуками на WinAPI. Так как получилось не так уж и плохо, а на Хабре нет статьи про программные логгеры, решил написать свою.
Как это сделано?
Для отлавливания нажатия клавиш был использован клавиатурный хук.
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
Для того, чтобы перехватывать нажатия всех клавиатурных клавиш, в качестве параметра idHook удобно указать WH_KEYBOARD или WH_KEYBOARD_LL. Разница лишь в том, что WH_KEYBOARD_LL также перехватывает нажатия системных клавиш (т.е Alt или любая клавиша при зажатом Alt), поэтому его мы и выберем.
lpfn — указатель на функцию, обрабатывающую перехваченные сообщения (в нашем случае нажатия клавиш).
hMod — дескриптор экземпляра приложения, содержащий обрабатывающую функцию.
dwThreadId — идентификатор потока, сообщения которого мы хотим перехватывать. Нужно установить данный параметр в 0, чтобы перехватывать сообщения всех потоков.
Возвращаемое значение — дескриптор нашего хука, который при выходе нужно будет освободить функцией UnhookWindowsHookEx.
Обратившись за справкой в MSDN, мы видим прототип функции, обрабатывающей сообщения данного хука.
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
Параметр nCode должен быть равен HC_ACTION, иначе сообщение предоставляется другому процессу.
wParam — это одно из следующих значений WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, или WM_SYSKEYUP.
lParam — указатель на структуру KBDLLHOOKSTRUCT, в полях которой нас интересуют только 2 параметра: vkCode(виртуальный код) и scanCode нажатой клавиши.
Данная функция должна вернуть значение функции CallNextHookEx, в противном же случае, следующий обрабатывающий событие хук, может получить неверные параметры сообщения.
Каждый раз при нажатии клавиши, наша программа будет перехватывать это событие и обрабатывать нашей процедурой LowLevelKeyboardProc.
Для того, чтобы ретранслировать виртуальный и скан код клавиши в символьный вид, нам понадобится функция ToAsciiEx.
int WINAPI ToAsciiEx(
_In_ UINT uVirtKey,
_In_ UINT uScanCode,
_In_opt_ const BYTE *lpKeyState,
_Out_ LPWORD lpChar,
_In_ UINT uFlags,
_In_opt_ HKL dwhkl
);
Первые 2 параметра — виртуальный и скан коды клавиши соответственно.
lpKeyState — состояние клавиатуры, проверяет какие клавиши нажаты/активны.
lpChar — указатель на двойное слово, в которое функция запишет символьное представление клавиши.
uFlags — параметр, указывающий на активность меню.
dwhkl — идентификатор раскладки клавиатуры.
Возвращаемое значение — количество символов, записанных в буфер lpChar. Нас интересует случай, когда записан 1 символ.
В принципе, это 3 основные функции, требуемые для самого простого клавиатурного логгера.
Немного о программе
Программа компилируется без RTL в Visual Studio 2013. Таким образом, мы получаем небольшой объём исполняемого файла и невозможность сборки в debug-версии. Данные записываются в лог файл в тот же каталог, где находится .exe файл. Для удобства логгер каждый раз создает новый файл при записи, записывает время нажатия клавиш, имя окна, в котором вводились символы и останавливается по нажатию LSHIFT+RSHIFT. Данный сниффер не адаптирован под полную клавиатуру, некоторые служебные клавиши, например F13 или NUM_LOCK, могут писаться, как [unknown]. Думаю те, кто хоть немного знаком с C/C++ смогут запросто их добавить. Более того, Вы можете полностью изменить код так, как Вам удобно.
Ссылки на Bitbucket: исходник, бинарник, проект на vs 2013
Программа не требует прав администратора, поэтому её можно втихую поставить на любой компьютер друга под управлением Windows.
Автор: Skapix
Привeт, код истотчника нe доступeн. Поможeтe?
С уважeниeм ипсилон.