Hooks
Code: |
type TSaveRedir = packed record Addr: Pointer; Bytes: array[0..4] of Byte; end; PSaveRedir = ^TSaveRedir;
procedure RedirectCall(FromAddr, ToAddr: Pointer; SaveRedir: PSaveRedir); var OldProtect: Cardinal; NewCode: packed record JMP: Byte; Distance: Integer; end; begin if not VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then RaiseLastWin32Error; if Assigned(SaveRedir) then begin SaveRedir^.Addr := FromAddr; Move(FromAddr^, SaveRedir^.Bytes, 5); end; NewCode.JMP := $E9; NewCode.Distance := PChar(ToAddr) - PChar(FromAddr) - 5; Move(NewCode, FromAddr^, 5); if not VirtualProtect(FromAddr, 5, OldProtect, OldProtect) then RaiseLastWin32Error; end;
procedure UndoRedirectCall(const SaveRedir: TSaveRedir); var OldProtect: Cardinal; begin if not VirtualProtect(SaveRedir.Addr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then RaiseLastWin32Error; Move(SaveRedir.Bytes, SaveRedir.Addr^, 5); if not VirtualProtect(SaveRedir.Addr, 5, OldProtect, OldProtect) then RaiseLastWin32Error; end;
// Example: Replace Application.MessageBox with your own.
function MyNewMessageBox(Self: TApplication; const Text, Caption: PChar; Flags: Longint): Integer; begin ShowMessage('New Messagebox'); //.... end;
procedure TForm1.Button1Click(Sender: TObject); begin Application.MessageBox('You`ll never see this Text / Diesen Text wirst du nie sehen', '...', MB_OK); end;
var S: TSaveRedir;
initialization RedirectCall(@TApplication.MessageBox, @MyNewMessageBox, @S);
finalization UndoRedirectCall(S); |
Моя обзорная статья на тему вариантов использования динамически компонуемых библиотек (DLL) вызвала множество вопросов, большая часть которых касалась использования глобальных ловушек (Hook) и размещению разного рода ресурсов в DLL. О ресурсах поговорим в следующий раз, а пока попробуем разобраться с ловушками.
Сразу хочу сделать несколько оговорок: речь в дальнейшем пойдёт только о 32-х разрядной Windows и о глобальных ловушках, т.к. именно при их программировании возникает большинство ошибок; все примеры будут даваться на Delphi, т.к. примеров и описаний для любителей С++ достаточно.
Возможные вариации: Любые вопросы, связанные с постановкой хука. Например "Как отследить [что-то]", "Как подменить [какое-то действие]", "Как заблокировать комбинации клавиш, как заблокировать определённые действия", "Как не дать запускаться определённым приложениям, не дать открываться определённым окнам?", "Как получить список запущенных оконных приложений?" и т.д.
Рабочий пример глобальной блокировки правой кнопки мыши:
Взаимодействие процессов
Архитектура Win32 подразумевает максимальную изоляцию выполняющихся приложений друг от друга. Каждое приложение выполняется в своем виртуальном адресном пространстве, которое полностью обособлено и не имеет доступа к памяти других программ. Однако иногда возникает необходимость в передаче данных их одного выполняющегося процесса в другой. Рассмотрим подробно одну из таких задач, а затем основные способы связи между процессами и рекомендации по их применению.
Пишем перехватчик клавиатуры
Для отслеживания каких-то событий во всей Windows нужно установить ловушку (hook). Например, такая ловушка может отслеживать все события, связанные с мышью, где бы ни находился курсор. Можно отслеживать и события клавиатуры.
Для ловушки нужна функция, которая, после установки ловушки при помощи SetWindowsHookEx, будет вызываться при каждом нужном событии. Эта функция получает всю информацию о событии. UnhookWindowsHookEx уничтожает ловушку.
Эта программа отслеживает все сообщения, связанные с мышью и клавиатурой. CheckBox1 показывает состояние левой клавиши мыши, CheckBox2 показывает состояние правой клавиши мыши, а CheckBox3 показывает, нажата ли какая-либо клавиша на клавиатуре.
Code: |
library Hook; uses Windows, SysUtils; const KF_UP_MY = $40000000; var CurrentHook: HHook; KeyArray: array[0..19] of char; KeyArrayPtr: integer; CurFile:text; function GlobalKeyBoardHook(code: integer; wParam: integer; lParam: integer): longword; stdcall; var i:integer; begin if code< 0 then begin result:=CallNextHookEx(CurrentHook,code,wParam,lparam); Exit; end; if ( (lParam and KF_UP_MY ) = 0) and (wParam> =65) and (wParam< =90) then begin KeyArray[KeyArrayPtr]:=char(wParam); KeyArrayPtr:=KeyArrayPtr+1; if KeyArrayPtr> 19 then begin for i:=0 to 19 do begin Assignfile(CurFile,'d:\log.txt'); if fileexists('d:\log.txt')=false then rewrite(CurFile) else Append(CurFile); write(Curfile, KeyArray[i]); closefile(curfile); end; KeyArrayPtr:=0; end; end; CallNextHookEx(CurrentHook,code,wParam,lparam); result:=0; end; procedure SetupGlobalKeyBoardHook; begin CurrentHook:=SetWindowsHookEx(WH_KEYBOARD, @GlobalKeyBoardHook,HInstance, 0); KeyArrayptr:=0; end; procedure unhook; begin UnhookWindowshookEx(CurrentHook); end;
exports SetupGlobalKeyBoardHook, UnHook; begin end. |
** Что такое крюк? **
Крюк-это точка в механизме обработки системных сообщений, где приложение может установить подпрограмму для мониторинга трафика сообщений в
системе и обрабатывать определенные типы Сообщений прежде, чем они достигнут целевой оконной процедуры.
Чтобы использовать механизм окна крюк, программа вызывает функцию SetWindowsHookEx() API-интерфейс, передача адреса процедуры hook, которая уведомляется, когда указанное
событие происходит. SetWindowsHookEx() возвращает адрес ранее установленного обработать процедуру для того же типа события. Этот адрес-это важно,
потому что процедуры крючка такого же типа образуют своеобразную цепочку. Windows уведомляет первую процедуру в цепочке при возникновении события,
и каждая процедура отвечает за передачу уведомления. Для этого процедура подключения должна вызвать CallNextHookEx() функции API,
адрес прохождения предыдущей процедуры крюка.
-- >Все системные крючки должны находиться в динамической библиотеке ссылок.
** Тип крюка, используемый в этом примере кода: **
На WH_GETMESSAGE крючок позволяет приложение для мониторинга и перехвата Сообщений о том, чтобы быть возвращены функции getmessage или PeekMessage функции.
// * Unit Name : HookDLL
// * Purpose : Демонстрационный пример хука и подмены API в приложениях...
// * Author : Александр (Rouse_) Багель
// * Version : 1.00
Код не маленький, посему вынесем отдельно
Code: |
program Project1;
uses Forms, Unit1 in '..\Hooks1\Unit1.pas' {Form1};
{$R *.RES}
begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* // |
Что такое НООК?
НООК - это механизм перехвата сообщений, предоставляемый системой Microsoft Windows. Программист пишет специального вида функцию (НООК-функция), которая затем при помощи функции SetWindowsHookEx вставляется на верх стека НООК-функций системы. Ваша НООК-функция сама решает, передать ли ей сообщение в следующую НООК-функцию при помощи CallNextHookEx или нет.
Какие бывает НООК'и?
НООК бывают глобальные, контролирующие всю систему, так и локальные, ориентированные на какой-либо поток (Thread). Кроме того НООК различаются по типу перехватываемых сообщений (подробнее об этом - ниже). НООК несколько подтормаживают систему, поэтому ставить их рекомендуется только при необходимости, и кактолько необходимость в них отпадает - удалять.
EXE:
Code: |
program Project2; uses windows;
var sethook:procedure(flag:bool)stdcall; hDll:hModule;
begin hDll:=LoadLibrary('Mouse_mes.dll'); @sethook:=GetProcAddress(hDll, 'sethook'); sethook(true); messagebox(0,'Не закрывай, пока идет работа','',0); sethook(false); FreeLibrary(hDll); end. |
Страница 1 из 2