Làm thế nào để tháo rời tệp thực thi nhị phân trong Linux để lấy mã lắp ráp?


81

Tôi được yêu cầu sử dụng một bộ tháo rời. Có gccbất cứ điều gì được xây dựng trong? cách dễ nhất để làm điều này là gì?



Liên quan: Làm thế nào để loại bỏ "tiếng ồn" khỏi đầu ra lắp ráp GCC / tiếng kêu? - nếu bạn thực sự chỉ muốn xem trình biên dịch đã làm gì, bạn không cần phải luôn biên dịch + liên kết + tháo rời.
Peter Cordes

Câu trả lời:


130

Tôi không nghĩ rằng gcccó một lá cờ cho nó, vì nó chủ yếu là một trình biên dịch, nhưng một công cụ phát triển GNU khác thì có. objdumplấy một -d/ --disassemblecờ:

$ objdump -d /path/to/binary

Việc tháo rời trông như thế này:

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
Đối với intel-cú pháp: objdump -Mintel -d. Hoặc trình gỡ bỏ objconv của Agner Fog là trình đẹp nhất mà tôi đã thử (xem câu trả lời của tôi). Thêm nhãn được đánh số vào mục tiêu chi nhánh thực sự rất hay.
Peter Cordes

5
Tùy chọn hữu ích: objdump -drwC -Mintel. -rhiển thị các vị trí từ bảng ký hiệu. -Chình tam giác tên C ++. -Wtránh gói dòng cho các hướng dẫn dài. Nếu bạn sử dụng nó thường xuyên, đây là tiện dụng: alias disas='objdump -drwC -Mintel'.
Peter Cordes

2
Thêm -Svào mã nguồn hiển thị xen lẫn với sự tháo gỡ. (Như đã chỉ ra trong một câu trả lời khác .)
Alexander Pozdneev

45

Một thay thế thú vị cho objdump là gdb. Bạn không phải chạy nhị phân hoặc có 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)

Với thông tin gỡ lỗi đầy đủ, nó thậm chí còn tốt hơn.

