RunPE   Version imprimable de cet article Enregistrer au format PDF

Technique de packing Windows


par Thanat0s

RunPE est le nom générique d’une technique utilisée par de nombreux malwares.

Cette technique consiste à lancer un nouveau processus en pause, puis de remplacer le contenu mémoire de l’exécutable en pause et enfin de relâcher le processus. Cela permet d’exécuter un exécutable complet sans avoir à le déposer sur le disque. Cela permet d’éviter la détections par les antivirus d’accès.

Prenons un exemple. dans un Buffer nous avons l’image complète d’un fichier exécutable windows (PE).

La première étape est de démarrer un processus en mode "CREATE_SUSPENDED" avec l’api CreateProcessA de Kernel32.dll. N’importe quel exécutable peut faire l’affaire. il y a juste deux restrictions.
Si on est sur un processus 32 bits, il faut démarrer un processus 32 bits.
Si on est sur un processus GUI, il faut démarrer un processus GUI et non console.

Une fois le processus démarré et suspendu on va utiliser la fonction GetThreadContext de kernel32.dll afin de récupérer le contexte cpu de ce process. Le contexte CPU est une structure (un array quoi) qui va récupérer les valeurs de chaque registre (de DR0 à FS en passant par EAX,EBX etc..) de notre process suspendu.

Dans ce contexte on va s’intéresser à un seul registre, EBX. il va donner l’adresse du PEB (Process Environnement Bloc). Le PEB sous windows est une structure essentielle.
À PEB se trouve l’adresse de base ou est mappé l’exécutable suspendu. On va lire cette adresse grâce à la fonction ReadProcessMemory de kernel32.dll.

Désormais on connait l’adresse de base ou est lancé et mappé le process que nous venons de lancer.

En parsant l’exécutable PE contenu dans notre buffer, on retrouve l’adresse de base de notre Exe (Champ ImageBase de la structure Image_Optionnal_Header).

Si l’adresse collisionne avec celle vu dans la PEB on va utiliser la fonction non documentée NtUnmapViewOfSection de NTDLL.dll afin de démapper l’exécutable dans le processus suspendu et ainsi libérer la mémoire.

On utilise alors la fonction VirtualAllocEX de kernel32.dll pour allouer la mémoire à l’adresse de base de l’exécutable dans notre exécutable suspendu (le mode RWE, pour tous suffit).

Il reste alors à remapper le header, et chaque section à sa place dans le process suspendu. Cette étape nécessite de parser une fois de plus notre PE et d’utiliser WriteProcessMemory de kernel32.dll

Si besoin, il faut updater l’adresse de base dans le process suspendu à PEB grâce encore une fois à WriteProcessMemory.

Enfin , il ne reste plus qu’à updater EAX dans notre contexte CPU avec l’adresse de départ du programme de notre buffer. Cette adresse est contenue elle aussi dans les Header de notre PE (AddressOfEntryPoint + ImageBase de Image_Optionnal_Header ). On replace le contexte CPU dans notre process avec la fonction SetThreadContext de kernel32.dll

Et enfin il ne reste qu’à relâcher le process suspendu avec ResumeThread de kernel32.

Windows reprend la main, initialise les tables d’import et saute a l’entry point de notre programme.

Cette technique nécessite de savoir parser un fichier PE mais n’est pas fondamentalement compliquée et il n’y a pas besoin de fixer les tables d’imports. En fonction des packers, la source est plus ou moins conservée chiffrée tant que c’est possible. Pour certains, breaker sur ResumeThread suffit pour récupérer l’exécutable déchiffré en mémoire. Pour d’autres, il faut breaker sur writeprocessmemory et reconstruire le puzzle.

Documentations publiées dans cette rubrique Documentations publiées dans cette rubrique