Cette réponse est spécifique à x86. Outils portables capables de désassembler AArch64, MIPS ou tout autre code machine incluant objdump
et llvm-objdump
.
Désassembleur d'Agner Fog, objconv
, c'est plutôt sympa. Il ajoutera des commentaires à la sortie de désassemblage pour les problèmes de performances (comme le décrochage LCP redouté des instructions avec des constantes immédiates 16 bits, par exemple).
objconv -fyasm a.out /dev/stdout | less
(Il ne reconnaît pas -
comme raccourci pour stdout, et sort par défaut dans un fichier de nom similaire au fichier d'entrée, avec .asm
cloué.)
Il ajoute également des cibles de branche au code. D'autres désassembleurs désassemblent généralement les instructions de saut avec juste une destination numérique, et ne placent aucun marqueur sur une cible de branche pour vous aider à trouver le haut des boucles et ainsi de suite.
Il indique également les NOP plus clairement que les autres désassembleurs (indiquant clairement quand il y a du rembourrage, plutôt que de le désassembler comme une simple instruction.)
C'est open source et facile à compiler pour Linux. Il peut se désassembler en syntaxe NASM, YASM, MASM ou GNU (AT&T).
Exemple de sortie :
; Filling space: 0FH
; Filler type: Multi-byte NOP
; db 0FH, 1FH, 44H, 00H, 00H, 66H, 2EH, 0FH
; db 1FH, 84H, 00H, 00H, 00H, 00H, 00H
ALIGN 16
foo: ; Function begin
cmp rdi, 1 ; 00400620 _ 48: 83. FF, 01
jbe ?_026 ; 00400624 _ 0F 86, 00000084
mov r11d, 1 ; 0040062A _ 41: BB, 00000001
?_020: mov r8, r11 ; 00400630 _ 4D: 89. D8
imul r8, r11 ; 00400633 _ 4D: 0F AF. C3
add r8, rdi ; 00400637 _ 49: 01. F8
cmp r8, 3 ; 0040063A _ 49: 83. F8, 03
jbe ?_029 ; 0040063E _ 0F 86, 00000097
mov esi, 1 ; 00400644 _ BE, 00000001
; Filling space: 7H
; Filler type: Multi-byte NOP
; db 0FH, 1FH, 80H, 00H, 00H, 00H, 00H
ALIGN 8
?_021: add rsi, rsi ; 00400650 _ 48: 01. F6
mov rax, rsi ; 00400653 _ 48: 89. F0
imul rax, rsi ; 00400656 _ 48: 0F AF. C6
shl rax, 2 ; 0040065A _ 48: C1. E0, 02
cmp r8, rax ; 0040065E _ 49: 39. C0
jnc ?_021 ; 00400661 _ 73, ED
lea rcx, [rsi+rsi] ; 00400663 _ 48: 8D. 0C 36
...
Notez que cette sortie est prête à être réassemblée dans un fichier objet, vous pouvez donc modifier le code au niveau de la source asm, plutôt qu'avec un éditeur hexadécimal sur le code machine. (Vous n'êtes donc pas limité à garder les choses de la même taille.) Sans changement, le résultat devrait être presque identique. Ce n'est peut-être pas le cas, car le démontage de choses comme
(from /lib/x86_64-linux-gnu/libc.so.6)
SECTION .plt align=16 execute ; section number 11, code
?_00001:; Local function
push qword [rel ?_37996] ; 0001F420 _ FF. 35, 003A4BE2(rel)
jmp near [rel ?_37997] ; 0001F426 _ FF. 25, 003A4BE4(rel)
...
ALIGN 8
?_00002:jmp near [rel ?_37998] ; 0001F430 _ FF. 25, 003A4BE2(rel)
; Note: Immediate operand could be made smaller by sign extension
push 11 ; 0001F436 _ 68, 0000000B
; Note: Immediate operand could be made smaller by sign extension
jmp ?_00001 ; 0001F43B _ E9, FFFFFFE0
n'a rien dans la source pour s'assurer qu'il s'assemble à l'encodage plus long qui laisse de la place pour les relocalisations pour le réécrire avec un décalage de 32 bits.
Si vous ne voulez pas l'installer objconv, GNU binutils objdump -Mintel -d
est très utilisable et sera déjà installé si vous avez une configuration gcc Linux normale.
Une alternative intéressante à objdump est gdb. Vous n'avez pas besoin d'exécuter le binaire ou d'avoir des informations de débogage.
$ gdb -q ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x00000000004003a8 _init
0x00000000004003e0 [email protected]
0x00000000004003f0 [email protected]
0x0000000000400400 _start
0x0000000000400430 deregister_tm_clones
0x0000000000400460 register_tm_clones
0x00000000004004a0 __do_global_dtors_aux
0x00000000004004c0 frame_dummy
0x00000000004004f0 fce
0x00000000004004fb main
0x0000000000400510 __libc_csu_init
0x0000000000400580 __libc_csu_fini
0x0000000000400584 _fini
(gdb) disassemble main
Dump of assembler code for function main:
0x00000000004004fb <+0>: push %rbp
0x00000000004004fc <+1>: mov %rsp,%rbp
0x00000000004004ff <+4>: sub $0x10,%rsp
0x0000000000400503 <+8>: callq 0x4004f0 <fce>
0x0000000000400508 <+13>: mov %eax,-0x4(%rbp)
0x000000000040050b <+16>: mov -0x4(%rbp),%eax
0x000000000040050e <+19>: leaveq
0x000000000040050f <+20>: retq
End of assembler dump.
(gdb) disassemble fce
Dump of assembler code for function fce:
0x00000000004004f0 <+0>: push %rbp
0x00000000004004f1 <+1>: mov %rsp,%rbp
0x00000000004004f4 <+4>: mov $0x2a,%eax
0x00000000004004f9 <+9>: pop %rbp
0x00000000004004fa <+10>: retq
End of assembler dump.
(gdb)
Avec des informations de débogage complètes, c'est encore mieux.
(gdb) disassemble /m main
Dump of assembler code for function main:
9 {
0x00000000004004fb <+0>: push %rbp
0x00000000004004fc <+1>: mov %rsp,%rbp
0x00000000004004ff <+4>: sub $0x10,%rsp
10 int x = fce ();
0x0000000000400503 <+8>: callq 0x4004f0 <fce>
0x0000000000400508 <+13>: mov %eax,-0x4(%rbp)
11 return x;
0x000000000040050b <+16>: mov -0x4(%rbp),%eax
12 }
0x000000000040050e <+19>: leaveq
0x000000000040050f <+20>: retq
End of assembler dump.
(gdb)
objdump a une option similaire (-S)
Je ne pense pas gcc
a un drapeau pour cela, puisqu'il s'agit principalement d'un compilateur, mais un autre des outils de développement GNU le fait. objdump
prend un -d
/--disassemble
drapeau :
$ objdump -d /path/to/binary
Le démontage ressemble à ceci :
080483b4 <main>:
80483b4: 8d 4c 24 04 lea 0x4(%esp),%ecx
80483b8: 83 e4 f0 and $0xfffffff0,%esp
80483bb: ff 71 fc pushl -0x4(%ecx)
80483be: 55 push %ebp
80483bf: 89 e5 mov %esp,%ebp
80483c1: 51 push %ecx
80483c2: b8 00 00 00 00 mov $0x0,%eax
80483c7: 59 pop %ecx
80483c8: 5d pop %ebp
80483c9: 8d 61 fc lea -0x4(%ecx),%esp
80483cc: c3 ret
80483cd: 90 nop
80483ce: 90 nop
80483cf: 90 nop