Bootstrap

二进制 分析工具:Radare2、r2frida、Binutils、file、string、as、nm、ldd、objdump、readelf、strip

1、二进制 分析工具

工欲善其事,必先利其器,在二进制安全的学习中,​使用工具尤为重要。遇到一个不熟悉的文件时, 首先要确定 "这是什么类型的文件",回答这个问题的首要原则是,绝不要根据文件的扩展名来确定文件的类型。这是最基本的原则。在脑子里建立起 "文件扩展名并无实际意义" 的印象后,就可以开始考虑学习下面的实用工具。

2、r2frida ( Radare2 + Frida )

github:https://github.com/nowsecure/r2frida

Radare2 和 Frida 在一起会更好:https://github.com/nowsecure/r2frida

r2frida 简 介

r2frida 是将 Radare2 和 Frida 的功能合二为一的强大工具,该工具本质上是一个Radare2 的自包含插件,可以帮助研究人员利用 Frida 的功能实现对目标进程的远程安全检测和通信管理。

Radare2 项目提供了针对逆向工程分析的完整工具链,功能也比较稳定,而且还支持利用其他编程语言和工具来扩展其功能。而 Frida 则是一个动态指令工具包,可以将 JavaScript 代码注入到正在运行的目标进程进行操作,而且还可以与脚本进行通信。

应用场景

  • 逆向工程:通过Frida脚本直接在目标进程中运行,无需修改源码。
  • 动态调试:动态跟踪代码执行,拦截函数调用,替换实现或添加钩子。
  • 移动设备应用测试:针对iOS和Android设备进行远程调试,不局限于同一网络环境。
  • 自动化安全审计:使用 r2pipe 自动化执行 r2 和 Frida 命令,提升效率。

项目特点

  • 直观易用:与Radare2无缝集成,提供丰富的命令行操作。
  • 多语言支持:可以在目标进程中执行C、JavaScript或TypeScript代码。
  • 跨平台兼容:覆盖多种硬件架构和操作系统。
  • 独立安装:不需要在主机上安装Frida工具,仅需r2frida即可运行。
  • 动态扩展:支持编写插件以增强r2frida的功能,插件在代理端运行。
  • 安全性:可变更页权限、修补代码和数据,增加调试安全性。

使用 r2frida

一旦安装完成,你可以使用以下命令开始探索:$ r2 'frida://0'

$ r2 'frida://?'
r2 frida://[action]/[link]/[device]/[target]
* action = list | apps | attach | spawn | launch
* link   = local | usb | remote host:port
* device = '' | host:port | device-id
* target = pid | appname | process-name | program-in-path | abspath
Local:
* frida://?                        # 显示工具帮助信息和退出
* frida://                         # 枚举本地进程
* frida://0                        # 连接至frida-helper
* frida:///usr/local/bin/rax2        # 生成进程的绝对路径
* frida://rax2                     # 生成进程的相对路径local/bin需在PATH中设置
* frida://spawn/$(program)         #在当前系统生成一个新的进程
* frida://attach/(target)            # 在当前主机连接至目标PID
USB:
* frida://list/usb//                # 枚举第一个USB设备的进程
* frida://apps/usb//               # 枚举第一个USB设备的应用程序
* frida://attach/usb//12345        # 连接至第一个USB设备的给定pid
* frida://spawn/usb//appname       # 在第一个USB设备中生成一个App
* frida://launch/usb//appname      # 在第一个USB设备中生成+恢复一个App
Remote:
* frida://attach/remote/10.0.0.3:9999/558 # 连接远程frida-server的pid 558
  R2FRIDA_SAFE_IO=0|1              # 解决Android/thumb上的Frida问题
  R2FRIDA_DEBUG=0|1               # 用于调试参数解析行为
  R2FRIDA_COMPILER_DISABLE=0|1  # 禁用新的Frida TypeScript编译器(`:. foo.ts`)
  R2FRIDA_AGENT_SCRIPT=[file]      # r2frida代理文件路径

