Wie zerlege ich eine ausführbare Binärdatei unter Linux, um den Assembler-Code zu erhalten?


81

Mir wurde gesagt, ich solle einen Disassembler verwenden. Hat gccetwas eingebaut? Was ist der einfachste Weg, dies zu tun?



Verwandte Themen: Wie entferne ich "Rauschen" von der Ausgabe der GCC / Clang-Baugruppe? - Wenn Sie wirklich nur sehen möchten, was der Compiler getan hat, müssen Sie nicht immer + Link + Disassemblieren kompilieren.
Peter Cordes

Antworten:


130

Ich glaube nicht, dass gcces ein Flag dafür gibt, da es hauptsächlich ein Compiler ist, aber ein anderes der GNU-Entwicklungstools. objdumpnimmt ein -d/ --disassembleflag:

$ 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

9
Für die Intel-Syntax : 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.
Peter Cordes

5
Nützliche Optionen : objdump -drwC -Mintel. -rzeigt Verschiebungen aus der Symboltabelle. -Centwirrt C ++ - Namen. -Wvermeidet Zeilenumbrüche für lange Anweisungen. Wenn Sie es häufig verwenden, ist dies praktisch : alias disas='objdump -drwC -Mintel'.
Peter Cordes

2
In -Szu Anzeige Quellcode mit der Demontage miteinander vermischen. (Wie in einer anderen Antwort gezeigt .)
Alexander Pozdneev

45

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)


12

Diese Antwort ist spezifisch für x86. Tragbare Werkzeuge, die AArch64, MIPS oder einen beliebigen Maschinencode zerlegen können, enthalten objdumpund llvm-objdump.


Agner Fogs Disassembler , objconvist 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 .asmangeheftet 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 -dsehr benutzerfreundlich und wird bereits installiert, wenn Sie ein normales Linux-gcc-Setup haben.


6

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.


2
Insbesondere: home.hccnet.nl/awmvan.der.horst/ciasdis.html enthält unter "Neueste Entwicklungen" ein Debian-Paket, das Sie einfach installieren können. Mit den richtigen Anweisungen (es führt Skripte aus) wird eine Quelldatei generiert, die wieder zu genau derselben Binärdatei zusammengesetzt wird. Mir ist kein Paket bekannt, das das kann. Es kann schwierig sein, die Anweisungen zu verwenden. Ich beabsichtige, sie in Github mit ausführlichen Beispielen zu veröffentlichen.
Albert van der Horst

3

Verwenden Sie IDA Pro und den Decompiler .


IDA scheint ein bisschen übertrieben, vor allem, wenn man bedenkt, dass es ziemlich teuer ist
Michael Mrozek

1
Die kostenlose Version ist nicht für Linux verfügbar, sondern nur die eingeschränkte Demoversion. (Schade, denn unter Windows ist das der beste Disassembler, den ich je benutzt habe)
Adrien Plisson

IDA ist gut, aber das Problem von IDA ist, dass Sie faul werden, wenn Sie für kleine Aufgaben verwendet werden. GDB erledigt die Arbeit für fast alles, GDB einfacher? nein, aber möglich.
Cfernandezlinux


1

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.


1

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.


Beeindruckend! Das ist fantastisch. Übrigens, in Bezug auf Ihr Problem, warum verwenden Sie keinen Alias, um die Eingabe dieses riesigen Befehls zu überspringen?
Fledermaus

0

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


Dies ist nur dann zuverlässig, wenn die Optimierung deaktiviert ist. Andernfalls können Teile der Vorgänge innerhalb der Region in Außenbereiche optimiert oder entfernt werden. Sie können also nur den klobigen -O0Asm sehen.
Peter Cordes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.