Powiedziano mi, żebym użył dezasemblera. Czy gcc
coś jest wbudowane? Jaki jest najłatwiejszy sposób na zrobienie tego?
Powiedziano mi, żebym użył dezasemblera. Czy gcc
coś jest wbudowane? Jaki jest najłatwiejszy sposób na zrobienie tego?
Odpowiedzi:
Myślę, że nie gcc
ma na to flagi, ponieważ jest to przede wszystkim kompilator, ale inne narzędzie programistyczne GNU ma. objdump
przyjmuje 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
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.
objdump -drwC -Mintel
. -r
pokazuje przemieszczenia z tabeli symboli. -C
demantuje nazwy C ++. -W
unika zawijania wierszy w przypadku długich instrukcji. Jeśli używamy go często, to jest poręczny: alias disas='objdump -drwC -Mintel'
.
-S
aby wyświetlić kod źródłowy zmieszany z deasemblacją. (Jak wskazano w innej odpowiedzi .)
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)
Ta odpowiedź jest specyficzna dla x86. Przenośne narzędzia, które mogą rozmontować AArch64, MIPS lub dowolny kod maszynowy, w tym objdump
i llvm-objdump
.
Dezasembler Agner Fog jest , objconv
jest 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 .asm
dołą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 -d
jest bardzo użyteczny i będzie już zainstalowany, jeśli masz normalną konfigurację gcc dla Linuksa.
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.
Użyj IDA Pro i Decompiler .
Może się przydać ODA. Jest to dezasembler internetowy, który obsługuje mnóstwo architektur.
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.
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.
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
-O0
asm.