通过r2frida,你可以列出本地进程(frida://),附加到指定进程(frida://attach/PID),或者在远程设备上启动或附加进程(frida://remote/hostname:port/PID)。

r2frida 提供了大量方便的命令,例如 :i 获取目标信息,:dm 列出内存映射,:dt 追踪函数,:.-pluginName 卸载插件等。

总的来说,r2frida 是一个强大的开源项目,为软件安全研究人员、开发者和逆向工程师提供了深入理解应用程序内部行为的优秀工具。无论是研究未知代码还是调试复杂问题,r2frida 都能助你一臂之力。立即尝试,开启你的代码探索之旅吧!

使用样例

$ r2 frida://0     # 与frida -p 0相同,连接至一个本地会话
我们可以通过进程名称或pid连接、生成或启动任意进程,下列命令将连接到第一个名为rax2的进程:

$ r2 frida://rax2   # 连接至第一个名为rax2的进程

$ r2 frida://1234  # 连接至指定pid
使用源码的绝对路径将生成一个进程:

$ r2 frida:///bin/ls

[0x00000000]> :dc        # 目标应用程序继续执行
添加其他参数运行:

$ r2 frida://"/bin/ls -al"
下列命令可以使用USB调试iOS/Android应用程序:

$ r2 frida://spawn/usb/         # 枚举设备

$ r2 frida://spawn/usb//        # 枚举iOS设备中的应用程序

$ r2 frida://spawn/usb//Weather # 运行Weather天气App

命令解析
:i         # 获取目标详情
.:i*        # 将目标进程详情导入至local r2
:?         # 显示所有可用命令
:dm       # 枚举映射,使用':dm|head'搜索程序基地址
:iE        # 枚举当前二进制的导出
:dt fread   # 跟踪'fread'函数
:dt-*      # 删除所有跟踪

3、Radare2 指南

github:https://github.com/radareorg/radare2

全功能的二进制文件分析工具 Radare2 指南:https://linux.cn/article-13074-1.html

Radare2 (也称为 r2) 是一个 类 Unix 系统上的逆向工程框架和命令行工具集,它名字中的 2 是因为这个版本从头开始重写的,使其更加模块化。

在《Linux 上分析二进制文件的 10 种方法》中,使用了 Linux 中原生工具集来分析二进制文件。但如果你想进一步探索你的二进制文件,你就需要一个为二进制分析定制的工具。如果你是二进制分析的新手,并且大多使用的是脚本语言,这篇文章《GNU binutils 里的九种武器》可以帮助你开始学习编译过程和什么是二进制。

Radare2 是全功能的二进制文件分析工具,二进制文件分析神器。

Radare2 手册:https://heersin.gitbook.io/radare2/intro

为什么需要一个新工具

如果现有的 Linux 原生工具也能做类似的事情,你自然会问为什么需要另一个工具。嗯,这和你用手机做闹钟、做笔记、做相机、听音乐、上网、偶尔打电话和接电话的原因是一样的。以前,使用单独的设备和工具处理这些功能 —— 比如拍照的实体相机,记笔记的小记事本,起床的床头闹钟等等。对用户来说,有一个设备来做多件(但相关的)事情是方便的。另外,杀手锏就是独立功能之间的互操作性

同样,即使许多 Linux 工具都有特定的用途,但在一个工具中捆绑类似(和更好)的功能是非常有用的。这就是为什么我认为 Radare2 应该是你需要处理二进制文件时的首选工具。

根据其 GitHub 简介,Radare2(也称为 r2)是一个“类 Unix 系统上的逆向工程框架和命令行工具集”。它名字中的 “2” 是因为这个版本从头开始重写的,使其更加模块化。

为什么选择 Radare2

有大量(非原生的)Linux 工具可用于二进制分析,为什么要选择 Radare2 呢?我的理由很简单。

首先,它是一个开源项目,有一个活跃而健康的社区。如果你正在寻找新颖的功能或提供着 bug 修复的工具,这很重要。

其次,Radare2 可以在命令行上使用,而且它有一个功能丰富的图形用户界面(GUI)环境,叫做 Cutter,适合那些对 GUI 比较熟悉的人。作为一个长期使用 Linux 的用户,我对习惯于在 shell 上输入。虽然熟悉 Radare2 的命令稍微有一点学习曲线,但我会把它比作 学习 Vim。你可以先学习基本的东西,一旦你掌握了它们,你就可以继续学习更高级的东西。很快,它就变成了肌肉记忆。

第三,Radare2 通过插件可以很好的支持外部工具。例如,最近开源的 Ghidra 二进制分析和逆向工具reversing tool很受欢迎,因为它的反编译器功能是逆向软件的关键要素。你可以直接从 Radare2 控制台安装 Ghidra 反编译器并使用,这很神奇,让你两全其美。

开始使用 Radare2

要安装 Radare2,只需克隆其存储库并运行 user.sh 脚本。如果你的系统上还没有一些预备软件包,你可能需要安装它们。一旦安装完成,运行 r2 -v 命令来查看 Radare2 是否被正确安装:

$ git clone https://github.com/radareorg/radare2.git
$ cd radare2
$ ./sys/user.sh
# version
$ r2 -v
radare2 4.6.0-git 25266 @ linux-x86-64 git.4.4.0-930-g48047b317
commit: 48047b3171e6ed0480a71a04c3693a0650d03543 build: 2020-11-17__09:31:03
$

获取二进制测试样本

现在 r2 已经安装好了,你需要一个样本二进制程序来试用它。你可以使用任何系统二进制文件(lsbash 等),但为了使本教程的内容简单,请编译以下 C 程序:

cat adder.c

#include <stdio.h>
int adder(int num) {
        return num + 1;
}
int main() {
        int res, num1 = 100;
        res = adder(num1);
        printf("Number now is  : %d\n", res);
        return 0;
}

$ gcc adder.c -o adder
$ file adder
adder: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9d4366f7160e1ffb46b14466e8e0d70f10de2240, not stripped
$ ./adder
Number now is  : 101

加载二进制文件

要分析二进制文件,你必须在 Radare2 中加载它。通过提供文件名作为 r2 命令的一个命令行参数来加载它。你会进入一个独立的 Radare2 控制台,这与你的 shell 不同。要退出控制台,你可以输入 Quit 或 Exit 或按 Ctrl+D

$ r2 ./adder
 -- Learn pancake as if you were radare!
[0x004004b0]> quit
$

分析二进制

在你探索二进制之前,你必须让 r2 为你分析它。你可以通过在 r2 控制台中运行 aaa 命令来实现:

$ r2 ./adder
 -- Sorry, radare2 has experienced an internal error.
[0x004004b0]>
[0x004004b0]>
[0x004004b0]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x004004b0]>

这意味着每次你选择一个二进制文件进行分析时,你必须在加载二进制文件后输入一个额外的命令 aaa。你可以绕过这一点,在命令后面跟上 -A 来调用 r2;这将告诉 r2 为你自动分析二进制:

$ r2 -A ./adder
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
 -- Already up-to-date.
[0x004004b0]>

获取一些关于二进制的基本信息

在开始分析一个二进制文件之前,你需要一些背景信息。在许多情况下,这可以是二进制文件的格式(ELF、PE 等)、二进制的架构(x86、AMD、ARM 等),以及二进制是 32 位还是 64 位。方便的 r2 的 iI 命令可以提供所需的信息:

[0x004004b0]> iI
arch     x86
baddr    0x400000
binsz    14724
bintype  elf
bits     64
canary   false
class    ELF64
compiler GCC: (GNU) 8.3.1 20190507 (Red Hat 8.3.1-4)
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      false
relocs   true
relro    partial
rpath    NONE
sanitiz  false
static   false
stripped false
subsys   linux
va       true
[0x004004b0]>
[0x004004b0]>

导入和导出

通常情况下,当你知道你要处理的是什么样的文件后,你就想知道二进制程序使用了什么样的标准库函数,或者了解程序的潜在功能。在本教程中的示例 C 程序中,唯一的库函数是 printf,用来打印信息。你可以通过运行 ii 命令看到这一点,它显示了该二进制所有导入的库:

[0x004004b0]> ii
[Imports]
nth vaddr      bind   type   lib name
―――――――――――――――――――――――――――――――――――――
1   0x00000000 WEAK   NOTYPE     _ITM_deregisterTMCloneTable
2   0x004004a0 GLOBAL FUNC       printf
3   0x00000000 GLOBAL FUNC       __libc_start_main
4   0x00000000 WEAK   NOTYPE     __gmon_start__
5   0x00000000 WEAK   NOTYPE     _ITM_registerTMCloneTable

该二进制也可以有自己的符号、函数或数据。这些函数通常显示在 Exports 下。这个测试的二进制导出了两个函数:main 和 adder。其余的函数是在编译阶段,当二进制文件被构建时添加的。加载器需要这些函数来加载二进制文件(现在不用太关心它们):

[0x004004b0]>
[0x004004b0]> iE
[Exports]
nth paddr       vaddr      bind   type   size lib name
――――――――――――――――――――――――――――――――――――――――――――――――――――――
82   0x00000650 0x00400650 GLOBAL FUNC   5        __libc_csu_fini
85   ---------- 0x00601024 GLOBAL NOTYPE 0        _edata
86   0x00000658 0x00400658 GLOBAL FUNC   0        _fini
89   0x00001020 0x00601020 GLOBAL NOTYPE 0        __data_start
90   0x00000596 0x00400596 GLOBAL FUNC   15       adder
92   0x00000670 0x00400670 GLOBAL OBJ    0        __dso_handle
93   0x00000668 0x00400668 GLOBAL OBJ    4        _IO_stdin_used
94   0x000005e0 0x004005e0 GLOBAL FUNC   101      __libc_csu_init
95   ---------- 0x00601028 GLOBAL NOTYPE 0        _end
96   0x000004e0 0x004004e0 GLOBAL FUNC   5        _dl_relocate_static_pie
97   0x000004b0 0x004004b0 GLOBAL FUNC   47       _start
98   ---------- 0x00601024 GLOBAL NOTYPE 0        __bss_start
99   0x000005a5 0x004005a5 GLOBAL FUNC   55       main
100  ---------- 0x00601028 GLOBAL OBJ    0        __TMC_END__
102  0x00000468 0x00400468 GLOBAL FUNC   0        _init
[0x004004b0]>

