如何在Linux中反汇编二进制可执行文件以获得汇编代码?

问题描述 投票:0回答:11

我被告知要使用反汇编程序。

gcc
有内置的东西吗?最简单的方法是什么?

c++ linux assembly executable disassembly
11个回答
206
投票

我不认为

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

63
投票

gdb 是 objdump 的一个有趣替代品。您不必运行二进制文件或拥有调试信息。

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


20
投票

此答案特定于 x86。可以反汇编 AArch64、MIPS 或任何机器代码的便携式工具,包括

objdump
llvm-objdump


Agner Fog 的反汇编器

objconv
,相当不错。它将向反汇编输出添加性能问题的注释(例如,来自具有 16 位立即常量的指令的可怕的 LCP 停顿)。

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

(它不会将

-
识别为标准输出的简写,并且默认输出到与输入文件名称相似的文件,并附加
.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 -drwC -Mintel
非常有用,如果你有正常的 Linux gcc 设置,它已经安装了。我在我的系统上使用
alias disas='objdump -drwC -Mintel'
。 (
-w
不换行,
-C
进行分解,
-r
在目标文件中打印重定位。)

llvm-objdump -d
也可以工作,并且可以从单个二进制文件中反汇编出各种架构。 (与 GNU
objdump
不同,您需要每个架构单独一个,如
aarch64-linux-gnu-objdump -d
。)类似地,
clang -O3 -target mips -c
clang -O3 -target riscv32 -c
或其他任何内容对于编译您感兴趣的体系结构很有用,但没有足够的兴趣麻烦安装一个交叉编译器。 (https://godbolt.org/编译器资源管理器也是一个有用的资源;请参阅如何从 GCC/clang 程序集输出中删除“噪音”?了解更多相关信息以及编写编译为的小函数有趣的汇编。)


6
投票

还有 ndisasm,它有一些怪癖,但如果您使用 nasm 会更有用。我同意 Michael Mrozek 的观点,即 objdump 可能是最好的。

[稍后] 您可能还想查看 Albert van der Horst 的 ciasdis:http://home.hccnet.nl/a.w.m.van.der.horst/forthassembler.html。它可能很难理解,但有一些您在其他地方可能找不到的有趣功能。


4
投票

使用IDA Pro反编译器


4
投票

您可能会发现 ODA 很有用。它是一个基于网络的反汇编器,支持大量架构。

http://onlinedisassembler.com/


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,然后选择 elf/image。


1
投票

假设您有:

#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


0
投票

用途:

gcc -S ProgramName.c

示例:

#include <stdio.h>

int myFunc(int x, int y) {
    char e = 'A';
    printf("%c, %d, %d\n", e, x, y);
    return 1;
}

int main() {
    int z = myFunc(5, 7);
    return 0;
}

品牌:

    .file   "temp.c"
    .text
    .section    .rodata
.LC0:
    .string "%c, %d, %d\n"
    .text
    .globl  myFunc
    .type   myFunc, @function
myFunc:
.LFB0:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movl    %edi, -20(%rbp)
    movl    %esi, -24(%rbp)
    movb    $65, -1(%rbp)
    movsbl  -1(%rbp), %eax
    movl    -24(%rbp), %ecx
    movl    -20(%rbp), %edx
    movl    %eax, %esi
    leaq    .LC0(%rip), %rax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $1, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   myFunc, .-myFunc
    .globl  main
    .type   main, @function
main:
.LFB1:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $7, %esi
    movl    $5, %edi
    call    myFunc
    movl    %eax, -4(%rbp)
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 12.3.0-1ubuntu1~23.04) 12.3.0"
    .section    .note.GNU-stack,"",@progbits
    .section    .note.gnu.property,"a"
    .align 8
    .long   1f - 0f
    .long   4f - 1f
    .long   5
0:
    .string "GNU"
1:
    .align 8
    .long   0xc0000002
    .long   3f - 2f
2:
    .long   0x3
3:
    .align 8
4:

-3
投票

使用 ghidra:https://ghidra-sre.org/。它已经安装在 Kali Linux 上。

© www.soinside.com 2019 - 2024. All rights reserved.