Як розібрати двійковий виконуваний файл у Linux, щоб отримати код збірки?


84

Мені сказали використовувати розбирач. Чи gccє щось вбудоване? Який найпростіший спосіб це зробити?



Пов’язане: Як видалити "шум" з виходу збірки GCC / clang? - якщо ви дійсно просто хочете побачити, що зробив компілятор, вам не завжди потрібно компілювати + посилання + розбирати.
Пітер Кордес,

Відповіді:


136

Я не думаю, що 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

9
Для Інтел-синтаксису: objdump -Mintel -d. Або розбірник файлів objconv Агнера Фога - найкращий із усіх, які я ще пробував (див. Мою відповідь). Додавання нумерованих міток до галузевих цілей - це дійсно приємно.
Пітер Кордес,

5
Корисні опції: objdump -drwC -Mintel. -rпоказує переміщення з таблиці символів. -Cдемонтує назви C ++. -Wуникає загортання рядків для довгих інструкцій. Якщо ви використовуєте його часто, це зручно: alias disas='objdump -drwC -Mintel'.
Пітер Кордес,

2
Додайте -Sдля відображення вихідний код, перемішаний з розбиранням. (Як вказується в іншій відповіді .)
Олександр Позднєєв

47

Цікавою альтернативою 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)


12

Ця відповідь специфічна для 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.


6

існує також ndisasm, який має деякі химерності, але може бути кориснішим, якщо ви використовуєте nasm. Я погоджуюся з Майклом Мрозеком, що objdump, мабуть, найкращий.

[пізніше], можливо, ви також захочете ознайомитися з ciasdis Альберта ван дер Горста: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . це може бути важко зрозуміти, але має кілька цікавих функцій, які ви, мабуть, не знайдете більше ніде.


2
Зокрема: home.hccnet.nl/awmvan.der.horst/ciasdis.html містить у розділі "останні розробки" пакет debian, який ви можете легко встановити. Завдяки належним інструкціям (це робить сценарії) він генерує вихідний файл, який знову збиратиметься в точно той самий двійковий файл. Мені не відомий жоден пакет, який може це зробити. Це може бути важко використати з інструкцій, я маю намір опублікувати в github із великими прикладами.
Альберт ван дер Горст,

4

Використовуйте IDA Pro та декомпілятор .


IDA здається трохи надмірним для цього, особливо враховуючи, що це досить дорого
Michael Mrozek

1
безкоштовна версія недоступна для Linux, лише обмежена демо-версія. (дуже погано, тому що на вікнах це найкращий розбирач, який я коли-небудь використовував)
Адріен Пліссон,

IDA - це добре, але проблема IDA полягає в тому, що ви лінуєтесь, якщо використовували для невеликих завдань .. gdb робить роботу більшістю всього, gdb простіше? ні, але можливо.
cfernandezlinux 02


3

Ви можете наблизитися (але без сигари) до створення збірки, яка буде збиратись повторно, якщо це те, що ви збираєтеся зробити, використовуючи цей досить грубий і нудно довгий трубопровідний трюк (замініть / 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

Однак зверніть увагу, як це довго. Я б дуже хотів, щоб був кращий спосіб (або, що стосується цього, розбірник, здатний вивести код, який розпізнає асемблер), але, на жаль, цього немає.


Оце Так! Це фантастика. До речі, щодо вашої проблеми, чому б вам не використати для неї псевдонім, щоб пропустити введення цієї величезної команди?
Кажан

1

ht редактор може розбирати двійкові файли у багатьох форматах. Це схоже на Hiew, але з відкритим кодом.

Щоб розібрати, відкрийте двійковий файл, потім натисніть F6, а потім виберіть ельф / зображення.


0

Скажімо, у вас є:

#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


Це надійно лише з вимкненою оптимізацією, інакше частини операцій усередині регіону можуть оптимізуватися на зовнішні речі або бути оптимізовані поза. Тож ви можете бачити лише незграбний -O0asm.
Пітер Кордес,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.