哈希信息

如何知道两个二进制文件是否相似?你不能只是打开一个二进制文件并查看里面的源代码。在大多数情况下,二进制文件的哈希值(md5sum、sha1、sha256)是用来唯一识别它的。你可以使用 it 命令找到二进制的哈希值:

[0x004004b0]> it
md5 7e6732f2b11dec4a0c7612852cede670
sha1 d5fa848c4b53021f6570dd9b18d115595a2290ae
sha256 13dd5a492219dac1443a816ef5f91db8d149e8edbf26f24539c220861769e1c2
[0x004004b0]>

函数

代码按函数分组;要列出二进制中存在的函数,请运行 afl 命令。下面的列表显示了 main 函数和 adder 函数。通常,以 sym.imp 开头的函数是从标准库(这里是 glibc)中导入的:

[0x004004b0]> afl
0x004004b0    1 46           entry0
0x004004f0    4 41   -> 34   sym.deregister_tm_clones
0x00400520    4 57   -> 51   sym.register_tm_clones
0x00400560    3 33   -> 32   sym.__do_global_dtors_aux
0x00400590    1 6            entry.init0
0x00400650    1 5            sym.__libc_csu_fini
0x00400658    1 13           sym._fini
0x00400596    1 15           sym.adder
0x004005e0    4 101          loc..annobin_elf_init.c
0x004004e0    1 5            loc..annobin_static_reloc.c
0x004005a5    1 55           main
0x004004a0    1 6            sym.imp.printf
0x00400468    3 27           sym._init
[0x004004b0]>

交叉引用

在 C 语言中,main 函数是一个程序开始执行的地方。理想情况下,其他函数都是从 main 函数调用的,在退出程序时,main 函数会向操作系统返回一个退出状态。这在源代码中是很明显的,然而,二进制程序呢?如何判断 adder 函数的调用位置呢?

你可以使用 axt 命令,后面加上函数名,看看 adder 函数是在哪里调用的;如下图所示,它是从 main 函数中调用的。这就是所谓的交叉引用cross-referencing。但什么调用 main 函数本身呢?从下面的 axt main 可以看出,它是由 entry0 调用的(关于 entry0 的学习我就不说了,留待读者练习)。

[0x004004b0]> axt sym.adder
main 0x4005b9 [CALL] call sym.adder
[0x004004b0]>
[0x004004b0]> axt main
entry0 0x4004d1 [DATA] mov rdi, main
[0x004004b0]>

寻找定位

在处理文本文件时,你经常通过引用行号和行或列号在文件内移动;在二进制文件中,你需要使用地址。这些是以 0x 开头的十六进制数字,后面跟着一个地址。要找到你在二进制中的位置,运行 s 命令。要移动到不同的位置,使用 s 命令,后面跟上地址。

函数名就像标签一样,内部用地址表示。如果函数名在二进制中(未剥离的),可以使用函数名后面的 s 命令跳转到一个特定的函数地址。同样,如果你想跳转到二进制的开始,输入 s 0

[0x004004b0]> s
0x4004b0
[0x004004b0]>
[0x004004b0]> s main
[0x004005a5]>
[0x004005a5]> s
0x4005a5
[0x004005a5]>
[0x004005a5]> s sym.adder
[0x00400596]>
[0x00400596]> s
0x400596
[0x00400596]>
[0x00400596]> s 0
[0x00000000]>
[0x00000000]> s
0x0
[0x00000000]>

十六进制视图

通常情况下,原始二进制没有意义。在十六进制模式下查看二进制及其等效的 ASCII 表示法会有帮助:

[0x004004b0]> s main
[0x004005a5]>
[0x004005a5]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x004005a5  5548 89e5 4883 ec10 c745 fc64 0000 008b  UH..H....E.d....
0x004005b5  45fc 89c7 e8d8 ffff ff89 45f8 8b45 f889  E.........E..E..
0x004005c5  c6bf 7806 4000 b800 0000 00e8 cbfe ffff  ..x.@...........
0x004005d5  b800 0000 00c9 c30f 1f40 00f3 0f1e fa41  [email protected]
0x004005e5  5749 89d7 4156 4989 f641 5541 89fd 4154  WI..AVI..AUA..AT
0x004005f5  4c8d 2504 0820 0055 488d 2d04 0820 0053  L.%.. .UH.-.. .S
0x00400605  4c29 e548 83ec 08e8 57fe ffff 48c1 fd03  L).H....W...H...
0x00400615  741f 31db 0f1f 8000 0000 004c 89fa 4c89  t.1........L..L.
0x00400625  f644 89ef 41ff 14dc 4883 c301 4839 dd75  .D..A...H...H9.u
0x00400635  ea48 83c4 085b 5d41 5c41 5d41 5e41 5fc3  .H...[]A\A]A^A_.
0x00400645  9066 2e0f 1f84 0000 0000 00f3 0f1e fac3  .f..............
0x00400655  0000 00f3 0f1e fa48 83ec 0848 83c4 08c3  .......H...H....
0x00400665  0000 0001 0002 0000 0000 0000 0000 0000  ................
0x00400675  0000 004e 756d 6265 7220 6e6f 7720 6973  ...Number now is
0x00400685  2020 3a20 2564 0a00 0000 0001 1b03 3b44    : %d........;D
0x00400695  0000 0007 0000 0000 feff ff88 0000 0020  ...............
[0x004005a5]>

反汇编

如果你使用的是编译后的二进制文件,则无法查看源代码。编译器将源代码转译成 CPU 可以理解和执行的机器语言指令;其结果就是二进制或可执行文件。然而,你可以查看汇编指令(的助记词)来理解程序正在做什么。例如,如果你想查看 main 函数在做什么,你可以使用 s main 寻找 main 函数的地址,然后运行 pdf 命令来查看反汇编的指令。

要理解汇编指令,你需要参考体系结构手册(这里是 x86),它的应用二进制接口(ABI,或调用惯例),并对堆栈的工作原理有基本的了解:

[0x004004b0]> s main
[0x004005a5]>
[0x004005a5]> s
0x4005a5
[0x004005a5]>
[0x004005a5]> pdf
            ; DATA XREF from entry0 @ 0x4004d1
┌ 55: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_8h @ rbp-0x8
│           ; var int64_t var_4h @ rbp-0x4
│           0x004005a5      55             push rbp
│           0x004005a6      4889e5         mov rbp, rsp
│           0x004005a9      4883ec10       sub rsp, 0x10
│           0x004005ad      c745fc640000.  mov dword [var_4h], 0x64    ; 'd' ; 100
│           0x004005b4      8b45fc         mov eax, dword [var_4h]
│           0x004005b7      89c7           mov edi, eax
│           0x004005b9      e8d8ffffff     call sym.adder
│           0x004005be      8945f8         mov dword [var_8h], eax
│           0x004005c1      8b45f8         mov eax, dword [var_8h]
│           0x004005c4      89c6           mov esi, eax
│           0x004005c6      bf78064000     mov edi, str.Number_now_is__:__d ; 0x400678 ; "Number now is  : %d\n" ; const char *format
│           0x004005cb      b800000000     mov eax, 0
│           0x004005d0      e8cbfeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x004005d5      b800000000     mov eax, 0
│           0x004005da      c9             leave
└           0x004005db      c3             ret
[0x004005a5]>

