mercredi 6 août 2008

Cachez ce module que je ne saurai voir...

Putin qu'est ce qu'il fait chaud...

L'affichage des modules chargés par un processus ( les DLLs kwa !) de façon fiable est un problème qui se pose rapidement quand on commence à foutre le dawa dans la mémoire.

Il y a deux méthodes fort connues basées sur des fonctions documentées par Microsoft...

1- Cheese !

La première façon c'est d'utiliser CreateToolhelp32Snapshot() avec TH32CS_SNAPMODULE en premier argument, ça nous retourne un HANDLE sur un snapshot ( une "image" à un instant t de la mémoire, dans notre cas des modules ) et il suffit de se promener à partir de ce HANDLE avec la fonction Module32Next() pour récupérer une structure MODULEENTRY32 qui donne le nom de chaque module, son adresse de base, son chemin...

Le code source du bouzin est ici, j'ai aussi utilisé cette API pour lister les threads d'un process ( même méthode sauf qu'on parcourt des THREADENTRY32 ce coup ci).

2- EnumProcessModules()

La deuxième f0cking way de lister les modules c'est d'utiliser une fonction qui porte bien son nom : EnumProcessModules()... Ca nous remplit un tableau avec des HANDLEs sur tous les modules chargés en mémoire pour un processus donné, ensuite on récupère les infos intéressantes avec les fonctions GetModuleBaseName(), GetModuleFileNameEx() et GetModuleInformation().

Le code source du bouzin est , rien de bien particulier à dire.

3- Euh oué... et alors ?

Là cher lecteur t'a envie de me dire "Chapi chapo jojo, t'a réussi à utiliser des fonctions documentés pour faire exactement la même chose de deux manières différentes, putin tu roxsamere". Oui mais c'est là que ça devient intéressant...

Si on s'interesse à la structure du Process Executiv Block (celle qui "décrit" un processus en user-land), son troisième champ est :

PEB_LDR_DATA * LdrData;

Et la structure PEB_LDR_DATA contient (entre autre) :

...
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
...

C'est à dire le point de départ vers trois listes doublement chainées ( la structure LIST_ENTRY n'est constituée que deux pointeurs, l'un vers "l'avant", l'autre vers "l'arrière" ) qui vont liées entre elles des structures LDR_DATA_ENTRY décrivant les modules chargées en mémoire dans trois ordres différents...

Cette structure contient la suite du chainage dans ses trois premiers champs :

LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;


Tout ceci est décrit très brièvement, car même si ce n'est pas documenté officiellement tu trouvera facilement toutes les infos sur ces structures dans l'internet multimédia ( par exemple dans le post de lilxam sur nibbles ).

Ce qui est intéressant c'est que tout ça c'est dans l'user-land, alors bien sûr la première idée qui up dans ton mind c'est de modifier ces listes chainées, pour "cacher" ton module favoris dans un processus cible.

Et tu as raison, car c'est facile à faire :

On trouve l'adresse du PEB du processus cible en créant un thread distant sur la routine RtlGetCurrentPeb() et on récupère sa valeur de retour (l'adresse du PEB) avec un GetExitCodeThread().

On trouve ensuite l'adresse du premier module dans le champ LdrData, puis on démarre un parcours sur les LDR_DATA_ENTRY, quand on trouve le module qui nous intéresse, on l'enlève du chainage. Pour cela, c'est simplement de l'unlinkage dans une liste doublement chainée : on fait pointer le "pointeur avant" (Flink) du module précédent vers le module suivant, et le "pointeur arrière" (Blink) du module suivant vers le module précédent ( tu me suis ?). Par exemple, pour la InLoadOrderModuleList, ça donne :


/* Modifie Flink du module précédent (=Blink du module courant) pour le faire pointer sur le module suivant (=Flink du module courant) */
WriteProcessMemory(targetProcess,
module.InLoadOrderModuleList.Blink,
&module.InLoadOrderModuleList.Flink,
4,
NULL);

/* Modifie Blink du module suivant (=Flink du module courant + 4) pour le faire pointer sur le module précédent (=Blink du module courant) */
WriteProcessMemory(targetProcess,
module.InLoadOrderModuleList.Flink+4,
&module.InLoadOrderModuleList.Blink,
4,
NULL);

Faut pas oublier qu'on est dans un process distant, donc un autre espace mémoire, d'où le WriteProcesMemory(). Et là où ca pwne, c'est que si après cet unlinkage je lance mon premier programme de listings ( celui avec
CreateToolhelp32Snapshot()), mon module n'apparait plus !!

Par contre, le EnumProcessModules() fonctionne toujours.. si on était un peu naïf on pourrait se dire que cette API utilise une façon "sûre" de lister les modules avec des techniques de jedi inside the kernel, et là on commence à s'exciter sur une way de la bypasser...

...Et puis on se rapelle qu'il y a deux autres listes doublement chainées sur les modules : InMemoryOrderModuleList et InInitializationOrderModuleList. Alors on fait le même unlinkage et on s'aperçoit que
EnumProcessModules() utilise la InMemoryOrderModuleList, et qu'il est donc aussi facilement pwnable...

Ca nous donne un "cacheur" de modules que vous trouverez ici, vous lui filez un PID et un nom de module ( du type ntdll.dll kwa ) et il n'apparaitra plus lorsqu'on utilise les APIs sus-nommées.

C'est bien sûr pas la technique du siècle, il suffit de checker le dump mémoire pour voir les modules mais ce qui est rigolo c'est que par exemple ProcessExplorer utilise ces APIs pour lister les modules d'un process et donc on le pwn sans aucun souci, ça invite à être prudent sur les infos fournies par ce genre d'outils :-)

2 commentaires:

Anonyme a dit…

Cool ça confirmerais une partie de ce que je pensais :)
(http://nibbles.tuxfamily.org/?p=66#comment-671)
oui le pseudo est différent mais j'ai pas encore trouvé de nom de scène XD

Anonyme a dit…

Juste un petit message pour rendre à césar ce qui est à césar.
On retrouve le même sujet en plus détaillé (par Neista) sur http://reverseengineering.online.fr/spip/spip.php?article85

Sinon bon boulot quand même :)