Jak zdemontować binarny plik wykonywalny w systemie Linux, aby uzyskać kod asemblera?

Odpowiedzi:

138

Myślę, że nie gccma na to flagi, ponieważ jest to przede wszystkim kompilator, ale inne narzędzie programistyczne GNU ma. objdumpprzyjmuje flagę -d/ --disassemble:

$ objdump -d /path/to/binary

Demontaż wygląda następująco:

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
Michał Mrozek
źródło
9
Intel składni: objdump -Mintel -d. Lub deasembler objconv firmy Agner Fog jest najładniejszym, jakiego do tej pory próbowałem (zobacz moją odpowiedź). Dodawanie numerowanych etykiet do celów gałęzi jest naprawdę fajne.
Peter Cordes
5
Przydatne opcje: objdump -drwC -Mintel. -rpokazuje przemieszczenia z tabeli symboli. -Cdemantuje nazwy C ++. -Wunika zawijania wierszy w przypadku długich instrukcji. Jeśli używamy go często, to jest poręczny: alias disas='objdump -drwC -Mintel'.
Peter Cordes
2
Dodaj, -Saby wyświetlić kod źródłowy zmieszany z deasemblacją. (Jak wskazano w innej odpowiedzi .)
Alexander Pozdneev
47

Ciekawą alternatywą dla objdump jest gdb. Nie musisz uruchamiać pliku binarnego ani mieć informacji o debugowaniu.

$ 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)

Z pełnymi informacjami o debugowaniu jest jeszcze lepiej.

(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 ma podobną opcję (-S)

Miroslav Franc
źródło
12

Ta odpowiedź jest specyficzna dla x86. Przenośne narzędzia, które mogą rozmontować AArch64, MIPS lub dowolny kod maszynowy, w tym objdumpi llvm-objdump.


Dezasembler Agner Fog jest , objconvjest bardzo miłe. Doda komentarze do danych wyjściowych demontażu dla problemów z wydajnością (na przykład przerażające przeciągnięcie LCP z instrukcji z 16-bitowymi stałymi natychmiastowymi).

objconv  -fyasm a.out /dev/stdout | less

(Nie rozpoznaje go -jako skrótu dla standardowego wyjścia i domyślnie wyprowadza do pliku o podobnej nazwie do pliku wejściowego, z .asmdołączonym.)

Dodaje również cele gałęzi do kodu. Inne deasemblery zazwyczaj demontują instrukcje skoku z tylko numerycznym miejscem docelowym i nie umieszczają żadnego znacznika w celu rozgałęzienia, aby pomóc ci znaleźć szczyt pętli i tak dalej.

Wskazuje również NOP wyraźniej niż inne deasemblery (wyjaśniając, kiedy jest wypełnienie, zamiast rozmontowywać go jako kolejną instrukcję).

Jest open source i łatwy do skompilowania dla systemu Linux. Można go rozłożyć na składnię NASM, YASM, MASM lub GNU (AT&T).

Przykładowe dane wyjściowe:

; 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
...

Zauważ, że to wyjście jest gotowe do złożenia z powrotem do pliku obiektowego, więc możesz modyfikować kod na poziomie źródła asm, zamiast za pomocą edytora szesnastkowego w kodzie maszynowym. (Więc nie musisz ograniczać się do utrzymywania tego samego rozmiaru). Bez żadnych zmian wynik powinien być prawie identyczny. Może jednak nie być, ponieważ demontaż takich rzeczy

  (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

nie ma niczego w źródle, aby upewnić się, że składa się z dłuższego kodowania, co pozostawia miejsce na relokacje, aby przepisać go z 32-bitowym przesunięciem.


Jeśli nie chcesz go instalować objconv, GNU binutils objdump -Mintel -djest bardzo użyteczny i będzie już zainstalowany, jeśli masz normalną konfigurację gcc dla Linuksa.

Peter Cordes
źródło
6

jest też ndisasm, który ma pewne dziwactwa, ale może być bardziej przydatny, jeśli używasz nasm. Zgadzam się z Michałem Mrozkiem, że chyba najlepiej jest objdump.

[później] możesz również sprawdzić ciasdis Alberta van der Horsta: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . może być trudny do zrozumienia, ale ma kilka interesujących funkcji, których prawdopodobnie nie znajdziesz nigdzie indziej.

jcomeau_ictx
źródło
2
W szczególności: home.hccnet.nl/awmvan.der.horst/ciasdis.html zawiera w sekcji „najnowsze osiągnięcia” pakiet Debiana, który można łatwo zainstalować. Z odpowiednimi instrukcjami (wykonuje skrypty) wygeneruje plik źródłowy, który zostanie ponownie złożony do dokładnie tego samego pliku binarnego. Nie znam żadnego pakietu, który to potrafi. Może być trudno skorzystać z instrukcji, które zamierzam opublikować na githubie z obszernymi przykładami.
Albert van der Horst
4

Użyj IDA Pro i Decompiler .

ta.speot.is
źródło
IDA wydaje się trochę przesadzone, zwłaszcza biorąc pod uwagę, że jest dość drogie
Michael Mrozek
1
darmowa wersja nie jest dostępna dla Linuksa, tylko ograniczona wersja demo. (szkoda, bo w systemie Windows to najlepszy deasembler, jakiego kiedykolwiek używałem)
Adrien Plisson,
IDA jest dobre, ale problem z IDA polega na tym, że robisz się leniwy, jeśli używasz do małych zadań. nie, ale możliwe.
cfernandezlinux
3

Możesz podejść cholernie blisko (ale nie cygara) do wygenerowania zestawu, który zostanie ponownie złożony, jeśli to jest to, co zamierzasz zrobić, używając tej dość prymitywnej i żmudnej sztuczki z rurociągiem (zastąp / bin / bash plikiem, który zamierzasz zdemontować i bash.S z tym, do czego zamierzasz wysłać dane wyjściowe):

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

Zwróć jednak uwagę, jak długo to trwa. Naprawdę chciałbym, żeby był lepszy sposób (lub, jeśli o to chodzi, deasembler zdolny do generowania kodu, który rozpozna asembler), ale niestety nie ma.

realkstrawn93
źródło
Łał! To jest fantastyczne. Przy okazji, jeśli chodzi o twój problem, dlaczego nie użyjesz do niego aliasu, aby pominąć wpisywanie tego ogromnego polecenia?
Bat
1

redaktor ht może deasemblować pliki binarne w wielu formatach. Jest podobny do Hiew, ale open source.

Aby zdemontować, otwórz plik binarny, a następnie naciśnij klawisz F6, a następnie wybierz elf / obraz.

arboreal84
źródło
0

Powiedzmy, że masz:

#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);
}

Aby uzyskać kod asemblera za pomocą gcc, możesz zrobić:

 g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.'

c++filt demantuje symbole

grep -vE '\s+\.' usuwa niepotrzebne informacje

Teraz, jeśli chcesz zwizualizować oznaczoną część, po prostu użyj:

g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.' | grep "MyTag BEGIN" -A 20

Z moim komputerem otrzymuję:

    # 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

Bardziej przyjaznym podejściem jest użycie: Compiler Explorer

Picaud Vincent
źródło
Jest to niezawodne tylko przy wyłączonej optymalizacji, w przeciwnym razie część operacji wewnątrz regionu może zostać zoptymalizowana do rzeczy na zewnątrz lub zostać zoptymalizowana poza nią. Możesz więc zobaczyć tylko niezgrabny -O0asm.
Peter Cordes