这是 adder 函数的反汇编结果:

[0x004005a5]> s sym.adder
[0x00400596]>
[0x00400596]> s
0x400596
[0x00400596]>
[0x00400596]> pdf
            ; CALL XREF from main @ 0x4005b9
┌ 15: sym.adder (int64_t arg1);
│           ; var int64_t var_4h @ rbp-0x4
│           ; arg int64_t arg1 @ rdi
│           0x00400596      55             push rbp
│           0x00400597      4889e5         mov rbp, rsp
│           0x0040059a      897dfc         mov dword [var_4h], edi     ; arg1
│           0x0040059d      8b45fc         mov eax, dword [var_4h]
│           0x004005a0      83c001         add eax, 1
│           0x004005a3      5d             pop rbp
└           0x004005a4      c3             ret
[0x00400596]>

字符串

查看二进制中存在哪些字符串可以作为二进制分析的起点。字符串是硬编码到二进制中的,通常会提供重要的提示,可以让你将重点转移到分析某些区域。在二进制中运行 iz 命令来列出所有的字符串。这个测试二进制中只有一个硬编码的字符串:

[0x004004b0]> iz
[Strings]
nth paddr      vaddr      len size section type  string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000678 0x00400678 20  21   .rodata ascii Number now is  : %d\n
[0x004004b0]>

交叉引用字符串

和函数一样,你可以交叉引用字符串,看看它们是从哪里被打印出来的,并理解它们周围的代码:

[0x004004b0]> ps @ 0x400678
Number now is  : %d
[0x004004b0]>
[0x004004b0]> axt 0x400678
main 0x4005c6 [DATA] mov edi, str.Number_now_is__:__d
[0x004004b0]>

可视模式

当你的代码很复杂,有多个函数被调用时,很容易迷失方向。如果能以图形或可视化的方式查看哪些函数被调用,根据某些条件采取了哪些路径等,会很有帮助。在移动到感兴趣的函数后,可以通过 VV 命令来探索 r2 的可视化模式。例如,对于 adder 函数:

[0x004004b0]> s sym.adder
[0x00400596]>
[0x00400596]> VV

调试器

到目前为止,你一直在做的是静态分析 —— 你只是在看二进制文件中的东西,而没有运行它,有时你需要执行二进制文件,并在运行时分析内存中的各种信息。r2 的内部调试器允许你运行二进制文件、设置断点、分析变量的值、或者转储寄存器的内容。

用 -d 标志启动调试器,并在加载二进制时添加 -A 标志进行分析。你可以通过使用 db <function-name> 命令在不同的地方设置断点,比如函数或内存地址。要查看现有的断点,使用 dbi 命令。一旦你放置了断点,使用 dc 命令开始运行二进制文件。你可以使用 dbt 命令查看堆栈,它可以显示函数调用。最后,你可以使用 drr 命令转储寄存器的内容:

$ r2 -d -A ./adder
Process with PID 17453 started...
= attach 17453 17453
bin.baddr 0x00400000
Using 0x400000
asm.bits 64
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
 -- git checkout hamster
[0x7f77b0a28030]>
[0x7f77b0a28030]> db main
[0x7f77b0a28030]>
[0x7f77b0a28030]> db sym.adder
[0x7f77b0a28030]>
[0x7f77b0a28030]> dbi
0 0x004005a5 E:1 T:0
1 0x00400596 E:1 T:0
[0x7f77b0a28030]>
[0x7f77b0a28030]> afl | grep main
0x004005a5    1 55           main
[0x7f77b0a28030]>
[0x7f77b0a28030]> afl | grep sym.adder
0x00400596    1 15           sym.adder
[0x7f77b0a28030]>
[0x7f77b0a28030]> dc
hit breakpoint at: 0x4005a5
[0x004005a5]>
[0x004005a5]> dbt
0  0x4005a5           sp: 0x0                 0    [main]  main sym.adder+15
1  0x7f77b0687873     sp: 0x7ffe35ff6858      0    [??]  section..gnu.build.attributes-1345820597
2  0x7f77b0a36e0a     sp: 0x7ffe35ff68e8      144  [??]  map.usr_lib64_ld_2.28.so.r_x+65034
[0x004005a5]> dc
hit breakpoint at: 0x400596
[0x00400596]> dbt
0  0x400596           sp: 0x0                 0    [sym.adder]  rip entry.init0+6
1  0x4005be           sp: 0x7ffe35ff6838      0    [main]  main+25
2  0x7f77b0687873     sp: 0x7ffe35ff6858      32   [??]  section..gnu.build.attributes-1345820597
3  0x7f77b0a36e0a     sp: 0x7ffe35ff68e8      144  [??]  map.usr_lib64_ld_2.28.so.r_x+65034
[0x00400596]>
[0x00400596]>
[0x00400596]> dr
rax = 0x00000064
rbx = 0x00000000
rcx = 0x7f77b0a21738
rdx = 0x7ffe35ff6948
r8 = 0x7f77b0a22da0
r9 = 0x7f77b0a22da0
r10 = 0x0000000f
r11 = 0x00000002
r12 = 0x004004b0
r13 = 0x7ffe35ff6930
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x7ffe35ff6938
rdi = 0x00000064
rsp = 0x7ffe35ff6838
rbp = 0x7ffe35ff6850
rip = 0x00400596
rflags = 0x00000202
orax = 0xffffffffffffffff
[0x00400596]>

反编译器

能够理解汇编是二进制分析的前提。汇编语言总是与二进制建立和预期运行的架构相关。一行源代码和汇编代码之间从来没有 1:1 的映射。通常,一行 C 源代码会产生多行汇编代码。所以,逐行读取汇编代码并不是最佳的选择。

这就是反编译器的作用。它们试图根据汇编指令重建可能的源代码。这与用于创建二进制的源代码绝不完全相同,它是基于汇编的源代码的近似表示。另外,要考虑到编译器进行的优化,它会生成不同的汇编代码以加快速度,减小二进制的大小等,会使反编译器的工作更加困难。另外,恶意软件作者经常故意混淆代码,让恶意软件的分析人员望而却步。

Radare2 通过插件提供反编译器。你可以安装任何 Radare2 支持的反编译器。使用 r2pm -l 命令可以查看当前插件。使用 r2pm install 命令来安装一个示例的反编译器 r2dec

$ r2pm  -l
$
$ r2pm install r2dec
Cloning into 'r2dec'...
remote: Enumerating objects: 100, done.
remote: Counting objects: 100% (100/100), done.
remote: Compressing objects: 100% (97/97), done.
remote: Total 100 (delta 18), reused 27 (delta 1), pack-reused 0
Receiving objects: 100% (100/100), 1.01 MiB | 1.31 MiB/s, done.
Resolving deltas: 100% (18/18), done.
Install Done For r2dec
gmake: Entering directory '/root/.local/share/radare2/r2pm/git/r2dec/p'
[CC] duktape/duktape.o
[CC] duktape/duk_console.o
[CC] core_pdd.o
[CC] core_pdd.so
gmake: Leaving directory '/root/.local/share/radare2/r2pm/git/r2dec/p'
$
$ r2pm  -l
r2dec
$

