Accueil > > > [TUTO]PRISE EN MAIN ET CRÉATION DE .EXE À L'AIDE D'UN DÉBUGUEUR 32BITS FOURNIT AVEC WINDOWS
[TUTO]PRISE EN MAIN ET CRÉATION DE .EXE À L'AIDE D'UN DÉBUGUEUR 32BITS FOURNIT AVEC WINDOWS
Information sur la source
Description
#### Prérequis : Windows XP ou 2000. Non testé sous Vista. notepad.exe, dans la même version que moi (Fournie dans le zip). Aucun logiciel, même pas un assembleur, n'est nécessaire. Des connaissances en assembleur, niveau très débutant, sont requises (Connaître call, push, lea, mov xor...) De la patience. Evidemment, je décline toute responsabilité en cas de problème. #### Objectifs de ce tutorial : 1 Faire de l'assembleur 32 bits et des .exe sur un PC sans installer de logiciels. 2 Apprendre à se servir d'un débogueur ring 3 de Microsoft. 3 Apprendre à récupérer des informations sur un executable, et mieux connaître le format PE. Et surtout, se faire plaisir en discutant avec le processeur !!
Source
-
- #### Introduction :
-
- Vous partez en vacance à la campagne chez tante Martine.
-
- Tante Martine a un PC sous XP qui lui sert de machine à écrire, mais n'a pas le net.
-
- Vous prenez donc vos précautions en mettant votre environement de développement préféré sur votre clé USB, histoire de pouvoir l'installer pèpère chez elle.
-
- Manque de chance, à peine arrivé, vous perdez votre clé USB en batifolant dans un champ.
-
- Le soir, désespéré devant le PC de tante Martine, vous vous demandez ce que vous allez bien pouvoir faire...
-
- Un PC sortie d'usine ne propose que peu d'options pour faire du développement.
-
- On peut faire de l'assembleur DOS 16 bits avec debug. On est coincé dans une console, et on ne peut utiliser que les interruption DOS et BIOS, dont vous avez forcément oublié les numéros (Et oui, il n'y a pas le net chez tantine).
-
- On peut faire du DOS. Cette fois, la liste des commande (Plus complète que celle de help) est présente dans l'aide et support, et les commandes s'autodocumentent avec /?. Manque de chance vous connaissez déjà bien le DOS, et vous ne voyez pas grand chose d'intéressant à faire avec...
-
- Le navigateur est capable de dessiner des pages web à partir de code HTML. Mais vous vous êtes toujours dit qu'appelez le HTML du code, c'était un peu exagéré.
-
- On peut faire des scripts VB et JS, qui seront executés par wscript, cscript, ou encore les mêler à de l'HTML pour les exécuter sous IE. Si office est présent on peut faire aussi du VBA. Mais le problème c'est que ces langages vous donnent de l'urticaire (Et vi, ce tuto est pas sur usineàgazfr.com).
-
- Ne commencez pas une partie de démineur : il reste une option !
-
- Le vieux Bill a en effet décidé de nous livrer en standard sous XP un débugueur 32 bits. Vivi, même à tante Martine.
-
- Celui-ci répond au doux nom de ntsd.exe et se trouve fort peu originalement dans system32.
-
-
- #### Présentation de ntsd :
-
- Pour ceux qui connaissent WinDbg, la prise en main de ntsd ne posera pas de problème. ntsd est comme WinDbg à plusieurs différences près :
-
- 1 Il ne permet que de déboguer en ring 3.
- 2 Il est en invite de commande.
- 3 Des dlls d'extension proposant des commandes supplémentaires fournies avec WinDbg ne sont pas présentes en standard.
- 4 Il lui arrive de planter si on fait une faute de frappe...
- 5 Une bonne partie des commandes des extensions fonctionnent mal ou plantent.
- 6 Il vaut mieux oublier les pseudo registres ($ra, $ea, $exp...).
-
- Les débogueurs Microsoft (WinDbg, kd, ntsd, cdb...) se différencient de certains débugueurs/désassembleurs classiques (IDA, TD32, W32DASM...) par le fait qu'ils se manipulent en entrant des commandes en invite, comme avec le bon vieux debug.exe. Ils proposent toutes les fonctionnalités nécessaires en matière de pas à pas, points d'arrêts... mais ne propose pas forcément autant d'outils d'extraction d'information. Par exemple, IDA est capable d'éssayer de déterminer quel compilo à généré un exe, ou W32DASM affiche les tables d'import et d'export directement.
-
-
- #### Démarrage de ntsd :
-
- Si on lance ntsd sans lui passer de paramètre, il affiche son aide et se ferme.
-
- Voici quelques possibilités de lancement :
-
- 1 On peut passer un fichier .exe en paramètre de ntsd pour qu'il démarre son débogage.
- 2 On peut s'attacher à un processus existant, via son pid et l'option -p.
- 3 On peut s'attacher à un processus existant, via son nom de module, mais il faut qu'il y en ai un seul qui ait ce nom. Exemple :
- ntsd -pn notepad.exe
- 4 On peut le mettre en remplacement du docteur Watson, pour qu'il s'attache automatiquement à un processus qui plante.
- Pour cela, il faut changer la clé :
- HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
- La valeur auto permet de définir la présence d'une MessageBox qui demande si on veut déboguer le processus.
- Dans la valeur Debugger, on peut par exemple mettre :
- ntsd -p %ld -g
-
-
- #### Prise en main :
-
- ? permet d'afficher une partie des commandes, et .help une autre partie.
-
- Au début, le débugueur affiche des erreurs ou des warnings en parlant de symboles. Les symboles de débogages facilitent celui-ci. Cependant, on ne dispose généralement pas des symboles d'une application que l'on a pas compilé.
-
- Quand le débugueur laisse tourner le processus (On ne peut plus rentrer de commandes et le processus à un fonctionnement normal), il faut utiliser ctrl + c.
-
- q permet de quitter ntsd, et tuera l'application débuguée.
-
- Alt + entrée est le raccourcis qui permet de mettre une application DOS en mode plein écran. Mais je n'ai pas trouver d'instruction more.
-
- ctrl + b permet de quitter plus violemment que que q.
-
- Pour se détacher d'une application sans la tuer, il faut commencer par faire .detach, puis q.
-
- .restart permet de redémarrer l'application, mais ne fonctionne pas si on s'est attaché à un processus.
-
- Le processus peut avoir plusieurs threads. On affiche la liste des threads avec ~*.
-
- On peut executer une commande sur un seul thread en la préfixant :
- ~NumeroThreadCommande
-
- Un ? suivie d'une expression provoque l'évaluation de celle-ci.
-
- echap permet d'effacer la ligne.
-
- Les registres doivent parfois être préfixés par @.
-
-
- #### S'attacher à un processus en cours de fonctionnement :
-
- On a vu que l'on pouvait utiliser l'option -pn.
-
- ntsd crée alors un thread pour mettre un point d'arrêt.
- On met alors souvent en place un point d'arrêt sur une fonction de l'API Win32 probablement utilisée dans le code que l'on veut voire.
- Par exemple :
- bp BitBlt
- Puis on utilise la commande g pour reprendre l'execution.
-
- Si la fonction est appelée par le programme, celui-ci va être arrêter par le point d'arrêt.
- On se trouve alors au tout début de la fonction BitBlt.
- On peut examiner les paramètres passés à cette fonction de Windows, mais son code nous intéresse moins que le code de l'appelant.
-
- L'adresse de l'appelant est empilée sur la pile.
- Pour faire un step out il faut donc faire (La commande gu et le pseudo registre $ra de WinDbg semblent mal supportés par ntsd) :
- g poi(esp)
-
- poi() permet de déférencer un pointeur.
-
-
- #### Les dlls d'extension :
-
- Ces dlls permettent de rajouter des commandes à ntsd.
- Ces commandes commencent par un ! et certaines d'entres-elles fonctionnent mal sous ntsd.
-
- Pour afficher la liste des dlls dans lesquelles ntsd cherche les commandes, on utilise la commande .chain.
- Elle affiche le chemin de recherche des dlls, ainsi que les dlls chargées et non chargées.
-
- Les dlls non chargées peuvent provoquer l'affichage de messages d'erreurs génants. On peut les décharger avec la commande .unload.
-
- Les dlls présentes en standard sur mon XP sont :
- dbghelp.dll
- exts.dll
- ntsdexts.dll
-
- Vous pouvez charger une dll d'extension avec la commande .load.
-
- Pour lister les commandes d'une dll, on utilise la syntaxe suivante :
-
- !module.help
-
- Elle ne fonctionne pas sur dbghelp.dll.
-
-
- #### Format PE et loader de windows :
-
- Chez tantine, on souhaite pouvoir rédiger et tester du code assembleur, et pourquoi pas s'arranger pour obtenir des .exe valides.
-
- Un .exe est au format PE. Il est composé de headers et de sections. Le loader de windows charge les headers dans la mémoire à une certaine adresse qui peut théoriquement être arbitraire. En réalité, la plupart des .exe exigent d'être chargés à une adresse bien précise (Les dlls ont par contre une base de relocation qui leurs permet d'être chargées à n'importe quelle adresse). Les headers précisent à quelles adresses doivent être chargées les sections, relativement à l'adresse des headers (L'adresse du module quoi).
-
- Les PE disposent d'une table d'importation qui définit quelles fonctions de l'API Win32 sont nécessaires à l'application. Le loader de Windows complète cette table avec les adresses qui vont bien lors du chargement de l'exe. Dans le code machine, lors de l'appel d'une fonction de l'API, le call récupère l'adresse dans cette table. Il connait l'adresse de cette table car elle se trouve toujours au même endroit, à un certains offset depuis l'adresse du module qui elle même est fixe.
-
- La construction d'un exe valide au format PE à la main et sans documentation est du pur suicide. Il est bien plus simple d'utiliser un .exe éjà existant sur le PC de tantine.
-
- On souhaite pouvoir executer n'importe quelle fonction de l'API Win32... Il faut donc disposer des fonctions LoadLibrary et GetProcAddress. notepad.exe importe ces deux fonctions, et servirat donc de base de travail.
-
-
- #### Récupération des informations sur un executable :
-
- Voyons voir ce que notepad a dans le ventre :
- ntsd notepad.exe
-
- ntsd charge le programme et s'arrête sur un point d'arrêt.
-
- La commande lm affiche les modules (.dll, .exe, .ocx...) chargés par l'appli, ainsi que leurs adresses de début et fin.
- Notons l'adresse du module notepad : 01000000.
-
- La command !lmi, suivie d'un nom de module, donne un peu plus d'informations sur celui-ci.
-
- Mais la commande !dh est nettement plus efficace : elle affiche tout un tas d'infos sur les headers et les sections.
-
- On remarquera par exemple dans les caractéristiques "Relocations stripped", qui signifie que ce module ne peut être chargée qu'à une seule adresse.
-
- On peut vérifier que le champ "Image Base" a bien la valeure qu'on a noté tout à l'heure : 01000000.
-
- On prend note du point d'entrée (entry point), 739D.
-
- Sous le "optional header", il y a les data directories. Ce sont des pointeurs vers des données particulières, elles même se trouvant généralement dans les sections. Ces données sont par exemple les tables d'import et d'export, les tables de relocation qui permettent aux dlls d'être chargées à n'importe quelle adresse, les ressources...
-
- Notons l'adresse de la table d'importation, 7604.
-
- Finalement, on a les informations sur les sections.
-
- notepad à 3 sections :
-
- 1 .text : Contient le code. Dans le fichier, cette section se trouve à l'adresse 400 à partir du bébut de fichier, pour une taille de
- 7800 octets (Informations "raw data"). Dans la mémoire elle se trouve à l'adresse du module (01000000) + 1000, pour une taille de 7748 (Informations "virtual") On remarque que le point d'entrée et la table d'importation sont dans cette section.
-
- 2 .data : Contient les données initialisées ou non. On constate que la taille dans le fichier (raw data) est très inferieure à la taille dans la mémoire (virtual). Les données non initialisée seront placés dans cet espace supplémentaire. Les données initialisées ou non sont par exemple les variables globales.
-
- 3 .rsrc : Les ressources. Menu, bitmap, chaînes...
-
- Jettons un coup d'oeil à la table d'importation. Elle se trouve en 01000000 + 7604. Pour l'afficher, on utilise la commande db. db prend une adresse ou une plage.
- db notepad + 7604 L1000 affiche les 0x1000 premiers octets de la table.
- Cela affiche les noms des fonctions importées, même si le formatage n'est pas terrible...
-
- On peut préciser la base d'un nombre en le préfixant :
-
- 0x prefix (hexadecimal)
- 0n prefix (decimal)
- 0t prefix (octal)
- 0y prefix (binary)
-
- Les commandes classiques d'affichage du contenu de la mémoire sont :
-
- dd : dword
- da : ASCII
- du : Unicode
- db : byte
- dw : 16 bits
- dq : 64 bits
- dyd : Binaire
-
- Toujours concernant la récupération d'informations sur les modules, on peut récupérer la liste des fonctions exportées par une dll. Par exemple :
- x kernel32!Get*
-
- Affiche toutes les fonctions exportées par kernel32 qui commencent par Get.
-
- Certaines API sont en deux versions, une suffixée A pour l'ASCII, une autre suffixée W pour l'Unicode.
- Pour se simplifier la vie, on peut utiliser la commande ss, suivie de w(Unicode), a(ASCII) ou n(Aucun).
- De cette manière, lorsque l'on entre un symbole que ntsd ne trouve pas, il ajoute le suffixe au symbole, et recommence la recherche.
-
- La commande ln nous donne les symboles les plus proches d'une adresse. Dans notre cas où l'on a pas de symboles autres que les fonctions exportées, cela nous permet de savoir à quelle fonction appartient une adresse facilement.
-
- Enfin, la commande !handle liste les handles sur des objets systèmes et leurs types.
-
-
- #### Détourner le comportement d'un executable :
-
- A présent, on se propose de modifier notepad pendant son execution.
-
- On commence par repartir sur une base propre :
- ntsd notepad.exe
-
- On se souvient de l'adresse du point d'entré : c'est là que l'on va assembler notre code.
-
- Jettons un coup d'oeil au code actuel :
- u notepad + 739D
-
- On va appeler MessageBoxA, puis ExitProcess.
-
- On va commencer par mettre deux chaînes dans la mémoire, une pour le contenu de la MessageBox, une autre pour le titre.
-
- Ou mettre ces chaînes ?
- On a beaucoup de place, mais ce n'est pas une raison pour écraser quelque-chose.
- On va donc écrire dans les données non initialisées.
- !dh notepad
-
- Section .data.
-
- 1BA8 virtual size
- 9000 virtual address
- 800 size of raw data
- 7C00 file pointer to raw data
-
- On ecrira donc en notepad + 9000 + 800.
-
- Les commandes pour écrire dans la mémoire sont les suivantes :
-
- ea : Ecrire une chaîne de caractère dans la mémoire, sans caractère terminal.
- eb : Ecrire des octets dans la mémoire.
- ew, ed, eq : Ecrire de la taille spécifiée dans la mémoire.
-
- La commande d'écriture de chaînes à zéro terminal eza de WinDbg n'est pas utilisable... On écrit donc :
- eb notepad + 9800 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
-
- on vérifie :
- db notepad + 9800
-
- On écrit le titre un peu plus loin :
- eb notepad + 9810 'Y' 'e' 'a' 'h' '!' 0
-
- A présent, il ne reste plus qu'à coder !! (Enfin !)
- Pour assembler, c'est a :
- a notepad + 739D
-
- Pour arrêter d'assembler, il suffit de ne rien entrer sur la ligne et de presser entrée.
-
- Il faut pousser les arguments de MessageBoxA, de la droite vers la gauche :
-
- xor @eax, @eax
- push @eax
- lea @ebx, [notepad + 9810]
- push @ebx
- lea @ebx, [notepad + 9800]
- push @ebx
- push @eax
- call MessageBoxA
- xor @eax, @eax
- push @eax
- call ExitProcess
-
- On désassemble pour vérifier :
- u notepad + 739D
-
- Et go ! :
- g
-
-
- #### Travailler depuis un fichier :
-
- Bon, le code que l'on a écrit, on préfèrerait le conserver...
- Avec debug, on pouvait envoyer des commandes contenues dans un fichier avec une redirection <, ou encore en utilisant un pipe et la commande type.
- Avec ntsd, on peut utiliser l'option -c et la commande $< comme je l'ai fait dans le zip.
-
- Si on souhaite mettre des lignes vides dans le fichier en question, il faut mettre celles-ci en commentaire, car une pression sur entrée demande à ntsd de réexecuter la dernière commande. Pour mettre en commentaire une ligne, on utilise $. On garderat la commande * pour mettre des vrais commentaires. Cela peut nous permettre de faciliter l'imortation du code dans un assembleur classique. Ces caractères provoqueront une erreur mineure au milieu du code (Par contre cela ne facilite pas vérification que l'assemblage s'est bien passé), lors de l'assemblage, sans poser plus de problèmes.
-
- ===============================
- eb notepad + 9800 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
- eb notepad + 9810 'Y' 'e' 'a' 'h' '!' 0
- $
- a notepad + 739D
- $
- xor @eax, @eax
- push @eax
- lea @ebx, [notepad + 9810]
- push @ebx
- lea @ebx, [notepad + 9800]
- push @ebx
- push @eax
- call MessageBoxA
- xor @eax, @eax
- push @eax
- call ExitProcess
-
- $
- g
- q
-
- ===============================
-
- On copie ça, on lance ntsd notepad.exe, puis clique gauche sur l'icône en haut à gauche de la console, coller.
-
-
- #### Principe de la sauvegarde de l'exe sur le disque :
-
- Devoir faire un copier coller lors du développement, c'est déjà bof, mais alors être obligé de le faire sur l'application finale, ce serait horrible. Qui plus est, si on pouvait éviter de réassembler l'application à chaque lancement, ce ne serait pas plus mal...
-
- On va donc écrire notre notepad depuis la RAM vers le disque.
-
- Le programme de compilation sera placé dans les données non initialisées pour pouvoir disparaître.
- Les chaînes de notre application vont devoir se trouver dans les données initialisées.
- Notre code devra se trouver dans la section .text.
-
- On exploiterat donc les zones suivantes :
-
- Données non initialisées :
- 9800 -> ABA8
-
- Données initialisées (2 milles caractères) :
- 9000 -> 9800
-
- Code :
- 3000 -> 7604
-
- Le point d'entrée est en 739D. La valeur de 3000 comme début du code est arbitraire mais assure une bonne marge de sécurité tout en nous donnant bien assez de place. Le point d'entrée en 739D était un peu près de la table d'import en 7604. On va donc faire un saut à l'adresse 3000 dès le début du programme.
-
- Le code de compilation est théoriquement simple : il faut claquer la mémoire dans un fichier. Mais pas n'importe comment car il faut penser aux section.
-
- L'écriture se fait en 4 phase :
- 1 Copie des headers et des bits qui le suive (On dirait que la Bound Import Table n'est pas dans une section...) :
- raw data : 0 -> 400
- virtual : 0
- taille : 400
- 2 Copie de la section .text (On remplit jusqu'à tomber sur l'offset de la section .data :
- raw data : 400 -> 7C00
- virtual : 1000
- taille : 7800
- 3 Copie de la section .data
- raw data : 7C00 -> 8400
- virtual : 9000
- taille : 800
- 4 Copie de la section .rsrc
- raw data : 8400 -> 11400
- virtual : B000
- taille : 9000
-
- 0x11400 = 70656 octets = la taille de notepad.exe !
-
-
- #### L'utilisation des fonctions de l'API Win32 :
-
- Les fonctions de l'API Win32 sont dans des dlls, et sont donc suceptibles de ne pas être chargées aux mêmes adresse d'une execution sur l'autre. En fait si, on fait des éssais, on s'aperçoit que les principale dlls (kernel32...) sont toujours chargées au même endroit.
-
- Mais essayons de travailler proprement.
- On sait que le loader va mettre les adresses de LoadLibrary et GetProcAddress à une adresse précise en mémoire car ces fonctions sont dans la table d'import de notepad.
-
- La première étape consiste à déterminer les emplacement de ses adresses.
- On pourrait décrypter la table d'import. Mais on va plutôt laisser faire le travail à ntsd.
-
- La dernière section est à l'adresse virtuelle notepad + B000, avec une taille qu'on arrondit à 9000.
- On va donc bourriner en cherchant de notepad à notepad + 14000.
-
-
- La commande s permet de rechercher dans une plage mémoire :
- s -b : 8 bits
- s -w : 16 bits
- s -d : 32 bits
- s -q : 64 bits
- s -a : ASCII
- s -u : Unicode
-
- Les commandes sa et su permettent de cherher des chaînes repectivement ASCII et Unicode sans préciser de pattern.
-
- s -d notepad L14000 LoadLibraryA
- s -d notepad L14000 GetProcAddress
-
- Les résultats nous donnent une adresse et une série de valeur. On peut vérifier que c'est bien la première valeur en évaluant le symbole :
- ? LoadLibraryA
-
- On obtient :
- LoadLibraryA -> 10c8
- GetProcAddress -> 1110
-
- On peut essayer de vérifier tout ça en utilisant une fonction intéressante de ntsd. On va placer un point d'arrêt qui ne va pas se déclencher lorsque eip pointera dessus, mais lorsque qu'un accès mémoire sera effectué à cette adresse. ntsd peut refuse la mise en place de ce type de breakpoint lorsque l'on est pas passé par l'entry point, ce qui arrive lorsque l'on lance ntsd en même temps que le processus (Il suffit alors de faire tourner celui-ci puis de le stopper avec ctrl+c), ou, plus grave, lorsque l'on s'attache à un processus.
-
- Dans notre cas, lOadLibraryA est suceptible de n'être appeler qu'en début de prorgamme, on va donc mettre un point d'arrêt sur l'entry point.
-
- ntsd notepad.exe
- bp notepad + 739D
- g
- ba r4 notepad + 10c8
- ba r4 notepad + 1110
-
- Ces breaks point seront déclenchés lors de la lecture ou écriture de 4 octets à l'adresse spécifiée.
- bl liste les breakpoints, et bc permet d'en supprimer un à partir de son id.
-
- Pas de chance, LoadLibrary est appelée avant l'entry point (On voit bien les ModLoad)...
- Mais on va quand même se stopper sur un GetProcAddress.
-
-
- #### Le code de l'application :
-
- L'application serat identique à la précédente, à trois différences près :
- 1 MessageBox et ExitProcess seront appelés quelles que soient leurs adresses.
- 2 Les chaînes seront stockées dans la mémoire initialisée de manière à être sauvegardée sur le disque quand on compilera.
- 3 On mettra seulement un saut vers notre code à l'entry point.
-
- ========================================
- * Mise en place du saut au niveau de l'entry point
- a notepad + 739D
- jmp notepad + 3000
-
- * Mise en place des chaines dans la mémoire
- eb notepad + 9000 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
- eb notepad + 9010 'Y' 'e' 'a' 'h' '!' 0
- eb notepad + 9020 'k' 'e' 'r' 'n' 'e' 'l' '3' '2' 0
- eb notepad + 9030 'u' 's' 'e' 'r' '3' '2' 0
- eb notepad + 9040 'M' 'e' 's' 's' 'a' 'g' 'e' 'B' 'o' 'x' 'A' 0
- eb notepad + 9050 'E' 'x' 'i' 't' 'P' 'r' 'o' 'c' 'e' 's' 's' 0
- $
- a notepad + 3000
- $
- * Récupération d'un handle sur kernel32
- lea @ebx, [notepad + 9020]
- push @ebx
- call dword ptr [notepad + 10c8]
- mov @edi, @eax
- $
- * Récupération d'un handle sur user32
- lea @ebx, [notepad + 9030]
- push @ebx
- call dword ptr [notepad + 10c8]
- mov @esi, @eax
- $
- * Récupération de l'adresse de MessageBoxA
- lea @ebx, [notepad + 9040]
- push @ebx
- push @esi
- call dword ptr [notepad + 1110]
- mov @esi, @eax
- $
- * Récupération de l'adresse de ExitProcess
- lea @ebx, [notepad + 9050]
- push @ebx
- push @edi
- call dword ptr [notepad + 1110]
- mov @edi, @eax
- $
- * Execution du programme
- xor @eax, @eax
- push @eax
- lea @ebx, [notepad + 9010]
- push @ebx
- lea @ebx, [notepad + 9000]
- push @ebx
- push @eax
- call @esi
- xor @eax, @eax
- push @eax
- call @edi
-
-
- ========================================
-
- Comme on est dans un débogueur, il ne faut pas hésiter à utiliser t (Step into) et p (step over) pour vérifier le comportement du programme.
- La commande ln de recherche du symbole le plus proche, et db, d'affichage de données, nous permettent de contrôler que l'on tape bien au bon endroit.
-
- On peut afficher les registres avec r, et les affecter de la manière suivante :
- r eip = notepad + 3000
-
- Ne pas oublier aussi .restart qui permet de redémarrer le programme sans quitter ntsd.
-
- #### Le code de compilation :
-
- Le code de compilation est par définition executé dans ntsd. Il n'y a donc pas besoin de prendre des précautions au niveau de l'emploi des API Win32.
-
- Chez tante Martine, l'absence de documentation est génante, tout spécialement lorsque l'on emploie des structures, dont on a oublié les champs, ou lorsque l'on emploie des constantes, dont on connait plus les identifiants que les valeurs. Une solution qui peut aider consiste à mettre des points d'arrêts sur ces fonctions, de manière à déterminer les paramètres passés à celles-ci.
-
- ========================================
- * Le nom de l'exe que l'on va créer
- eb notepad + 9800 'c' ':' '\\' 'T' 'e' 's' 't' '.' 'e' 'x' 'e' 0
- $
- a notepad + A000
- $
- * Ouverture du fichier
- xor @eax, @eax
- push @eax
- * FILE_ATTRIBUTE_NORMAL
- mov @ebx, 80
- push @ebx
- * CREATE_ALWAYS
- mov @ebx, 2
- push @ebx
- push @eax
- push @eax
- * GENERIC_WRITE
- mov @ebx, 40000000
- push @ebx
- lea @ebx, [notepad + 9800]
- push @ebx
- call CreateFileA
- mov @edi, @eax
- $
- * Ecriture des headers et sections
- $
- xor @eax, @eax
- push @eax
- lea @eax, [notepad + 9810]
- push @eax
- mov @eax, 0x400
- push @eax
- lea @eax, [notepad]
- push @eax
- push @edi
- call WriteFile
- $
- xor @eax, @eax
- push @eax
- lea @eax, [notepad + 9810]
- push @eax
- mov @eax, 0x7800
- push @eax
- lea @eax, [notepad + 0x1000]
- push @eax
- push @edi
- call WriteFile
- $
- xor @eax, @eax
- push @eax
- lea @eax, [notepad + 9810]
- push @eax
- mov @eax, 0x800
- push @eax
- lea @eax, [notepad + 0x9000]
- push @eax
- push @edi
- call WriteFile
- $
- xor @eax, @eax
- push @eax
- lea @eax, [notepad + 9810]
- push @eax
- mov @eax, 0x9000
- push @eax
- lea @eax, [notepad + 0xB000]
- push @eax
- push @edi
- call WriteFile
- $
- * Fermeture du fichier
- $
- push @edi
- call CloseHandle
- nop
-
- r eip = notepad + A000
- bp CloseHandle
- g
- g poi(esp)
- q
-
- ========================================
-
- C'est aussi simple que cela : la copie successive des deux codes précédents dans une console crée avec ntsd notepad.exe génère un executable nommé Test.exe, à la racine de C:\. Cette executable a la même taille que notepad.exe (70 656 octets), mais affiche simplement une MessageBox.
-
-
- #### Conclusion :
-
- On est tout à fait en mesure de faire des .exe valides, codés en assembleurs, chez tantine. La seule condition nécessaire (Qui est malheureusement de taille...) est de très bien connaître les fonctions, structures et valeurs des constantes de l'API Win32.
-
-
- #### Commandes diverses :
-
- ctrl + v : Active/désactive le mode verbose (Affichage d'informations supplémentaires).
- .time : Affiche le temps depuis le démarrage du système et depuis le débogage.
- .tlist : Liste les processus du système.
- .imgscan : Cherche les images (MZ).
- wt : Trace une fonction, et affiche les fonctions appelées.
- pa : Step jusqu'à une adresse, avec affichage d'infos.
- .shell : Execution d'une commande DOS.
- .echo : Même comportement que la echo du DOS.
- !str : Affichage d'une chaîne ASCII ou OEM (La chaîne doit être précédée par sa taille et sa taille max).
- !ustr : Affichage d'une chaîne Unicode (La chaîne doit être précédée par sa taille et sa taille max).
-
-
- #### Commandes pouvant être utiles lorsque le débogueur s'attache à un processus en perdition :
-
- .kframes : Spécification de la profondeur d'inspection de la pile d'appel.
- .lastevent : Affiche des infos sur le dernier évènement ou exception.
- !error : Affiche la description d'une erreur Win32 à partir de son numéro.
- !gle : Dernières erreurs Win32 et NTDLL.
- .cxr : affiche les infos de contexte.
- .ecxr : affiche les infos de contexte de l'exception.
#### Introduction :
Vous partez en vacance à la campagne chez tante Martine.
Tante Martine a un PC sous XP qui lui sert de machine à écrire, mais n'a pas le net.
Vous prenez donc vos précautions en mettant votre environement de développement préféré sur votre clé USB, histoire de pouvoir l'installer pèpère chez elle.
Manque de chance, à peine arrivé, vous perdez votre clé USB en batifolant dans un champ.
Le soir, désespéré devant le PC de tante Martine, vous vous demandez ce que vous allez bien pouvoir faire...
Un PC sortie d'usine ne propose que peu d'options pour faire du développement.
On peut faire de l'assembleur DOS 16 bits avec debug. On est coincé dans une console, et on ne peut utiliser que les interruption DOS et BIOS, dont vous avez forcément oublié les numéros (Et oui, il n'y a pas le net chez tantine).
On peut faire du DOS. Cette fois, la liste des commande (Plus complète que celle de help) est présente dans l'aide et support, et les commandes s'autodocumentent avec /?. Manque de chance vous connaissez déjà bien le DOS, et vous ne voyez pas grand chose d'intéressant à faire avec...
Le navigateur est capable de dessiner des pages web à partir de code HTML. Mais vous vous êtes toujours dit qu'appelez le HTML du code, c'était un peu exagéré.
On peut faire des scripts VB et JS, qui seront executés par wscript, cscript, ou encore les mêler à de l'HTML pour les exécuter sous IE. Si office est présent on peut faire aussi du VBA. Mais le problème c'est que ces langages vous donnent de l'urticaire (Et vi, ce tuto est pas sur usineàgazfr.com).
Ne commencez pas une partie de démineur : il reste une option !
Le vieux Bill a en effet décidé de nous livrer en standard sous XP un débugueur 32 bits. Vivi, même à tante Martine.
Celui-ci répond au doux nom de ntsd.exe et se trouve fort peu originalement dans system32.
#### Présentation de ntsd :
Pour ceux qui connaissent WinDbg, la prise en main de ntsd ne posera pas de problème. ntsd est comme WinDbg à plusieurs différences près :
1 Il ne permet que de déboguer en ring 3.
2 Il est en invite de commande.
3 Des dlls d'extension proposant des commandes supplémentaires fournies avec WinDbg ne sont pas présentes en standard.
4 Il lui arrive de planter si on fait une faute de frappe...
5 Une bonne partie des commandes des extensions fonctionnent mal ou plantent.
6 Il vaut mieux oublier les pseudo registres ($ra, $ea, $exp...).
Les débogueurs Microsoft (WinDbg, kd, ntsd, cdb...) se différencient de certains débugueurs/désassembleurs classiques (IDA, TD32, W32DASM...) par le fait qu'ils se manipulent en entrant des commandes en invite, comme avec le bon vieux debug.exe. Ils proposent toutes les fonctionnalités nécessaires en matière de pas à pas, points d'arrêts... mais ne propose pas forcément autant d'outils d'extraction d'information. Par exemple, IDA est capable d'éssayer de déterminer quel compilo à généré un exe, ou W32DASM affiche les tables d'import et d'export directement.
#### Démarrage de ntsd :
Si on lance ntsd sans lui passer de paramètre, il affiche son aide et se ferme.
Voici quelques possibilités de lancement :
1 On peut passer un fichier .exe en paramètre de ntsd pour qu'il démarre son débogage.
2 On peut s'attacher à un processus existant, via son pid et l'option -p.
3 On peut s'attacher à un processus existant, via son nom de module, mais il faut qu'il y en ai un seul qui ait ce nom. Exemple :
ntsd -pn notepad.exe
4 On peut le mettre en remplacement du docteur Watson, pour qu'il s'attache automatiquement à un processus qui plante.
Pour cela, il faut changer la clé :
HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
La valeur auto permet de définir la présence d'une MessageBox qui demande si on veut déboguer le processus.
Dans la valeur Debugger, on peut par exemple mettre :
ntsd -p %ld -g
#### Prise en main :
? permet d'afficher une partie des commandes, et .help une autre partie.
Au début, le débugueur affiche des erreurs ou des warnings en parlant de symboles. Les symboles de débogages facilitent celui-ci. Cependant, on ne dispose généralement pas des symboles d'une application que l'on a pas compilé.
Quand le débugueur laisse tourner le processus (On ne peut plus rentrer de commandes et le processus à un fonctionnement normal), il faut utiliser ctrl + c.
q permet de quitter ntsd, et tuera l'application débuguée.
Alt + entrée est le raccourcis qui permet de mettre une application DOS en mode plein écran. Mais je n'ai pas trouver d'instruction more.
ctrl + b permet de quitter plus violemment que que q.
Pour se détacher d'une application sans la tuer, il faut commencer par faire .detach, puis q.
.restart permet de redémarrer l'application, mais ne fonctionne pas si on s'est attaché à un processus.
Le processus peut avoir plusieurs threads. On affiche la liste des threads avec ~*.
On peut executer une commande sur un seul thread en la préfixant :
~NumeroThreadCommande
Un ? suivie d'une expression provoque l'évaluation de celle-ci.
echap permet d'effacer la ligne.
Les registres doivent parfois être préfixés par @.
#### S'attacher à un processus en cours de fonctionnement :
On a vu que l'on pouvait utiliser l'option -pn.
ntsd crée alors un thread pour mettre un point d'arrêt.
On met alors souvent en place un point d'arrêt sur une fonction de l'API Win32 probablement utilisée dans le code que l'on veut voire.
Par exemple :
bp BitBlt
Puis on utilise la commande g pour reprendre l'execution.
Si la fonction est appelée par le programme, celui-ci va être arrêter par le point d'arrêt.
On se trouve alors au tout début de la fonction BitBlt.
On peut examiner les paramètres passés à cette fonction de Windows, mais son code nous intéresse moins que le code de l'appelant.
L'adresse de l'appelant est empilée sur la pile.
Pour faire un step out il faut donc faire (La commande gu et le pseudo registre $ra de WinDbg semblent mal supportés par ntsd) :
g poi(esp)
poi() permet de déférencer un pointeur.
#### Les dlls d'extension :
Ces dlls permettent de rajouter des commandes à ntsd.
Ces commandes commencent par un ! et certaines d'entres-elles fonctionnent mal sous ntsd.
Pour afficher la liste des dlls dans lesquelles ntsd cherche les commandes, on utilise la commande .chain.
Elle affiche le chemin de recherche des dlls, ainsi que les dlls chargées et non chargées.
Les dlls non chargées peuvent provoquer l'affichage de messages d'erreurs génants. On peut les décharger avec la commande .unload.
Les dlls présentes en standard sur mon XP sont :
dbghelp.dll
exts.dll
ntsdexts.dll
Vous pouvez charger une dll d'extension avec la commande .load.
Pour lister les commandes d'une dll, on utilise la syntaxe suivante :
!module.help
Elle ne fonctionne pas sur dbghelp.dll.
#### Format PE et loader de windows :
Chez tantine, on souhaite pouvoir rédiger et tester du code assembleur, et pourquoi pas s'arranger pour obtenir des .exe valides.
Un .exe est au format PE. Il est composé de headers et de sections. Le loader de windows charge les headers dans la mémoire à une certaine adresse qui peut théoriquement être arbitraire. En réalité, la plupart des .exe exigent d'être chargés à une adresse bien précise (Les dlls ont par contre une base de relocation qui leurs permet d'être chargées à n'importe quelle adresse). Les headers précisent à quelles adresses doivent être chargées les sections, relativement à l'adresse des headers (L'adresse du module quoi).
Les PE disposent d'une table d'importation qui définit quelles fonctions de l'API Win32 sont nécessaires à l'application. Le loader de Windows complète cette table avec les adresses qui vont bien lors du chargement de l'exe. Dans le code machine, lors de l'appel d'une fonction de l'API, le call récupère l'adresse dans cette table. Il connait l'adresse de cette table car elle se trouve toujours au même endroit, à un certains offset depuis l'adresse du module qui elle même est fixe.
La construction d'un exe valide au format PE à la main et sans documentation est du pur suicide. Il est bien plus simple d'utiliser un .exe éjà existant sur le PC de tantine.
On souhaite pouvoir executer n'importe quelle fonction de l'API Win32... Il faut donc disposer des fonctions LoadLibrary et GetProcAddress. notepad.exe importe ces deux fonctions, et servirat donc de base de travail.
#### Récupération des informations sur un executable :
Voyons voir ce que notepad a dans le ventre :
ntsd notepad.exe
ntsd charge le programme et s'arrête sur un point d'arrêt.
La commande lm affiche les modules (.dll, .exe, .ocx...) chargés par l'appli, ainsi que leurs adresses de début et fin.
Notons l'adresse du module notepad : 01000000.
La command !lmi, suivie d'un nom de module, donne un peu plus d'informations sur celui-ci.
Mais la commande !dh est nettement plus efficace : elle affiche tout un tas d'infos sur les headers et les sections.
On remarquera par exemple dans les caractéristiques "Relocations stripped", qui signifie que ce module ne peut être chargée qu'à une seule adresse.
On peut vérifier que le champ "Image Base" a bien la valeure qu'on a noté tout à l'heure : 01000000.
On prend note du point d'entrée (entry point), 739D.
Sous le "optional header", il y a les data directories. Ce sont des pointeurs vers des données particulières, elles même se trouvant généralement dans les sections. Ces données sont par exemple les tables d'import et d'export, les tables de relocation qui permettent aux dlls d'être chargées à n'importe quelle adresse, les ressources...
Notons l'adresse de la table d'importation, 7604.
Finalement, on a les informations sur les sections.
notepad à 3 sections :
1 .text : Contient le code. Dans le fichier, cette section se trouve à l'adresse 400 à partir du bébut de fichier, pour une taille de
7800 octets (Informations "raw data"). Dans la mémoire elle se trouve à l'adresse du module (01000000) + 1000, pour une taille de 7748 (Informations "virtual") On remarque que le point d'entrée et la table d'importation sont dans cette section.
2 .data : Contient les données initialisées ou non. On constate que la taille dans le fichier (raw data) est très inferieure à la taille dans la mémoire (virtual). Les données non initialisée seront placés dans cet espace supplémentaire. Les données initialisées ou non sont par exemple les variables globales.
3 .rsrc : Les ressources. Menu, bitmap, chaînes...
Jettons un coup d'oeil à la table d'importation. Elle se trouve en 01000000 + 7604. Pour l'afficher, on utilise la commande db. db prend une adresse ou une plage.
db notepad + 7604 L1000 affiche les 0x1000 premiers octets de la table.
Cela affiche les noms des fonctions importées, même si le formatage n'est pas terrible...
On peut préciser la base d'un nombre en le préfixant :
0x prefix (hexadecimal)
0n prefix (decimal)
0t prefix (octal)
0y prefix (binary)
Les commandes classiques d'affichage du contenu de la mémoire sont :
dd : dword
da : ASCII
du : Unicode
db : byte
dw : 16 bits
dq : 64 bits
dyd : Binaire
Toujours concernant la récupération d'informations sur les modules, on peut récupérer la liste des fonctions exportées par une dll. Par exemple :
x kernel32!Get*
Affiche toutes les fonctions exportées par kernel32 qui commencent par Get.
Certaines API sont en deux versions, une suffixée A pour l'ASCII, une autre suffixée W pour l'Unicode.
Pour se simplifier la vie, on peut utiliser la commande ss, suivie de w(Unicode), a(ASCII) ou n(Aucun).
De cette manière, lorsque l'on entre un symbole que ntsd ne trouve pas, il ajoute le suffixe au symbole, et recommence la recherche.
La commande ln nous donne les symboles les plus proches d'une adresse. Dans notre cas où l'on a pas de symboles autres que les fonctions exportées, cela nous permet de savoir à quelle fonction appartient une adresse facilement.
Enfin, la commande !handle liste les handles sur des objets systèmes et leurs types.
#### Détourner le comportement d'un executable :
A présent, on se propose de modifier notepad pendant son execution.
On commence par repartir sur une base propre :
ntsd notepad.exe
On se souvient de l'adresse du point d'entré : c'est là que l'on va assembler notre code.
Jettons un coup d'oeil au code actuel :
u notepad + 739D
On va appeler MessageBoxA, puis ExitProcess.
On va commencer par mettre deux chaînes dans la mémoire, une pour le contenu de la MessageBox, une autre pour le titre.
Ou mettre ces chaînes ?
On a beaucoup de place, mais ce n'est pas une raison pour écraser quelque-chose.
On va donc écrire dans les données non initialisées.
!dh notepad
Section .data.
1BA8 virtual size
9000 virtual address
800 size of raw data
7C00 file pointer to raw data
On ecrira donc en notepad + 9000 + 800.
Les commandes pour écrire dans la mémoire sont les suivantes :
ea : Ecrire une chaîne de caractère dans la mémoire, sans caractère terminal.
eb : Ecrire des octets dans la mémoire.
ew, ed, eq : Ecrire de la taille spécifiée dans la mémoire.
La commande d'écriture de chaînes à zéro terminal eza de WinDbg n'est pas utilisable... On écrit donc :
eb notepad + 9800 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
on vérifie :
db notepad + 9800
On écrit le titre un peu plus loin :
eb notepad + 9810 'Y' 'e' 'a' 'h' '!' 0
A présent, il ne reste plus qu'à coder !! (Enfin !)
Pour assembler, c'est a :
a notepad + 739D
Pour arrêter d'assembler, il suffit de ne rien entrer sur la ligne et de presser entrée.
Il faut pousser les arguments de MessageBoxA, de la droite vers la gauche :
xor @eax, @eax
push @eax
lea @ebx, [notepad + 9810]
push @ebx
lea @ebx, [notepad + 9800]
push @ebx
push @eax
call MessageBoxA
xor @eax, @eax
push @eax
call ExitProcess
On désassemble pour vérifier :
u notepad + 739D
Et go ! :
g
#### Travailler depuis un fichier :
Bon, le code que l'on a écrit, on préfèrerait le conserver...
Avec debug, on pouvait envoyer des commandes contenues dans un fichier avec une redirection <, ou encore en utilisant un pipe et la commande type.
Avec ntsd, on peut utiliser l'option -c et la commande $< comme je l'ai fait dans le zip.
Si on souhaite mettre des lignes vides dans le fichier en question, il faut mettre celles-ci en commentaire, car une pression sur entrée demande à ntsd de réexecuter la dernière commande. Pour mettre en commentaire une ligne, on utilise $. On garderat la commande * pour mettre des vrais commentaires. Cela peut nous permettre de faciliter l'imortation du code dans un assembleur classique. Ces caractères provoqueront une erreur mineure au milieu du code (Par contre cela ne facilite pas vérification que l'assemblage s'est bien passé), lors de l'assemblage, sans poser plus de problèmes.
===============================
eb notepad + 9800 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
eb notepad + 9810 'Y' 'e' 'a' 'h' '!' 0
$
a notepad + 739D
$
xor @eax, @eax
push @eax
lea @ebx, [notepad + 9810]
push @ebx
lea @ebx, [notepad + 9800]
push @ebx
push @eax
call MessageBoxA
xor @eax, @eax
push @eax
call ExitProcess
$
g
q
===============================
On copie ça, on lance ntsd notepad.exe, puis clique gauche sur l'icône en haut à gauche de la console, coller.
#### Principe de la sauvegarde de l'exe sur le disque :
Devoir faire un copier coller lors du développement, c'est déjà bof, mais alors être obligé de le faire sur l'application finale, ce serait horrible. Qui plus est, si on pouvait éviter de réassembler l'application à chaque lancement, ce ne serait pas plus mal...
On va donc écrire notre notepad depuis la RAM vers le disque.
Le programme de compilation sera placé dans les données non initialisées pour pouvoir disparaître.
Les chaînes de notre application vont devoir se trouver dans les données initialisées.
Notre code devra se trouver dans la section .text.
On exploiterat donc les zones suivantes :
Données non initialisées :
9800 -> ABA8
Données initialisées (2 milles caractères) :
9000 -> 9800
Code :
3000 -> 7604
Le point d'entrée est en 739D. La valeur de 3000 comme début du code est arbitraire mais assure une bonne marge de sécurité tout en nous donnant bien assez de place. Le point d'entrée en 739D était un peu près de la table d'import en 7604. On va donc faire un saut à l'adresse 3000 dès le début du programme.
Le code de compilation est théoriquement simple : il faut claquer la mémoire dans un fichier. Mais pas n'importe comment car il faut penser aux section.
L'écriture se fait en 4 phase :
1 Copie des headers et des bits qui le suive (On dirait que la Bound Import Table n'est pas dans une section...) :
raw data : 0 -> 400
virtual : 0
taille : 400
2 Copie de la section .text (On remplit jusqu'à tomber sur l'offset de la section .data :
raw data : 400 -> 7C00
virtual : 1000
taille : 7800
3 Copie de la section .data
raw data : 7C00 -> 8400
virtual : 9000
taille : 800
4 Copie de la section .rsrc
raw data : 8400 -> 11400
virtual : B000
taille : 9000
0x11400 = 70656 octets = la taille de notepad.exe !
#### L'utilisation des fonctions de l'API Win32 :
Les fonctions de l'API Win32 sont dans des dlls, et sont donc suceptibles de ne pas être chargées aux mêmes adresse d'une execution sur l'autre. En fait si, on fait des éssais, on s'aperçoit que les principale dlls (kernel32...) sont toujours chargées au même endroit.
Mais essayons de travailler proprement.
On sait que le loader va mettre les adresses de LoadLibrary et GetProcAddress à une adresse précise en mémoire car ces fonctions sont dans la table d'import de notepad.
La première étape consiste à déterminer les emplacement de ses adresses.
On pourrait décrypter la table d'import. Mais on va plutôt laisser faire le travail à ntsd.
La dernière section est à l'adresse virtuelle notepad + B000, avec une taille qu'on arrondit à 9000.
On va donc bourriner en cherchant de notepad à notepad + 14000.
La commande s permet de rechercher dans une plage mémoire :
s -b : 8 bits
s -w : 16 bits
s -d : 32 bits
s -q : 64 bits
s -a : ASCII
s -u : Unicode
Les commandes sa et su permettent de cherher des chaînes repectivement ASCII et Unicode sans préciser de pattern.
s -d notepad L14000 LoadLibraryA
s -d notepad L14000 GetProcAddress
Les résultats nous donnent une adresse et une série de valeur. On peut vérifier que c'est bien la première valeur en évaluant le symbole :
? LoadLibraryA
On obtient :
LoadLibraryA -> 10c8
GetProcAddress -> 1110
On peut essayer de vérifier tout ça en utilisant une fonction intéressante de ntsd. On va placer un point d'arrêt qui ne va pas se déclencher lorsque eip pointera dessus, mais lorsque qu'un accès mémoire sera effectué à cette adresse. ntsd peut refuse la mise en place de ce type de breakpoint lorsque l'on est pas passé par l'entry point, ce qui arrive lorsque l'on lance ntsd en même temps que le processus (Il suffit alors de faire tourner celui-ci puis de le stopper avec ctrl+c), ou, plus grave, lorsque l'on s'attache à un processus.
Dans notre cas, lOadLibraryA est suceptible de n'être appeler qu'en début de prorgamme, on va donc mettre un point d'arrêt sur l'entry point.
ntsd notepad.exe
bp notepad + 739D
g
ba r4 notepad + 10c8
ba r4 notepad + 1110
Ces breaks point seront déclenchés lors de la lecture ou écriture de 4 octets à l'adresse spécifiée.
bl liste les breakpoints, et bc permet d'en supprimer un à partir de son id.
Pas de chance, LoadLibrary est appelée avant l'entry point (On voit bien les ModLoad)...
Mais on va quand même se stopper sur un GetProcAddress.
#### Le code de l'application :
L'application serat identique à la précédente, à trois différences près :
1 MessageBox et ExitProcess seront appelés quelles que soient leurs adresses.
2 Les chaînes seront stockées dans la mémoire initialisée de manière à être sauvegardée sur le disque quand on compilera.
3 On mettra seulement un saut vers notre code à l'entry point.
========================================
* Mise en place du saut au niveau de l'entry point
a notepad + 739D
jmp notepad + 3000
* Mise en place des chaines dans la mémoire
eb notepad + 9000 'H' 'e' 'l' 'l' 'o' ' ' 'w' 'o' 'r' 'l' 'd' 0
eb notepad + 9010 'Y' 'e' 'a' 'h' '!' 0
eb notepad + 9020 'k' 'e' 'r' 'n' 'e' 'l' '3' '2' 0
eb notepad + 9030 'u' 's' 'e' 'r' '3' '2' 0
eb notepad + 9040 'M' 'e' 's' 's' 'a' 'g' 'e' 'B' 'o' 'x' 'A' 0
eb notepad + 9050 'E' 'x' 'i' 't' 'P' 'r' 'o' 'c' 'e' 's' 's' 0
$
a notepad + 3000
$
* Récupération d'un handle sur kernel32
lea @ebx, [notepad + 9020]
push @ebx
call dword ptr [notepad + 10c8]
mov @edi, @eax
$
* Récupération d'un handle sur user32
lea @ebx, [notepad + 9030]
push @ebx
call dword ptr [notepad + 10c8]
mov @esi, @eax
$
* Récupération de l'adresse de MessageBoxA
lea @ebx, [notepad + 9040]
push @ebx
push @esi
call dword ptr [notepad + 1110]
mov @esi, @eax
$
* Récupération de l'adresse de ExitProcess
lea @ebx, [notepad + 9050]
push @ebx
push @edi
call dword ptr [notepad + 1110]
mov @edi, @eax
$
* Execution du programme
xor @eax, @eax
push @eax
lea @ebx, [notepad + 9010]
push @ebx
lea @ebx, [notepad + 9000]
push @ebx
push @eax
call @esi
xor @eax, @eax
push @eax
call @edi
========================================
Comme on est dans un débogueur, il ne faut pas hésiter à utiliser t (Step into) et p (step over) pour vérifier le comportement du programme.
La commande ln de recherche du symbole le plus proche, et db, d'affichage de données, nous permettent de contrôler que l'on tape bien au bon endroit.
On peut afficher les registres avec r, et les affecter de la manière suivante :
r eip = notepad + 3000
Ne pas oublier aussi .restart qui permet de redémarrer le programme sans quitter ntsd.
#### Le code de compilation :
Le code de compilation est par définition executé dans ntsd. Il n'y a donc pas besoin de prendre des précautions au niveau de l'emploi des API Win32.
Chez tante Martine, l'absence de documentation est génante, tout spécialement lorsque l'on emploie des structures, dont on a oublié les champs, ou lorsque l'on emploie des constantes, dont on connait plus les identifiants que les valeurs. Une solution qui peut aider consiste à mettre des points d'arrêts sur ces fonctions, de manière à déterminer les paramètres passés à celles-ci.
========================================
* Le nom de l'exe que l'on va créer
eb notepad + 9800 'c' ':' '\\' 'T' 'e' 's' 't' '.' 'e' 'x' 'e' 0
$
a notepad + A000
$
* Ouverture du fichier
xor @eax, @eax
push @eax
* FILE_ATTRIBUTE_NORMAL
mov @ebx, 80
push @ebx
* CREATE_ALWAYS
mov @ebx, 2
push @ebx
push @eax
push @eax
* GENERIC_WRITE
mov @ebx, 40000000
push @ebx
lea @ebx, [notepad + 9800]
push @ebx
call CreateFileA
mov @edi, @eax
$
* Ecriture des headers et sections
$
xor @eax, @eax
push @eax
lea @eax, [notepad + 9810]
push @eax
mov @eax, 0x400
push @eax
lea @eax, [notepad]
push @eax
push @edi
call WriteFile
$
xor @eax, @eax
push @eax
lea @eax, [notepad + 9810]
push @eax
mov @eax, 0x7800
push @eax
lea @eax, [notepad + 0x1000]
push @eax
push @edi
call WriteFile
$
xor @eax, @eax
push @eax
lea @eax, [notepad + 9810]
push @eax
mov @eax, 0x800
push @eax
lea @eax, [notepad + 0x9000]
push @eax
push @edi
call WriteFile
$
xor @eax, @eax
push @eax
lea @eax, [notepad + 9810]
push @eax
mov @eax, 0x9000
push @eax
lea @eax, [notepad + 0xB000]
push @eax
push @edi
call WriteFile
$
* Fermeture du fichier
$
push @edi
call CloseHandle
nop
r eip = notepad + A000
bp CloseHandle
g
g poi(esp)
q
========================================
C'est aussi simple que cela : la copie successive des deux codes précédents dans une console crée avec ntsd notepad.exe génère un executable nommé Test.exe, à la racine de C:\. Cette executable a la même taille que notepad.exe (70 656 octets), mais affiche simplement une MessageBox.
#### Conclusion :
On est tout à fait en mesure de faire des .exe valides, codés en assembleurs, chez tantine. La seule condition nécessaire (Qui est malheureusement de taille...) est de très bien connaître les fonctions, structures et valeurs des constantes de l'API Win32.
#### Commandes diverses :
ctrl + v : Active/désactive le mode verbose (Affichage d'informations supplémentaires).
.time : Affiche le temps depuis le démarrage du système et depuis le débogage.
.tlist : Liste les processus du système.
.imgscan : Cherche les images (MZ).
wt : Trace une fonction, et affiche les fonctions appelées.
pa : Step jusqu'à une adresse, avec affichage d'infos.
.shell : Execution d'une commande DOS.
.echo : Même comportement que la echo du DOS.
!str : Affichage d'une chaîne ASCII ou OEM (La chaîne doit être précédée par sa taille et sa taille max).
!ustr : Affichage d'une chaîne Unicode (La chaîne doit être précédée par sa taille et sa taille max).
#### Commandes pouvant être utiles lorsque le débogueur s'attache à un processus en perdition :
.kframes : Spécification de la profondeur d'inspection de la pile d'appel.
.lastevent : Affiche des infos sur le dernier évènement ou exception.
!error : Affiche la description d'une erreur Win32 à partir de son numéro.
!gle : Dernières erreurs Win32 et NTDLL.
.cxr : affiche les infos de contexte.
.ecxr : affiche les infos de contexte de l'exception.
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
Code manchine / myown exe [ par 6Po ]
CoucouQqun sais ou je peux trouve de la doc pour cree soit meme un exe ?Cree son propre compilateur en gros. Mici 6Pohttp://www.dreamersteam.org
EXE Infector [ par seboss ]
Bonjour tout le monde,Je dois realiser un petit projet sympas utilisant le C++,VB et ASM... Mais l'ASm je ne connasi pas assez bien pour realiser ce q
Tuto ES Console [ par coockiesch ]
Hello!!!Je cherche un tuto qui explique les ES Console, en fr et compatible TASM (oui, c bcp)...Merci@++Raf
Activation d'un bouton dans un .exe [ par rems02 ]
Bonjour,Je voudrais activer un bouton qui bien sûr est desactivéle seul petit problème j'ai juste l'executablemerci de votre aideRem'S<img sr
pb d'interruptions [ par sollda ]
Bonjour à tous,j'ai écrit un petit prog de test en asm (compilé avec ML.EXE), dont voici le code :-------------------include winapi.inc.datamess db
Stdout cmd.exe ou console.com [ par Stormy ]
Salut à tous!Sauriez-vous comment obtenir le output qui suit une commande sur CMD. Pour bien exprimer mon problème, voici un exemple. Le code commande
Cherche tuto pour debuter [ par Tetris42 ]
Salut je dois realiser une librairie dynamique elf pouvant remplacer la librairie standard C. Quelqu'un peut me filer quelques tuto ??MerciTetris
Les fonctions noName de MFC [ par thiosyiasar ]
SalutJe suis en train de déssambler un exe (console) écrit avec Vc++ utilisant les MFCJe me trouve confronté au pb suivant :L'exe utilise des function
Probleme de compilation flat binary ou plain binary sur windows [ par hexanium ]
Salut, j'essaye de compiler un fichier C en binaire plat, c'est a dire le bytecode pur sans entete ni ajout de library. J'ai essayé sous Linux ca
la structure des fichier exe [ par izou ]
je veux savoire quelle est le role de la table de relogement dans les fichier exe
|
Derniers Blogs
[MIX10] KEYNOTE DEUXIèME JOURNéE - INTERNET EXPLORER 9, HTML5, VISUAL STUDIO 2010, ODATA[MIX10] KEYNOTE DEUXIèME JOURNéE - INTERNET EXPLORER 9, HTML5, VISUAL STUDIO 2010, ODATA par cyril
Le deuxième keynote du mix fut très riche en contenu. Internet Explorer 9 Juste un après le lancement de Internet Explorer 8, Microsoft a dévoilé les nouveautés de Internet Explorer 9. Désormais, IE supportera HTML5, SVG et CSS3. L'élément ...
Cliquez pour lire la suite de l'article par cyril CERTIFICATIONS BETA .NET 4CERTIFICATIONS BETA .NET 4 par KooKiz
Les inscriptions pour les certifications beta .NET 4 ont commencé. L'inscription est offerte pour les examens suivants : - 71-511, TS: Windows Applications Development with Microsoft .NET Framework 4 - 71-515, TS: Web Applications Development with...
Cliquez pour lire la suite de l'article par KooKiz [MIX 2010] - MICROSOFT TRANSLATOR TECHNOLOGY PREVIEW V2[MIX 2010] - MICROSOFT TRANSLATOR TECHNOLOGY PREVIEW V2 par redo
J'imagine que la plupart d'entre vous connaissent bien et utilisent le service de traduction de Google, mais connaissez-vous celui de Microsoft . Microsoft Translator ? Effectivement, Microsoft nous annoncé le lancement version 2 de la Technologie Preview...
Cliquez pour lire la suite de l'article par redo LANCEMENT EN PREVIEW DE CYCLONE LORS DES TECHDAYS 2010!LANCEMENT EN PREVIEW DE CYCLONE LORS DES TECHDAYS 2010! par MPOWARE
Toutes les vidéos de ce lancement sont en ligne!
Partie I - Intro
http://www.youtube.com/watch?v=LkQzTQ8T6CA
Partie II - Démo 1
http://www.youtube.com/watch?v=drAhYQ7lqvo
Partie III - Démo 2
http://www.youtube.com/watch?v=c8KM_1Gqybc...
Cliquez pour lire la suite de l'article par MPOWARE [WP7] JE NE VEUX PAS D'UN NOUVEL IPHONE[WP7] JE NE VEUX PAS D'UN NOUVEL IPHONE par FREMYCOMPANY
Je pense qu'ils ont besoin d'une piqure de rappel chez Microsoft : c'est bien gentil d'avoir une interface jolie, mais si c'est pour avoir un truc qui ne convainct pas dedans, c'est peine perdue.
---->
Système ouvert ----> Fermé ?
P...
Cliquez pour lire la suite de l'article par FREMYCOMPANY
Forum
RE : CSHARPRE : CSHARP par ghuysmans99
Cliquez pour lire la suite par ghuysmans99
Logiciels
Academy System (10.9.4.0)ACADEMY SYSTEM (10.9.4.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Xilisoft Convertisseur Vidéo Ultimate (5.1.39.0305)XILISOFT CONVERTISSEUR VIDéO ULTIMATE (5.1.39.0305)Xilisoft Convertisseur Vidéo Ultimate est un outil puissant de conversion vidéo, facile à utilise... Cliquez pour télécharger Xilisoft Convertisseur Vidéo Ultimate Xilisoft DVD Ripper Ultimate (5.0.64.0304)XILISOFT DVD RIPPER ULTIMATE (5.0.64.0304)Xilisoft DVD Ripper Ultimate est un logiciel excellent pour copier et convertir DVD vers presque ... Cliquez pour télécharger Xilisoft DVD Ripper Ultimate Rigs of Rods (63.3)RIGS OF RODS (63.3)c'est un jeu de multi-simulation camions,autobus voitures, avions, bateaux, hélicoptère avec défo... Cliquez pour télécharger Rigs of Rods
|