Мені сказали використовувати розбирач. Чи gcc
є щось вбудоване? Який найпростіший спосіб це зробити?
Мені сказали використовувати розбирач. Чи gcc
є щось вбудоване? Який найпростіший спосіб це зробити?
Відповіді:
Я не думаю, що gcc
має прапор для цього, оскільки це в першу чергу компілятор, але інший із засобів розробки GNU має. objdump
приймає -d
/ --disassemble
прапор:
$ objdump -d /path/to/binary
Розбирання виглядає так:
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
. Або розбірник файлів objconv Агнера Фога - найкращий із усіх, які я ще пробував (див. Мою відповідь). Додавання нумерованих міток до галузевих цілей - це дійсно приємно.
objdump -drwC -Mintel
. -r
показує переміщення з таблиці символів. -C
демонтує назви C ++. -W
уникає загортання рядків для довгих інструкцій. Якщо ви використовуєте його часто, це зручно: alias disas='objdump -drwC -Mintel'
.
-S
для відображення вихідний код, перемішаний з розбиранням. (Як вказується в іншій відповіді .)
Цікавою альтернативою objdump є gdb. Вам не потрібно запускати двійковий файл або мати debuginfo.
$ 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)
З повною інформацією про налагодження це ще краще.
(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 має подібний варіант (-S)
Ця відповідь специфічна для x86. Портативні інструменти, які можуть розбирати AArch64, MIPS або будь-який інший машинний код objdump
та llvm-objdump
.
Дізассемблер Agner Fog в , objconv
, дуже приємно. Це додасть коментарі до результатів розбирання для проблем із продуктивністю (наприклад, страшний зрив LCP з інструкцій з 16-бітними безпосередніми константами, наприклад).
objconv -fyasm a.out /dev/stdout | less
(Він не розпізнає -
як скорочення для stdout і за замовчуванням виводить у файл із подібною назвою до вхідного файлу, з .asm
позначкою.)
Він також додає цільові гілки до коду. Інші розбирачі зазвичай розбирають інструкції переходу лише з числовим призначенням, і не ставлять ніяких позначок на цілі гілки, щоб допомогти вам знайти верхню частину петель тощо.
Він також вказує NOP більш чітко, ніж інші десемблери (що робить зрозумілим, коли є заповнення, а не розбирає його як чергову інструкцію).
Це відкритий код, який легко компілювати для Linux. Він може розбиратися на синтаксис NASM, YASM, MASM або GNU (AT&T).
Вихідні дані:
; 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
...
Зверніть увагу, що цей результат готовий до збирання назад у файл об’єкта, тому ви можете налаштувати код на рівні джерела asm, а не за допомогою шестнадцяткового редактора на машинному коді. (Таким чином, ви не обмежуєтеся тим, щоб зберігати речі однакового розміру.) Без змін результат повинен бути майже однаковим. Однак, можливо, цього не буде, оскільки розбирання подібних речей
(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
не має нічого у джерелі, щоб переконатися, що воно збирається до більш тривалого кодування, що залишає місце для переміщень, щоб переписати його зі зміщенням у 32 біти.
Якщо ви не хочете встановлювати його objconv, GNU binutils objdump -Mintel -d
дуже корисний і вже буде встановлений, якщо у вас нормальна установка gcc для Linux.
існує також ndisasm, який має деякі химерності, але може бути кориснішим, якщо ви використовуєте nasm. Я погоджуюся з Майклом Мрозеком, що objdump, мабуть, найкращий.
[пізніше], можливо, ви також захочете ознайомитися з ciasdis Альберта ван дер Горста: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . це може бути важко зрозуміти, але має кілька цікавих функцій, які ви, мабуть, не знайдете більше ніде.
Використовуйте IDA Pro та декомпілятор .
Вам може бути корисна ОПР. Це веб-розбірник, який підтримує безліч архітектур.
Ви можете наблизитися (але без сигари) до створення збірки, яка буде збиратись повторно, якщо це те, що ви збираєтеся зробити, використовуючи цей досить грубий і нудно довгий трубопровідний трюк (замініть / bin / bash файлом, який ви збираєтеся розібрати, і bash.S з тим, до чого ви збираєтесь надіслати вихідні дані):
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
Однак зверніть увагу, як це довго. Я б дуже хотів, щоб був кращий спосіб (або, що стосується цього, розбірник, здатний вивести код, який розпізнає асемблер), але, на жаль, цього немає.
ht редактор може розбирати двійкові файли у багатьох форматах. Це схоже на Hiew, але з відкритим кодом.
Щоб розібрати, відкрийте двійковий файл, потім натисніть F6, а потім виберіть ельф / зображення.
Скажімо, у вас є:
#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);
}
Щоб отримати код збірки за допомогою gcc, ви можете зробити:
g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.'
c++filt
демонтує символи
grep -vE '\s+\.'
видаляє деяку марну інформацію
Тепер, якщо ви хочете візуалізувати позначену частину, просто використовуйте:
g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.' | grep "MyTag BEGIN" -A 20
За допомогою комп’ютера я отримую:
# 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
Більш дружній підхід полягає у використанні: Compiler Explorer
-O0
asm.