反编译器视图

要反编译一个二进制文件,在 r2 中加载二进制文件并自动分析它。在本例中,使用 s sym.adder 命令移动到感兴趣的 adder 函数,然后使用 pdda 命令并排查看汇编和反编译后的源代码。阅读这个反编译后的源代码往往比逐行阅读汇编更容易:

$ r2 -A ./adder
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
 -- What do you want to debug today?
[0x004004b0]>
[0x004004b0]> s sym.adder
[0x00400596]>
[0x00400596]> s
0x400596
[0x00400596]>
[0x00400596]> pdda
    ; assembly                               | /* r2dec pseudo code output */
                                             | /* ./adder @ 0x400596 */
                                             | #include &lt;stdint.h>
                                             |  
    ; (fcn) sym.adder ()                     | int32_t adder (int64_t arg1) {
                                             |     int64_t var_4h;
                                             |     rdi = arg1;
    0x00400596 push rbp                      |    
    0x00400597 mov rbp, rsp                  |    
    0x0040059a mov dword [rbp - 4], edi      |     *((rbp - 4)) = edi;
    0x0040059d mov eax, dword [rbp - 4]      |     eax = *((rbp - 4));
    0x004005a0 add eax, 1                    |     eax++;
    0x004005a3 pop rbp                       |    
    0x004005a4 ret                           |     return eax;
                                             | }
[0x00400596]>

配置设置

随着你对 Radare2 的使用越来越熟悉,你会想改变它的配置,以适应你的工作方式。你可以使用 e 命令查看 r2 的默认配置。要设置一个特定的配置,在 e 命令后面添加 config = value

[0x004005a5]> e | wc -l
593
[0x004005a5]> e | grep syntax
asm.syntax = intel
[0x004005a5]>
[0x004005a5]> e asm.syntax = att
[0x004005a5]>
[0x004005a5]> e | grep syntax
asm.syntax = att
[0x004005a5]>

要使配置更改永久化,请将它们放在 r2 启动时读取的名为 .radare2rc 的启动文件中。这个文件通常在你的主目录下,如果没有,你可以创建一个。一些示例配置选项包括:

$ cat ~/.radare2rc
e asm.syntax = att
e scr.utf8 = true
eco solarized
e cmd.stack = true
e stack.size = 256
$

探索更多

你已经看到了足够多的 Radare2 功能,对这个工具有了一定的了解。因为 Radare2 遵循 Unix 哲学,即使你可以从它的主控台做各种事情,它也会在下面使用一套独立的二进制来完成它的任务。

探索下面列出的独立二进制文件,看看它们是如何工作的。例如,用 iI 命令在控制台看到的二进制信息也可以用 rabin2 <binary> 命令找到:

$ cd bin/
$
$ ls
prefix  r2agent    r2pm  rabin2   radiff2  ragg2    rarun2   rasm2
r2      r2-indent  r2r   radare2  rafind2  rahash2  rasign2  rax2
$

4、Binutils 工具

GNU Binutils:http://www.gnu.org/software/binutils/

对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高。目前,可以说嵌入式系统的开发工具是GNU的天下,因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC,还有一个非常重要的、同样来自于 GNU的工具集(toolchain) —— binutils toolchain。这一工具集中存在的一些工具,可以说是我们开发和调试不可缺少的利器。 Binutils中的工具不少和GCC相类似,也是针对特定的处理器的。

在binutils中以下的工具是我们在做嵌入式系统开发时需要掌握的:

addr2line

将程序地址翻译成文件名和行号;给定地址和可执行文件名称,它使用其中的调试信息判断与此地址有关联的源文件和行号。
(将地址对应到文件名和行号)

ar

创建、修改和提取归档

as

一个汇编器,as工具主要用来将汇编语言编写的源程序转换成二进制形式的目标代码。Linux平台的标准汇编器是GAS,它是Gnu GCC编译器所依赖的后台汇编工具,通常包含在Binutils软件包中。

c++filt

被链接器用于修复 C++ 和 Java 符号,防止重载的函数相互冲突

elfedit

更新 ELF 文件的 ELF 头

gprof

显示分析数据的调用图表。gprof是Linux下一个强有力的程序分析工具。能够以“日志”的形式记录程序运行时的统计信息:程序运行中各个函数消耗的时间和函数调用关 系,以及每个函数被调用的次数等等。从而可以帮助程序员找出众多函数中耗时最多的函数,也可以帮助程序员分析程序的运行流程。相信这些功能对于分析开源代 码的程序员来说,有着相当大的诱惑力;同时我们也可以借助gprof进行性能优化和分析。

ld

一个链接器,将几个对象和归档文件组合成一个文件,重新定位它们的数据并且捆绑符号索引。同as一样,ld也是GNU Binutils工具集中重要的工具,Linux使用ld作为标准的链接程序,由汇编器产生的目标代码是不能直接在计算机上运行的,它必须经过链接器的处 理才能生成可执行代码,链接是创建一个可执行程序的最后一个步骤,ld可以将多个目标文件链接成为可执行程序,同时指定了程序在运行时是如何执行的。

ld.bfd

到 ld 的硬链接

nm

列出给定对象文件中出现的符号(列出目标文件(.o)的符号清单)

objcopy

将一种对象文件翻译成另一种,.bin 转换成 .elf 、.elf 转换成 .bin等。

objdump

显示有关给定对象文件的内容信息,包含指定显示信息的选项;显示的信息对编译工具开发者很有用(能实现(ar,nm)的很多功能)
最主要的作用是反汇编

ranlib

创建一个归档的内容索引并存储在归档内;索引列出其成员中可重定位的对象文件定义的所有符号。ranlib 生成索引以加快对归档文件的访问,并将结果保存到这个归档文件中,在索引中列出了归档文件各个成员所定义的可重分配目标文件。ar -s可以实现类似的功能。

readelf

显示有关 ELF 二进制文件的信息,readelf -h *.exe进行查看。是ELF文件查看利器。

size

列出给定对象文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等。

strings

对每个给定的文件输出不短于指定长度 (默认为 4) 的所有可打印字符序列;对于对象文件默认只打印初始化和加载部分的字符串,否则扫描整个文件

strip

移除对象文件中的符号,进行文件压缩,进行瘦身。即删除目标文件中的全部或者特定符号,这样可以减小可执行文件的大小。在嵌入式应用中,可执行文件一般存放在flash中,空间有限,因此在产品 release的过程中采用strip对程序进行裁剪很有必要。

libiberty

包含多个 GNU 程序会使用的途径,包括 getoptobstackstrerrorstrtol 和 strtoul

libbfd

二进制文件描述器库

libopcodes

一个库,用于处理 opcodes——处理器指令的 “可读文本” 版本;用于编制 objdump 这样的工具

file 查看文件类型

