Diese Antwort ist spezifisch für x86. Zu den tragbaren Tools, die AArch64, MIPS oder anderen Maschinencode disassemblieren können, gehört objdump
und llvm-objdump
.
Agner Fogs Disassembler, objconv
, ist ganz nett. Bei Leistungsproblemen (wie zum Beispiel dem gefürchteten LCP-Stall von Anweisungen mit 16-Bit-Immediate-Konstanten) fügt es Kommentare zur Disassemblierungsausgabe hinzu.
objconv -fyasm a.out /dev/stdout | less
(-
wird nicht erkannt als Abkürzung für stdout und gibt standardmäßig in eine Datei mit ähnlichem Namen wie die Eingabedatei aus, mit .asm
angeklebt.)
Es fügt dem Code auch Verzweigungsziele hinzu. Andere Disassembler zerlegen Sprunganweisungen normalerweise nur mit einem numerischen Ziel und platzieren keine Markierung an einem Verzweigungsziel, um Ihnen zu helfen, den Anfang von Schleifen zu finden und so weiter.
Es zeigt auch NOPs deutlicher an als andere Disassembler (wodurch es deutlich wird, wenn eine Auffüllung vorhanden ist, anstatt es nur als eine weitere Anweisung zu disassemblieren.)
Es ist Open Source und einfach für Linux zu kompilieren. Es kann in die Syntax NASM, YASM, MASM oder GNU (AT&T) zerlegt werden.
Beispielausgabe:
; 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
...
Beachten Sie, dass diese Ausgabe bereit ist, wieder in eine Objektdatei zusammengestellt zu werden, sodass Sie den Code auf der asm-Quellebene optimieren können, anstatt mit einem Hex-Editor im Maschinencode. (Sie sind also nicht darauf beschränkt, die Dinge gleich groß zu halten.) Ohne Änderungen sollte das Ergebnis nahezu identisch sein. Es ist jedoch möglicherweise nicht der Fall, da Dinge wie
zerlegt werden (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
hat nichts in der Quelle, um sicherzustellen, dass es sich in die längere Codierung zusammenfügt, die Platz für Verschiebungen lässt, um es mit einem 32-Bit-Offset neu zu schreiben.
Wenn Sie es nicht installieren möchten, objconv, GNU binutils objdump -Mintel -d
ist sehr brauchbar und wird bereits installiert, wenn Sie ein normales Linux-gcc-Setup haben.
Eine interessante Alternative zu objdump ist gdb. Sie müssen die Binärdatei nicht ausführen oder Debuginformationen haben.
$ 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)
Mit vollständigen Debugging-Informationen ist es sogar noch besser.
(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 hat eine ähnliche Option (-S)
Ich denke nicht gcc
hat ein Flag dafür, da es in erster Linie ein Compiler ist, aber ein anderes der GNU-Entwicklungswerkzeuge tut es. objdump
nimmt einen -d
/--disassemble
Flagge:
$ objdump -d /path/to/binary
Die Demontage sieht so aus:
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