Déboguer un programme en assembleur avec GDB

Le débogage en assembleur est une compétence essentielle pour les développeurs bas niveau travaillant avec l’architecture x86, ARM ou RISC-V. L’outil le plus utilisé pour cette tâche est GDB (GNU Debugger), qui permet d’analyser l’exécution d’un programme, d’examiner les registres, la mémoire et le code machine.

Dans cet article, nous verrons :
Comment compiler un programme en assembleur pour le débogage
Les commandes essentielles de GDB
L’analyse des registres et de la mémoire
Des cas pratiques de débogage


1. Préparer un programme en assembleur pour GDB

Avant d’utiliser GDB, il faut compiler le programme en assembleur avec les options adaptées.

🔹 Exemple de programme en assembleur x86 (NASM, Linux 64 bits)

assemblyCopierModifiersection .data
    message db "Hello, GDB!", 0  ; Chaîne de caractères avec un terminateur NULL

section .bss
    buffer resb 10               ; Réserve un buffer de 10 octets

section .text
    global _start

_start:
    mov rax, 1      ; syscall write (sys_write)
    mov rdi, 1      ; stdout
    mov rsi, message ; Adresse du message
    mov rdx, 13     ; Longueur du message
    syscall         ; Appel système

    mov rax, 60     ; syscall exit (sys_exit)
    xor rdi, rdi    ; Code de retour 0
    syscall         ; Quitte le programme

🔹 Compilation avec NASM et GCC

shCopierModifiernasm -f elf64 -g -F dwarf program.asm -o program.o
gcc -no-pie program.o -o program

Options utilisées :

  • -g -F dwarf : Ajoute les informations de débogage au format DWARF.
  • -no-pie : Désactive l’ASLR (Address Space Layout Randomization) pour simplifier le débogage.

2. Lancer GDB et charger le programme

🔹 Démarrer GDB sur le programme compilé

shCopierModifiergdb program

Une fois dans GDB, vous verrez un prompt similaire à :

scssCopierModifierGNU gdb (Ubuntu 12.1) 12.1
Reading symbols from program...
(gdb)

🔹 Lancer l’exécution pas à pas

Vous pouvez exécuter immédiatement le programme avec :

shCopierModifier(gdb) run

Mais pour analyser l’exécution en détail, il est préférable de définir des points d’arrêt (breakpoints).


3. Commandes essentielles pour le débogage

📌 1️⃣ Définir des points d’arrêt (breakpoints)

Un point d’arrêt permet de stopper l’exécution du programme à une ligne précise.

  • Arrêter à l’entrée du programme :
shCopierModifier(gdb) break _start
  • Arrêter à une adresse mémoire précise :
shCopierModifier(gdb) break *0x401000
  • Lister tous les breakpoints :
shCopierModifier(gdb) info breakpoints

📌 2️⃣ Exécuter pas à pas

  • Exécuter une seule instruction en assembleur (y compris les appels système) :
shCopierModifier(gdb) stepi
  • Exécuter une seule instruction en ignorant les appels système :
shCopierModifier(gdb) nexti
  • Continuer jusqu’au prochain breakpoint :
shCopierModifier(gdb) continue

📌 3️⃣ Examiner le code assembleur

  • Afficher le désassemblage du programme :
shCopierModifier(gdb) disassemble _start
  • Désassembler les instructions autour de l’exécution actuelle :
shCopierModifier(gdb) disassemble $pc
  • Afficher les instructions suivantes :
shCopierModifier(gdb) x/10i $pc

💡 $pc représente le compteur de programme (registre qui stocke l’adresse de l’instruction actuelle).


4. Analyser les registres et la mémoire

📌 1️⃣ Lire les registres

  • Afficher tous les registres :
shCopierModifier(gdb) info registers
  • Afficher un registre spécifique :
shCopierModifier(gdb) print $rax

📌 2️⃣ Examiner la mémoire

  • Lire 10 octets en hexadécimal à l’adresse du message :
shCopierModifier(gdb) x/10xb &message
  • Lire une valeur à une adresse donnée :
shCopierModifier(gdb) x/gx 0x601000

5. Cas pratiques de débogage

🔹 Détecter une erreur de segmentation (segfault)

Si le programme crashe avec une erreur de segmentation, lancez-le dans GDB :

shCopierModifiergdb program
(gdb) run

Si une segfault se produit, GDB affiche un message du type :

javaCopierModifierProgram received signal SIGSEGV, Segmentation fault.
0x0000000000401000 in _start ()

🔍 Étapes pour analyser l’erreur :
1️⃣ Vérifier l’adresse où le crash a eu lieu :

shCopierModifier(gdb) bt

2️⃣ Désassembler pour voir l’instruction qui a causé le crash :

shCopierModifier(gdb) disassemble _start

3️⃣ Vérifier les valeurs des registres :

shCopierModifier(gdb) info registers

🔹 Déboguer une boucle infinie

Si le programme tourne sans fin, utilisez CTRL+C pour l’interrompre dans GDB :

shCopierModifier(gdb) interrupt

Ensuite, vérifiez l’instruction en cours d’exécution avec :

shCopierModifier(gdb) x/10i $pc

Si une boucle tourne indéfiniment, vous pouvez modifier la valeur d’un registre pour la forcer à sortir :

shCopierModifier(gdb) set $rcx=0

🔹 Modifier des valeurs à l’exécution

Si vous voulez modifier une variable ou un registre sans modifier le code source, vous pouvez utiliser set :

  • Modifier un registre :
shCopierModifier(gdb) set $rax = 100
  • Modifier une adresse mémoire :
shCopierModifier(gdb) set {int}0x601000 = 42

Cela permet de tester des correctifs en direct sans recompiler le programme.


6. Conclusion et bonnes pratiques

✔️ Toujours compiler avec -g pour inclure les symboles de débogage.
✔️ Utiliser breakpoints pour examiner le programme progressivement.
✔️ Surveiller les registres et la mémoire pour comprendre l’état du programme.
✔️ Désactiver ASLR (set disable-randomization on) pour des adresses fixes.
✔️ Si une segfault se produit, utiliser bt, info registers et disassemble pour analyser.

Le débogage en assembleur avec GDB est une compétence précieuse pour les développeurs systèmes, embarqués et reverse-engineers. Maîtriser ces techniques permet de mieux comprendre le fonctionnement des processeurs et d’écrire du code plus robuste. 🚀

carle
carle