file 会设法判断文件的结构是否符合某种已知的文件格式。多数情况下,它会搜索某些文件类型所特有的标签值(通常称为幻数)。file 能够识别大量的文件格式,包括数种 ASCII 文本文件、各种可执行文件和数据文件。file 执行的幻数检查是由幻数文件(magicfile)所包含的规则控制。幻数文件的默认位置因操作系统而异,常见的位置包括 /usr/share/file/magic、/usr/share/misc/magic和/etc/magic。欲了解更多有关幻数文件的信息,请参阅 file 的文档资料。

  • file:分析任何文件的基本格式。常用参数
    -b:列出辨识结果时,不显示文件名称;
    -c:详细显示指令执行过程,便于排错或分析程序执行的情形;常与 -m 一起使用,用来在安装幻数文件之前调试它
    -f <文件名>:指定一个或多个文件名,让file依序辨识这些文件,格式为每列一个文件名称;
    -L:直接显示符号连接所指向的文件类别;
    -m <魔法数字文件>:指定魔法数字文件;
    -v:显示版本信息;
    -z:尝试去解读压缩文件的内容。
    -i:显示MIME类别

file 及类似的工具同样也会出错。如果一个文件碰巧包含了某种文件格式的标记,file等工具很可能会错误地识别这个文件。你可以使用一个十六进制文件编辑器将任何文件的前4字节修改为Java的幻数序列CAFEBABE,自已证实一下上述情况。这时,file 会将这个新修改的文件错误地识别为已编译的Java类数据。同样,一个仅包含MZ这两个字符的文本文件会被误认为是一个MS-DOS可执行文件。在逆向工程过程中,绝不要完全相信任何工具所提供的结果,除非该结果得到其他几款工具和手动分析的确认。

string 查看 字符串

strings 可以打印二进制文件中的字符串信息。参数 -a 表示搜索整个文件,参数 -t 可以显示出每一个字符串的偏移,参数-e 可以用于搜索更多的字符编码的字符串,如Unicode编码。结合grep进行搜索,用grep命令其实可以直接在二进制文件中搜索内容,但是不够直观,用strings看起来的更直观些。strings 会把任何可打印字符串都显示出来,比nm的内容更多

-a --all:扫描整个文件而不是只扫描目标文件初始化和装载段
-f --print-file-name:在显示字符串前先显示文件名
-n --bytes=[number]:找到并且输出所有NUL终止符序列
- :设置显示的最少的字符数,默认是4个字符
-t --radix={o,d,x} :输出字符的位置,基于八进制,十进制或者十六进制
-o :类似--radix=o
-T --target= :指定二进制文件格式
-e --encoding={s,S,b,l,B,L} :选择字符大小和排列顺序:s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit
@ :读取中选项

查找ls中包含libc的字符串,不区分大小写:strings /bin/ls | grep -i libc

addr2line 将地址映射到代码行

addr2line是用来将程序地址转换成其所对应的程序源文件及所对应的代码行,当然,也可以得到所对应的函数。为了说明addr2line是如何使用的,我们需要有一个练习用的程序。先采用编辑工具编辑一个test.c源文件,其内容如图所示。

运行如下的命令将test.c编译成可执行文件,注意:必须加 -g 这个选项 并运行之。在运行test程序后,我们可以在其终端上看到它打印出的fun()函数的地址 —— 0x80483e4。

现在,我们可以用这一地址来看一看addr2line是如何使用的。在终端中运行如下的命令,从命令的运行结果来看,addr2line工具正确的指出了地址0x80483e4 所对于应的程序的具体位置是在哪以及所对应的函数名是什么。

可能有人会问了:这个0x80483e4地址是我们打印出来,即然有打印,我们一般情况下也会打印出其具体的函数位置,而不是只打印地址,我为何要这么绕一下通过addr2line去找到地址所对应的函数呢?其实,这里打印出地址只是为了得到一个地址以便用于练习。在现实中,地址往往是在调试过程中或是当程序崩溃时通过某种方式获得的。此外,采用nm工具(后面会讲到)可以得到如下的函数地址信息。

as 汇编器

as 是将汇编代码汇编成目标文件

size 查看段大小

size 是列出程序文件中各段的大小。在后面的章节中,我们会使用objdump查看段信息,除了这三个段还有.rdata和.idata两个段,其中.rdata段被归类到.text段中,而.idata段被归类到.data段中。下面是采用size工具所显示出的test中的段大小信息。

nm 查看符号

nm命令是Linux下自带的强大的文本分析工具,是命令来源于 name 的简写。该命令用来列出指定文件中的符号(如常用的函数名、变量等,以及这些符号存储的区域)。它显示指定文件中的符号信息,文件可以是对象文件、可执行文件或对象文件库。如果文件中没有包含符号信息,nm报告该情况,单不把他解释为出错。nm缺省情况下报告十进制符号表示法下的数字值。

nm 列举目标文件中的符号。可查看文件中的符号,包括全局变量,全局函数等。对于一些动态库,直接nm可能查不到信息,可以通过nm -D命令查看。

参数说明:

-A / -o / --print-file-name: 在输出时加上文件名;
-a / --debug-syms: 输出所有符号,包含debugger-only symbols;
-B / --format=bsd: BSD码显示,兼容MIPS nm;
-C / --demangle: 将低级符号名解析为用户级名字,可以使得C++函数名更具可读性;
-D / --dynamic: 显示动态符号。该选项只对动态目标(如特定类型的共享库)有意义;
-f format / --format=format 使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd
-g / --extern-only: 只显示外部符号;
-l / --line-numbers: 对于每个符号,使用debug信息找到文件名和行号;
-n / -v / --numeric-sort: 按符号对应地址的顺序排序,而非按符号名字字符顺序排序;
-P /--portability: 按照POSIX2.0标准格式输出,等同于使用 -f posix;
-p / --no-sort: 按照目标文件中遇到的符号顺序显示,不排序;
-r / --reverse-sort: 反转排序;
-s / --print-armap: 当列出库成员符号时,包含索引。索引的内容:模块和其包含名字的映射;
-u / --undefined-only: 只显示未定义符号;
--defined-only: 只显示定义了的符号。

常用选项:
-A    每个符号前显示文件名
-D    显示动态符号
-g    仅显示外部符号
-r    反序显示符号表

nm用于列出程序文件中的符号,符号是指函数或是变量名什么的。下面来看一看图 2所编译出来的程序当中有些什么符号

      nm所列出的每一行有三部分组成:

第一列是指程序运行时的符号所对应的地址,对于函数则地址表示的是函数的开始地址,对于变量则表示的是变量的存储地址

第二列是指相应符号是放在哪一个段的;

第三列则是指符号的名称。

在前面我们讲解addr2line时,我们提到addr2line是将程序地址转换成这一地址所对应的具体函数是什么,而nm则是全面的列出这些信息。但是,nm不具备列出符号所在的源文件及其行号这一功能,因此,我们说每一个工具有其特定的功能,在嵌入式系统的开发过程中我们需要灵活的运用它们。  

       对于nm列出的第二列信息,非常的有用,其意义在于可以了解我们在程序中所定义的一个符号(比如变量等等)是被放在程序的哪一个段的,下表列出了第二列将会出现的部分字母的含义,要参看所有字母的意思,请在你的开发环境中运行“man nm”。