(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 có một tùy chọn tương tự (-S)


12

Câu trả lời này dành riêng cho x86. Các công cụ cầm tay có thể tháo rời AArch64, MIPS hoặc bất kỳ mã máy nào bao gồm objdumpllvm-objdump.


Disassembler Agner Sương mù của , objconvlà khá đẹp. Nó sẽ thêm nhận xét vào đầu ra tháo gỡ cho các vấn đề về hiệu suất (ví dụ như lỗi LCP đáng sợ từ các lệnh có hằng số tức thời 16 bit).

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

(Nó không nhận dạng -là viết tắt của stdout và mặc định xuất ra một tệp có tên tương tự với tệp đầu vào, khi được .asmbật.)

Nó cũng thêm các mục tiêu nhánh vào mã. Các trình tháo rời khác thường tháo rời các hướng dẫn nhảy chỉ bằng một đích số và không đặt bất kỳ điểm đánh dấu nào tại mục tiêu nhánh để giúp bạn tìm thấy đầu các vòng lặp, v.v.

Nó cũng chỉ ra NOP rõ ràng hơn so với các bộ tháo rời khác (làm rõ khi có phần đệm, thay vì tháo rời nó như một chỉ dẫn khác.)

Nó là mã nguồn mở và dễ biên dịch cho Linux. Nó có thể tháo rời thành cú pháp NASM, YASM, MASM hoặc GNU (AT&T).

Đầu ra mẫu:

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

Lưu ý rằng đầu ra này đã sẵn sàng để được lắp ráp trở lại thành tệp đối tượng, vì vậy bạn có thể chỉnh sửa mã ở cấp nguồn asm, thay vì với trình chỉnh sửa hex trên mã máy. (Vì vậy, bạn không bị giới hạn trong việc giữ cho mọi thứ có cùng kích thước.) Không có thay đổi, kết quả sẽ gần giống nhau. Tuy nhiên, nó có thể không được như vậy vì việc tháo rời những thứ như

  (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

không có bất kỳ thứ gì trong nguồn để đảm bảo rằng nó lắp ráp thành mã hóa dài hơn, để lại chỗ cho các vị trí để viết lại nó với độ lệch 32bit.


Nếu bạn không muốn cài đặt nó objconv, GNU binutils objdump -Mintel -drất có thể sử dụng và sẽ được cài đặt sẵn nếu bạn có thiết lập gcc Linux bình thường.


6

cũng có ndisasm, có một số điều kỳ quặc, nhưng có thể hữu ích hơn nếu bạn sử dụng nasm. Tôi đồng ý với Michael Mrozek rằng objdump có lẽ là tốt nhất.

[sau] bạn cũng có thể muốn xem ciasdis của Albert van der Horst: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . nó có thể khó hiểu, nhưng có một số tính năng thú vị mà bạn có thể sẽ không tìm thấy ở bất kỳ nơi nào khác.


2
Cụ thể: home.hccnet.nl/awmvan.der.horst/ciasdis.html có chứa gói debian "phát triển mới nhất" mà bạn có thể cài đặt dễ dàng. Với hướng dẫn thích hợp (nó thực hiện tập lệnh), nó sẽ tạo ra một tệp nguồn sẽ tập hợp lại thành cùng một tệp nhị phân chính xác. Tôi không biết bất kỳ gói nào có thể làm điều đó. Có thể khó sử dụng theo hướng dẫn, tôi định xuất bản trên github với các ví dụ sâu rộng.
Albert van der Horst,

3

Sử dụng IDA ProDecompiler .


IDA có vẻ hơi quá đáng chút cho điều này, đặc biệt là xem xét nó khá đắt tiền
Michael Mrozek

1
phiên bản miễn phí không có sẵn cho Linux, chỉ có phiên bản demo giới hạn. (vì quá xấu, trên cửa sổ, đó là bộ phận phân tách tốt nhất tôi đã từng sử dụng)
Adrien Plisson

IDA là tốt nhưng vấn đề của IDA là bạn sẽ lười biếng nếu bạn sử dụng cho các nhiệm vụ nhỏ .. gdb thực hiện công việc cho hầu hết mọi thứ, gdb dễ dàng hơn? không, nhưng có thể.
cfernandezlinux


2

Bạn có thể đến khá gần (nhưng không có xì gà) để tạo ra bộ phận lắp ráp sẽ lắp ráp lại, nếu đó là điều bạn đang có ý định làm, bằng cách sử dụng thủ thuật đường ống dài khá thô sơ và buồn tẻ này (thay thế / bin / bash bằng tệp bạn định tháo rời và bash.S với những gì bạn định gửi đầu ra):

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

Tuy nhiên, hãy lưu ý điều này là bao lâu. Tôi thực sự ước có một cách tốt hơn (hoặc, đối với vấn đề đó, một trình tháo gỡ có khả năng xuất mã mà trình hợp dịch sẽ nhận ra), nhưng tiếc là không có.


Chà! Cái này thật tuyệt. Btw, liên quan đến vấn đề của bạn, tại sao bạn không sử dụng một bí danh cho nó để bỏ qua việc gõ lệnh lớn này?
Bát

1

ht editor có thể tháo rời các tệp nhị phân ở nhiều định dạng. Nó tương tự như Hiew, nhưng mã nguồn mở.

Để tháo rời, hãy mở tệp nhị phân, sau đó nhấn F6 rồi chọn elf / image.


0

Giả sử rằng bạn có:

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

Để lấy mã lắp ráp bằng gcc, bạn có thể làm:

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

c++filt biểu tượng hình tam giác

grep -vE '\s+\.' loại bỏ một số thông tin vô ích

Bây giờ nếu bạn muốn hình dung phần được gắn thẻ, chỉ cần sử dụng:

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

Với máy tính của mình, tôi nhận được:

    # 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

Một cách tiếp cận thân thiện hơn là sử dụng: Compiler Explorer


Điều này chỉ đáng tin cậy khi tối ưu hóa bị tắt, nếu không các phần của hoạt động bên trong khu vực có thể tối ưu hóa thành nội dung bên ngoài hoặc được tối ưu hóa đi. Vì vậy, bạn chỉ có thể nhìn thấy -O0asm vụng về .
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.