Concepts Fondamentaux
L'Empaquetage (Packing)
L'empaquetage, ou packing, est une technique de compression ou de chiffrement des ressources d'un programme exécutable. Un programme empaqueté peut être exécuté directement, mais son code source original est masqué et inaccessible sans un processus de dédosage (unpacking).
Le mécanisme de déchiffrement ou de décompression est entièrement transparent et s'opère en mémoire vive. Lorsque le programme est chargé par le système d'exploitation Windows, le code de l'empaqueteur prend le contrôle en premier. Il se charge de déchiffrer ou de décompresser le programme original en mémoire, puis lui restitue le contrôle pour son exécution normale. Ainsi, le code original sur le disque est souvent chiffré ou compressé, et n'est restauré qu'au moment de l'exécution.
Les empaqueteurs se divisent généralement en deux catégories : les compresseurs, qui visent principalement à réduire la taille du fichier, et les protecteurs, dont l'objectif est le chiffrement et la protection du code. Certains protecteurs offrent des fonctionnalités supplémentaires comme des mécanismes de licence, des limites d'utilisation ou de temps.
Détection
Une signature courante des programmes empaquetés est la présence de l'instruction pushal (qui empile tous les registres généraux) au point d'entrée du programme, souvent détectée par des outils d'analyse statique ou heuristique.
(Heur)Packer: Packer detected[Last section EP + "pushal" at EP]
Point d'Entrée Original (OEP)
L'OEP (Original Entry Point) est l'adresse réelle où le code du programme non empaqueté commence son exécution. Les empaqueteurs masquent souvent l'OEP véritable (ou en utilisent un faux), rendant sa découverte essentielle pour le dédosage.
Lors de l'utilisation d'un débogueur comme OllyDbg, un programme empaqueté s'arrête généralement dans le bloc de prétraitement de l'empaqueteur, avant la décompression ou le déchiffrement du code original. Une fois le module d'auto-dédosage de l'empaqueteur terminé, le programme s'arrête à l'OEP d'origine. C'est le moment idéal pour dumper le programme. Placer un point d'arrêt INT3 à l'OEP réel permet de capturer l'état entièrement restauré du segment de code du programme. La recherche de l'OEP correct est donc la priorité numéro un lors du dédosage manuel.
Table d'Importation (IAT)
L'IAT (Import Address Table) est une structure cruciale pour les exécutables PE. Elle cotnient les adresses des fonctions externes (importées) que le programme appelle et dont le code réside dans une ou plusieurs bibliothèques de liens dynamiques (DLL).
Lorsque le fichier PE est chargé en mémoire, le chargeur Windows charge également les DLL nécessaires et "lie" les appels aux fonctions importées aux adresses réelles de ces fonctions (liaison dynamique). C'est l'IAT qui gère cette résolution d'adresses. La plupart des empaqueteurs reconstruisent cette table pendant l'exécution, rendant l'obtention d'une IAT correcte après dédosage une étape clé.
Dump Mémoire
Le "dump" d'un programme consiste à extraire l'état d'un programme en cours d'exécution depuis la mémoire (notamment le code qui a été déchiffré ou décompressé dynamiquement) et à le sauvegarder dans un nouveau fichier exécutable.
Points d'Arrêt (Breakpoints)
Point d'arrêt INT3 : L'instruction assembleur INT3 a un opcode machine de 0xCC. Lorsqu'un processus débogué exécute cette instruction, une exception est générée (ou un signal SIGTRAP sur Linux). Le débogueur intercepte cette exception, provoquant l'arrêt du programme à cet endroit.
Méthodes de Dédosage
Suivi Pas à Pas (Single-Step Tracing)
En utilisant les fonctions de débogage pas à pas d'un outil comme OllyDbg (F8 pour le pas à pas simple, F7 pour le pas à pas détaillé, et F4 pour exécuter jusqu'à une adresse), il est possible de traverser l'ensemble du processus d'auto-dédosage du programme. Il faut sauter les boucles de restauration de code et utiliser le pas à pas détaillé pour s'assurer que l'OEP n'est pas ignoré. Une fois le module d'auto-dédosage terminé, le programme s'arrêtera à l'OEP, permettant de dumper le processus.
La Règle de l'ESP
Instruction CALL :
- Pousse l'adresse de l'instruction suivante sur la pile.
- Effectue un saut (JMP) à l'adresse de la sous-routine appelée.
Exemple :
00401029 E8 DA240A00 CALL 004A3508
0040102E 5A POP EDX
Après l'exécution de 00401029, l'adresse 0040102E est poussée sur la pile, puis le flux d'exécution saute à 004A3508.
Instruction RETN :
- Dépile la valeur pointée par
ESPet y effectue un saut (JMP).
La règle de l'ESP repose sur l'équilibre de la pile. De nombreux empaqueteurs, lors de la décompression ou du déchiffrement, sauvegardent l'état des registres en les poussant sur la pile (ex: pushad). Une fois l'opération terminée, ils restaurent ces registres en les dépilant (ex: popad). Lorsque les registres sont dépilés, le code du programme est souvent déjà restauré. Placer un point d'arrêt matériel sur la valeur de ESP juste après un pushad permet d'intercepter le moment où ESP revient à sa valeur initiale, souvent très proche de l'OEP. Un léger suivi pas à pas à partir de là mène généralement à l'OEP.
Points d'Arrêt Mémoire Doubles (Méthode des Miroirs Mémoire)
Cette méthode exploite la fonction d'affichage de la mémoire virtuelle d'un débogueur (par exemple, ALT+M dans OllyDbg). En plaçant des points d'arrêt uniques en mémoire sur les sections de ressources et de code du programme, on peut trouver l'OEP. Généralement, un programme auto-décompressant ou auto-déchiffrant accède d'abord à la section de ressources pour obtenir des données, puis, une fois le dédosage terminé, revient à la section de code. Un point d'arrêt sur la section de code à ce moment précis arrêtera le programme à l'OEP.
Recherche de Séquences Caractéristiques (One-Step to OEP)
Certains empaqueteurs simples ont des épilogues très proches de l'OEP, comme l'instruction popad ou un "Magic Jump" (un saut conditionnel ou inconditionnel menant directement à l'OEP). En utilisant la fonction de recherche de code assembleur d'un débogueur, on peut localiser ces séquences et placer un point d'arrêt directement, atteignant l'OEP en une seule étape.
Dernière Exception
Lors de son auto-décompression ou auto-déchiffrement, un programme peut déclencher de nombreuses exceptions. La méthode de la "dernière exception" consiste à localiser la dernière exception avant la fin du processus de dédosage. Des plugins de débogueur peuvent compter les exceptions, puis recharger le programme et s'arrêter automatiquement à la dernière exception, qui se trouve souvent très près de l'OEP.
Suivi de Simulation
Cette méthode utilise des points d'arrêt conditionnels dans un débogueur. Elle se base sur le fait que le segment SFX (Self-eXtracting) est un bloc d'auto-décompression. Lorsque le pointeur d'instruction (EIP) quitte le segment SFX pour entrer dans le segment de code du programme, l'OEP est proche. Bien qu'efficace, cette méthode peut être chronophage.
Recherche OEP Automatique (Méthode "SFX")
Certains débogueurs, comme OllyDbg, intègrent une fonction de recherche d'OEP spécifique aux exécutables SFX. En activant cette option et en rechargeant le programme, le débogueur peut s'arrêter directement à l'OEP trouvé, permettant un dump immédiat.
Extraction (Dump)
Plugin Scylla pour x64dbg
Pour dumper un processus dépaqueté avec Scylla (souvent intégré à x64dbg ou disponible comme plugin) :
- Cliquez sur "IAT Autosearch" pour détecter la table d'importation.
- Cliquez sur "Get Imports" pour charger les importations détectées.
- Supprimez toutes les entrées marquées d'une croix rouge (importations invalides).
- Cliquez sur "Dump" pour extraire le fichier dépaqueté.
- Enfin, cliquez sur "Fix Dump" et sélectionnez le fichier précédemment dumpé pour réparer l'IAT et générer le programme dépaqueté final.
Exemples de Dédosage
UPX
UPX est un compresseur/décompresseur d'exécutables open source.
Vérification de l'empaqueteur : Utilisez un outil comme PEiD ou Detect It Easy pour confirmer que le programme est empaqueté avec UPX. Chargez ensuite le programme dans un débogueur (ex: OllyDbg).
Dédosage Pas à Pas
Exécutez le programme pas à pas. Suivez les sauts vers le bas, et entrez dans les appels (F7). Si vous rencontrez des boucles de restauration, sautez-les. Par exemple, si vous voyez mov eax, 0x1, utilisez F4 pour exécuter jusqu'à cette ligne.
Si vous rencontrez un saut (jmp) ou un appel (call) dont la destination est elle-même un saut, exécutez jusqu'à l'instruction suivant le saut/appel final. Répétez jusqu'à rencontrer une séquence caractéristique, comme :
- Une instruction
popad(restauration des registres) suivie d'un grand saut (jmp) inconditionnel. Ce grand saut mène généralement à l'OEP.
Exemple de séquence après le saut :
00400000 + OEP_OFFSET // Si l'adresse de base est 00400000 et l'OEP 7738C, l'OEP est 0047738C
Une fois l'OEP identifié, vous pouvez dumper le processus (ex: clic droit -> "Dump debugged process" avec OllyDump).
Règle de l'ESP
Après l'instruction pushad, la valeur du registre ESP change. Notez cette valeur. Sélectionnez la valeur de ESP, puis faites un clic droit -> "Follow in Dump" pour la visualiser dans la fenêtre de dump. Vous pouvez aussi utiliser dd <adresse_ESP> ou hr <adresse_ESP> dans la ligne de commande du débogueur.
Placez un point d'arrêt matériel sur cette zone mémoire (clic droit -> "Breakpoint" -> "Hardware on access" -> "Word" ou "Dword"). Exécutez le programme. Il devrait s'arrêter juste avant le saut final vers l'OEP, souvent après un popad. Exécutez l'instruction de saut, et vous serez à l'OEP. Vous pouvez alors dumper le fichier.
Méthode des Miroirs Mémoire Doubles
Allez dans la vue mémoire (View -> Memory ou ALT+M). Repérez la première section .rsrc (ressources) et placez un point d'arrêt (F2). Exécutez le programme. Lorsque le progrmame s'arrête, il a généralement terminé de décompresser les ressources. Retournez à la vue mémoire, et placez un point d'arrêt sur la section de code (par exemple, .text ou .code). Exécutez à nouveau. Le programme devrait s'arrêter à l'OEP.
Recherche de Séquences Caractéristiques
Si le programme commence par un pushad, il y aura très probablement un popad à la fin du routine de décompression. Utilisez Ctrl+F (ou Ctrl+L pour rechercher l'occurrence suivante) pour rechercher l'opcode de popad. Placez un point d'arrêt sur cette instruction, exécutez, puis un pas à pas mènera à l'OEP.
Dédosage Automatique (UPX)
Pour UPX spécifiquement, il existe un outil de décompression officiel :
upx -d "nom_du_fichier"
ASPACK
Dédosage Pas à Pas
Chargez le programme dans un débogueur. Souvent, il faut utiliser F7 (pas à pas détaillé) pour entrer dans les fonctions de l'empaqueteur, surtout lors des premiers sauts (ex: jnz). Continuez le pas à pas jusqu'à voir la restauration du code. Parfois, plusieurs F8 peuvent être nécessaires après un saut pour atteindre l'OEP.
Règle de l'ESP
Appliquez la même méthode que pour UPX : repérez pushad, notez ESP, placez un point d'arrêt matériel sur la zone mémoire pointée par ESP. Une fois que le programme s'arrête, le popad et le saut vers l'OEP sont souvent juste après. N'oubliez pas de supprimer les points d'arrêt matériels après usage (Debug -> Hardware breakpoints -> Delete).
Recherche de Séquences Caractéristiques
Après une première instruction (F8), recherchez popad (Ctrl+F, puis Ctrl+L pour les occurrences suivantes). Placez un point d'arrêt à cet endroit et exécutez jusqu'à lui pour atteindre rapidement la zone de l'OEP.
Méthode des Miroirs Mémoire Doubles
Allez dans la vue mémoire (View -> Memory). Placez un point d'arrêt F2 sur la première section .rsrc. Exécutez. Une fois arrêté, revenez à la vue mémoire et placez un point d'arrêt sur la section de code principale. Exécutez, et un court pas à pas devrait vous mener au popad et à l'OEP.
Suivi de Simulation
Allez dans la vue mémoire. Sélectionnez une adresse à partir de laquelle vous souhaitez commencer le suivi et utilisez la fonction de suivi. Ensuite, quelques F8 peuvent révéler l'OEP.
Recherche OEP SFX
Dans OllyDbg, activez l'option de recherche d'OEP SFX (Options -> Debug options -> SFX). Rechargez le programme, et il devrait s'arrêter directement à l'OEP détecté.
Nspack
Méthode des Miroirs Mémoire Doubles
Nspack n'a souvent pas de section .rsrc distincte avant le code. Dans ce cas, allez directement à la vue mémoire et placez votre premier point d'arrêt sur la section de code (ex: .text ou .code). Exécutez, puis après l'arrêt, effectuez un suivi pas à pas pour trouver l'OEP.