ldd 查看链接的库

查看文件执行所需要的 共享库/动态库。

-v    详细信息模式,打印所有相关信息
-u    打印未使用的直接依赖
-d    执行重定位和报告任何丢失的对象
-r    执行数据对象和函数的重定位,并且报告任何丢失的对象和函数
--help    显示帮助信息

示例:ldd /bin/vim

strip 去除elf中的符号

strip 用于去除程序文件中的调试信息以便减小文件的大小。 去除符号后仍然保持正常功能。但增加了逆向的难度,出题恶人必备。其与 objcopy 带 --strip-debug 参数时的功能是一样的。strip所具有的功能,objcopy也都有。

可以看到test小了几KB,strip在大文件中有更好的体现。

objcopy 

objcopy 用来修改可执行文件、目标文件或其他二进制文件的 格式、内容或属性。

  1. 文件格式转换:将文件从一种格式转换为另一种格式。示例:objcopy -O binary  xx  xx.bin
  2. 去除调试信息:从可执行文件或目标文件中移除调试符号。
  3. 提取部分数据:从文件中提取某段特定的数据或段。
  4. 修改符号表:重命名、删除或添加符号。
  5. 创建固件镜像:用于嵌入式开发,生成特定的二进制文件或镜像。

objdump

objdump 是 GNU binutils 工具套件的一部分。objdump 依靠二进制文件描述符库libbfd(二进制工具的一个组件)来访问目标文件,因此,它能够解析libbfd支持的文件格式(ELF、PE等)。另外 readelf 工具也可用于解析ELF文件。readelf 的大多数功能与objdump相同,它们之间的主要区别在于 readelf 并不依赖 libbfd。readelf 可查看文件的所有详细信息,包括文件的头信息,动态库信息,段信息等

objdump 将二进制代码转汇编指令。objdump是个值得深入学习的指令,不光可以还原汇编指令,还可以读取二进制中特定段的信息,更可怕的是,如果我们的程序是以-g -o0等调试不优化的情况下,用objdump -S指令可能尽可能地还原源代码信息(没看错,是还原出源代码信息),其实也可以理解这些信息是完整的在可执行文件中的,要不然gdb调试的时候没办法单步追踪了,

objdump可以用来查看目标程序中的段信息和调试信息,也可以用来对目标程序进行反汇编。我们知道程序是由多个段组成的,比如.text是用来放代码的、.data是用来放初始化好的数据的、.bss是用来放未初始化好的数据的,等等。在嵌入式系统的开发过程中,我们有时需要知道所生成的程序中的段信息来分析问题。比如,我们需要知道其中的某个段在程序运行时,共起始地址是什么,或者,我们需要知道正在运行的程序中是否存在调试信息等等。 

-a, --archive-headers
    显示文件的头信息,展示档案每一个成员的文件格式。效果等同于命令 ar -tv
-b, --target=BFDNAME
    指定目标码格式。非必须。objdump 能自动识别许多格式
-d, --disassemble  反汇编目标文件,将机器指令反汇编成汇编代码
-D, --disassemble-all
    与 -d 类似,但反汇编所有段(section)
-z, --disassemble-zeroes
    一般反汇编输出将省略零块,该选项使得这些零块也被反汇编 
-EB, -EL,--endian={big | little}
    指定目标文件的字节序,在目标文件没描述字节序时很有用,例如 S-records。这个选项只影响反汇编
-f, --file-headers
    显示每一个目标文件的头信息
-F, --file-offsets
    反汇编时,打印每一个符号的偏移地址
--file-start-context
    显示源码/汇编代码(假设为 -S)时,将上下文扩展到文件的开头
-g, --debugging
    显示调试信息。企图解析保存在文件中的调试信息并以 C 语言的语法显示出来。
	仅仅支持某些类型的调试信息。有些其他的格式被 readelf -w支持
-e, --debugging-tags
    类似 -g 选项,但是生成的信息是和ctags工具相兼容的格式
-h, --section-headers, --headers
    显示目标文件各个 section 的头部摘要信息
-i, --info
    显示对于 -b 或者 -m 选项可用的架构和目标格式列表
-j, --section=NAME
    仅显示指定名称的 section 的信息 
-l, --line-numbers
    用文件名和行号标注相应的目标代码,仅仅和 -d、-D 或者 -r 一起使用
-S,--source
    反汇编时尽可能使用源代码表示。隐含了-d参数
-m, --architecture=MACHINE
    指定反汇编目标文件时使用的架构,当待反汇编文件本身没描述架构信息的时候(比如S-records)这个选项很有用。
	可以用-i选项列出这里能够指定的架构
-M, --disassembler-options=OPTIONS
    给反汇编程序传递参数,可以指定多个,使用逗号分隔
-p, --private-headers
    打印目标文件格式的特定信息。打印的信息取决于目标文件格式,对于某些目标文件格式,不打印任何附加信息。
-P, --private=OPTIONS
    打印目标文件格式的特定信息。OPTIONS 是一个逗号分隔的列表。例如对于XCOFF,可用的
	选项有 header, aout, sections, syms, relocs, lineno, loader, except, typchk, traceback and toc
-r, --reloc
    显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来
-R, --dynamic-reloc
    显示文件的动态重定位入口,仅仅对于动态目标文件意义,比如某些共享库
-s, --full-contents
    显示section的完整内容。默认所有的非空section都会被显示
-W[lLiaprmfFsoRt],--dwarf=
    显示文件中调试段的内容,如果存在的话
-G, --stabs
    显示请求的任何 section 的全部内容。显示段 .stab、.stab.index 和 .stab.excl 的内容
-t, --syms
    显示文件的符号表入口。类似于nm -s提供的信息
-T, --dynamic-syms
    显示文件的动态符号表入口,仅仅对动态目标文件意义,
	比如某些共享库。它显示的信息类似于 nm -D,--dynamic 显示的信息
-x, --all-headers
    显示所可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -p -r -t 同时指定
-w, --wide
    为具有超过80列的输出设备格式化某些行。也不要在显示符号名称时截断符号名称
--start-address=ADDRESS
    从指定地址开始显示数据,该选项影响 -d、-r 和 -s 选项的输出
--stop-address=ADDRESS
    显示数据直到指定地址为止,该项影响-d、-r和-s选项的输出
--prefix-addresses
    反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式
--no-show-raw-insn
    反汇编时,不显示汇编指令的机器码。当使用--prefix-addresses时,这是缺省选项
--adjust-vma=OFFSET
    当解析信息时,首先给所有的段添加偏移值offset。当段地址与符号表不符时,这个选项很有用。
	比如将段放置到特殊地址,因为某个格式无法表示段地址,比如 a.out
--special-syms
    显示特殊符号与用户不关心的符号
--prefix=PREFIX
    当使用 -S 时,指定前缀添加到绝对路径中
--prefix-strip=LEVEL
    指定剥离绝对路径中多少个前缀目录名。此选项只有在使用了选项 --prefix=PREFIX 才有效
--insn-width=WIDTH
    指定反汇编后的指令输出的行宽,单位字节
-V, --version
    版本信息
