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

2024-10-14 08:40:00
admin
原创
134
摘要:问题描述:我被告知要使用反汇编程序。gcc它有内置功能吗?最简单的方法是什么?解决方案 1:我认为gcc它没有标志,因为它主要是一个编译器,但另一个 GNU 开发工具有。objdump采用-d/--disassemble标志:$ objdump -d /path/to/binary 反汇编如下:080483b...

问题描述:

我被告知要使用反汇编程序。gcc它有内置功能吗?最简单的方法是什么?


解决方案 1:

我认为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

解决方案 2:

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

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

解决方案 3:

此答案特定于 x86。可拆卸工具可以反汇编 AArch64、MIPS 或任何包含objdump和的机器代码llvm-objdump


Agner Fog 的反汇编程序,objconv,相当不错。它会在反汇编输出中添加注释,以解决性能问题(例如,16 位立即数指令导致的可怕的 LCP 停顿)。

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 binutilsobjdump -drwC -Mintel也非常实用,如果您有正常的 Linux gcc 设置,它已经安装好了。我alias disas='objdump -drwC -Mintel'在我的系统上使用。(-w没有换行,-C是 demangle,-r打印目标文件中的重定位。)

llvm-objdump -d也可以工作,并且可以从单个二进制文件中反汇编各种体系结构。 (与 GNU 不同,objdump您需要为每个架构单独编写一个,例如aarch64-linux-gnu-objdump -d。)同样,clang -O3 -target mips -cclang -O3 -target riscv32 -c或任何对您感兴趣的体系结构有用的编译,但兴趣不足以安装交叉编译器。 (https://godbolt.org/ Compiler Explorer 也是一个有用的资源;有关更多信息以及编写编译为有趣的 asm 的小函数,请参阅如何从 GCC/clang 汇编输出中删除“噪音”?

解决方案 4:

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

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

解决方案 5:

使用IDA Pro和反编译器。

解决方案 6:

您可能会发现 ODA 很有用。它是一款基于 Web 的反汇编程序,支持大量架构。

http://onlinedisassembler.com/

解决方案 7:

如果您打算这样做,那么使用这个相当粗糙和冗长的管道技巧(将 /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

但请注意这有多长。我真的希望有更好的方法(或者,一个能够输出汇编程序可以识别的代码的反汇编程序),但不幸的是没有。

解决方案 8:

ht 编辑器可以反汇编多种格式的二进制文件。它与 Hiew 类似,但是是开源的。

要反汇编,请打开二进制文件,然后按 F6 并选择 elf/image。

解决方案 9:

假设您有:

#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

解决方案 10:

使用:gcc -S ProgramName.c

例子:

#include <stdio.h>

int myFunc(int x, int y) {
    char e = 'A';
    printf("%c, %d, %d
", 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
"
    .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:

解决方案 11:

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用