Mir wurde gesagt, ich solle einen Disassembler verwenden. Hat gcc
etwas eingebaut? Was ist der einfachste Weg, dies zu tun?
Mir wurde gesagt, ich solle einen Disassembler verwenden. Hat gcc
etwas eingebaut? Was ist der einfachste Weg, dies zu tun?
Antworten:
Ich glaube nicht, dass gcc
es ein Flag dafür gibt, da es hauptsächlich ein Compiler ist, aber ein anderes der GNU-Entwicklungstools. objdump
nimmt ein -d
/ --disassemble
flag:
$ objdump -d /path/to/binary
Die Demontage sieht folgendermaßen 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
objdump -Mintel -d
. Oder Agner Fogs objconv disassembler ist der schönste, den ich bisher versucht habe (siehe meine Antwort). Das Hinzufügen von nummerierten Labels zu Verzweigungszielen ist wirklich sehr, sehr schön.
objdump -drwC -Mintel
. -r
zeigt Verschiebungen aus der Symboltabelle. -C
entwirrt C ++ - Namen. -W
vermeidet Zeilenumbrüche für lange Anweisungen. Wenn Sie es häufig verwenden, ist dies praktisch : alias disas='objdump -drwC -Mintel'
.
-S
zu Anzeige Quellcode mit der Demontage miteinander vermischen. (Wie in einer anderen Antwort gezeigt .)
Eine interessante Alternative zu objdump ist gdb. Sie müssen weder die Binärdatei ausführen noch über Debuginfo verfügen.
$ 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 __libc_start_main@plt
0x00000000004003f0 __gmon_start__@plt
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)
Diese Antwort ist spezifisch für x86. Tragbare Werkzeuge, die AArch64, MIPS oder einen beliebigen Maschinencode zerlegen können, enthalten objdump
und llvm-objdump
.
Agner Fogs Disassembler , objconv
ist ganz nett. Es werden Kommentare zur Disassemblierungsausgabe für Leistungsprobleme hinzugefügt (wie zum Beispiel der gefürchtete LCP-Stillstand aufgrund von Anweisungen mit 16-Bit-Sofortkonstanten).
objconv -fyasm a.out /dev/stdout | less
(Es wird nicht -
als Abkürzung für stdout erkannt und standardmäßig in eine Datei mit einem ähnlichen Namen wie die Eingabedatei ausgegeben, die .asm
angeheftet ist.)
Außerdem werden dem Code Verzweigungsziele hinzugefügt. Andere Disassembler zerlegen normalerweise Sprunganweisungen mit nur einem numerischen Ziel und setzen keine Markierungen auf ein Verzweigungsziel, um das Auffinden der Oberseite von Schleifen usw. zu erleichtern.
Es zeigt auch NOPs deutlicher an als andere Disassembler (was deutlich macht, wenn Polster vorhanden sind, anstatt es als eine weitere Anweisung zu zerlegen).
Es ist Open Source und einfach für Linux zu kompilieren. Es kann in NASM-, YASM-, MASM- oder GNU-Syntax (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 zusammengesetzt zu werden, sodass Sie den Code auf der Ebene der ASM-Quelle und nicht mit einem Hex-Editor für den Maschinencode optimieren können. (Sie sind also nicht darauf beschränkt, die Größe gleich zu halten.) Ohne Änderungen sollte das Ergebnis nahezu identisch sein. Es könnte jedoch nicht sein, seit der Zerlegung von Sachen wie
(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
Die Quelle enthält nichts, um sicherzustellen, dass sie zu der längeren Codierung passt, die Platz für Verschiebungen lässt, um sie mit einem 32-Bit-Offset neu zu schreiben.
Wenn Sie es nicht objconv installieren möchten, ist GNU binutils objdump -Mintel -d
sehr benutzerfreundlich und wird bereits installiert, wenn Sie ein normales Linux-gcc-Setup haben.
Es gibt auch ndisasm, das einige Macken hat, aber nützlicher sein kann, wenn Sie nasm verwenden. Ich stimme Michael Mrozek zu, dass Objdump wahrscheinlich am besten ist.
[später] Vielleicht möchten Sie auch die Ciasdis von Albert van der Horst lesen: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . Es kann schwer zu verstehen sein, hat aber einige interessante Funktionen, die Sie wahrscheinlich nirgendwo anders finden werden.
Verwenden Sie IDA Pro und den Decompiler .
Möglicherweise ist ODA hilfreich. Es ist ein webbasierter Disassembler, der Tonnen von Architekturen unterstützt.
Der Editor kann Binärdateien in vielen Formaten zerlegen. Es ist ähnlich wie Hiew, aber Open Source.
Öffnen Sie zum Zerlegen eine Binärdatei, drücken Sie F6 und wählen Sie dann Elf / Bild.
Mit diesem ziemlich groben und langwierigen Pipeline-Trick (ersetzen Sie / bin / bash durch die Datei, die Sie zerlegen möchten, und Sie können verdammt nahe kommen (aber keine Zigarre), um eine Baugruppe zu generieren, die sich wieder zusammensetzt, wenn Sie dies beabsichtigen bash.S mit dem, an was Sie die Ausgabe senden möchten):
objdump --no-show-raw-insn -Matt,att-mnemonic -Dz /bin/bash | grep -v "file format" | grep -v "(bad)" | sed '1,4d' | cut -d' ' -f2- | cut -d '<' -f2 | tr -d '>' | cut -f2- | sed -e "s/of\ section/#Disassembly\ of\ section/" | grep -v "\.\.\." > bash.S
Beachten Sie jedoch, wie lange dies dauert. Ich wünschte wirklich, es gäbe einen besseren Weg (oder einen Disassembler, der Code ausgeben kann, den ein Assembler erkennt), aber leider gibt es keinen.
Nehmen wir an, Sie haben:
#include <iostream>
double foo(double x)
{
asm("# MyTag BEGIN"); // <- asm comment,
// used later to locate piece of code
double y = 2 * x + 1;
asm("# MyTag END");
return y;
}
int main()
{
std::cout << foo(2);
}
So erhalten Sie Assembler-Code mit gcc:
g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.'
c++filt
entwirrt Symbole
grep -vE '\s+\.'
entfernt einige nutzlose Informationen
Wenn Sie nun das markierte Teil visualisieren möchten, verwenden Sie einfach:
g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.' | grep "MyTag BEGIN" -A 20
Mit meinem Computer bekomme ich:
# MyTag BEGIN
# 0 "" 2
#NO_APP
movsd xmm0, QWORD PTR -24[rbp]
movapd xmm1, xmm0
addsd xmm1, xmm0
addsd xmm0, xmm1
movsd QWORD PTR -8[rbp], xmm0
#APP
# 9 "poub.cpp" 1
# MyTag END
# 0 "" 2
#NO_APP
movsd xmm0, QWORD PTR -8[rbp]
pop rbp
ret
.LFE1814:
main:
.LFB1815:
push rbp
mov rbp, rsp
Ein benutzerfreundlicherer Ansatz ist die Verwendung von: Compiler Explorer
-O0
Asm sehen.