-H, --help
    帮助信息

objdump命令的使用

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍:

objdump -f test        显示test的文件头信息
objdump -d test        反汇编test中的需要执行指令的那些section
objdump -D test        与-d类似,但反汇编test中的所有section
objdump -h test        显示test的Section Header信息
objdump -x test        显示test的全部Header信息
objdump -s test        除了显示test的全部Header信息,还显示他们对应的十六进制文件代码

举例:将C源代码和反汇编出来的指令对照:
1.编译成目标文件(要加-g选项):gcc -g -o test.c
2.输出C源代码和反汇编出来的指令对照的格式:objdump -S test.o

1)-- 下面是使用objdump的--h选项来查看程序中的段信息,练习用的程序如前面的图,这里假设你已将其编译成了可执行文件test

2)-- 反汇编

如何对任意一个二进制文件进行反汇编?

我们可以这样做:objdump -D -b binary -m i386 a.bin
-D表示对全部文件进行反汇编,-b表示二进制,-m表示指令集架构,a.bin就是我们要反汇编的二进制文件
objdump -m可以查看更多支持的指令集架构,如i386:x86-64,i8086等
另外上面的所有objdump命令的参数同样适用于arm-linux-objdump。
同时我们也可以指定big-endian或little-endian(-EB或-EL),我们可以指定从某一个位置开始反汇编等。所以objdump命令是非常强大的!

readelf  读取 elf 文件

objdump 和 readelf 都可以用来查看二进制文件的一些内部信息。objdump 是借助 BFD(LIB BFD, the Binary File Descriptor Library:http://www.mssl.ucl.ac.uk/swift/om/sw/help/man/bfd.html)而更加通用一些,可以应付不同文件格式。readelf 则并不需要 BFD 而是直接读取 ELF 格式文件的信息,按 readelf 手册页上所说,得到的信息也略细致一些。

 readelf、objdump、hexdump: http://blog.csdn.net/u011118014/article/details/20869441

如果二进制文件是ELF格式的,通过file文件可以查看文件格式.使用readelf指令可以方便分析ELF文件的结构,比如节信息,elf头文件信息,比如我们在分析文件是否为病毒文件的时候,需要读取elf文件头信息,做一些特征的判断,或作为特征参与机器学习的判断。

示例:readelf -h xxx

readelf 可以显示 elf 格式的可执行的文件信息。ELF格式是UNIX系统实验室作为应用程序二进制接口开发的。ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一。

常用的有以下几个功能选项:

1)-h或者--file-header   显示在ELF文件头里包含的所有信息
2)-l或者--program-headers或者--segments
     显示程序头表信息,包扩有几个段,每个段的属性,以及每个段中包含有哪几个节(Section)
3)-S或者--section-headers或者--sections    显示节区表内的所有信息,包括每个节的属性,注意这里是用的是大写的“S”
4)-t或者--section-details    用来显示所有节的详细信息,感觉上但从信息量上来说,和前面的“-S”没有什么大的不同
5)-e或者--headers    显示所有头的信息,包括ELF文件头、程序头和节头,也就是“-h -l -S”的组合。
6)-s或者--syms或者--symbols    显示符号表的信息,包含静态符号表(.symtab)和动态符号表(.dynsym)
7)-r或者--relocs     显示所有重定位入口的信息
8)-d或者--dynamic    显示动态节区的内容
9)-x或者--hex-dump=<number|name>    显示某个节区的二进制码,具体哪个节可以用该节的编号或者名字来指定,例如“-x .text”

C++filt

C++filt:可以用于显示出 c++ 中复杂的重载后的函数名称。

通常,一个目标文件中不能有两个名称相同的函数。为支持重载,编译器将描述函数参数类型的信息合并到函数的原始名称中,从而为重载函数生成唯一的函数名称。为名称完全相同的函数生成唯一名称的过程叫做 "名称改编(name mangling)、名称改写"。

PE Tools、PE Sniffer

PE Tools:https://github.com/petoolse/petools

PE tools 是一组用于分析 Windows 系统中正在运行的进程和可执行文件的工具。在进程列表中,用户可以将一个进程的内存映像转储到某个文件中,也可以使用 PESniffer 实用工具确定可热行文件由何种编译器构建,或者该文件是否经过某种已知的模糊实用工具的模糊处理。Tools 菜单提供了分析磁盘文件的类似选项。另外,用户还可以使用内嵌的 PEEditor 实用工具查看 PE 文件头字段,使用该工具还可以方便地修改任何文件头的值。通常,如果想要从文件的模糊版本重建一个有效的 PE,就需要修改PE文件头。

PEiD、 Exeinfo 检测 PE文件

PEiD 是另一款 Windows 工具,它主要用于识别构建某一特定 WindowsPE二进制文件所使
用的编译器,并确定 模糊WindowsPE所使用的二进制文件的工具。PEiD 的许多其他功能与PETools 的功能相同,包括显示PE文件头信息摘要、收集有关正在运行的进程的信息、执行基本的反汇编等。

file、PE Tools、PE Sniffer、PEiD 一般用来识别文件格式,由于要对二进制进行逆向,所以需要使用更高级的工具来理解某一特定的文件格式,来提取详细的信息,并且解析输入文件,提取输入文件包含的详细信息。

PEiD只支持检测32位的可执行文件,如果显示不是有效的PE文件,说明你的可执行文件为64位,不能用 PEiD 检测。可以使用 Exeinfo PE 查看,Exeinfo 相当于 PEiD 的升级版,可查看64位的可执行文件

dumpbin (类似 objdump )

微软 Visual Studio 套件中的一个工具。功能与 objdump 一样。可以显示大量与 Windows PE 相关的内容。

ltrace  跟踪函数调用

跟踪进程调用库函数过程  ltrace ./a.out
这在查看系统调用耗时很有用。
# -T 是查看调用时间开销
ltrace -T
#-t -tt -ttt 是查看调用绝对时间,t越多越精确
ltrace -t
ltrace 查看系统调用信息  ltrace -S

strace 跟踪函数调用

strace 和 ltrace 的命令差不多,strace 更偏向于系统调用的追踪或信号产生的情况。安装命令 yum -y install strace

强大地方在于可以指定系统调用的类型:

-e trace=set 
只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all. 
-e trace=file 
只跟踪有关文件操作的系统调用. 
-e trace=process 
只跟踪有关进程控制的系统调用. 
-e trace=network 
跟踪与网络有关的所有系统调用. 
-e strace=signal 
跟踪所有与系统信号有关的 系统调用 
-e trace=ipc 
跟踪所有与进程通讯有关的系统调用 
-e abbrev=set 
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all. 
-e raw=set 
将指 定的系统调用的参数以十六进制显示. 
-e signal=set 
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号. 
-e read=set 
输出从指定文件中读出 的数据.例如: 
-e read=3,5 
-e write=set 

xxd 查看文件的十六进制内容

xxd 是 Linux 系统中一个非常有用的工具,用于查看文件的十六进制内容,或者将十六进制数据转换回原始格式。它常用于调试、文件分析和二进制数据操作。

dd  复制块到文件

dd 命令用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。

otool

MAC 下类似 objdump 的工具

;