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

全功能的二进制文件分析工具 Radare2 指南  开源 开源工具 第1张

Radare2 是一个为二进制分析定制的开源工具。

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

为什么我需要另一个工具?

如果现有的 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 是否被正确安装:

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

获取二进制测试样本

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

  1. $ cat adder.c
  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
  1. $ gcc adder.c -o adder
  2. $ file adder
  3. 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
  4. $ ./adder
  5. Number now is : 101

加载二进制文件

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

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

分析二进制

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

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

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

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

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

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

  1. [0x004004b0]> iI
  2. arch x86
  3. baddr 0x400000
  4. binsz 14724
  5. bintype elf
  6. bits 64
  7. canary false
  8. class ELF64
  9. compiler GCC: (GNU) 8.3.1 20190507 (Red Hat 8.3.1-4)
  10. crypto false
  11. endian little
  12. havecode true
  13. intrp /lib64/ld-linux-x86-64.so.2
  14. laddr 0x0
  15. lang c
  16. linenum true
  17. lsyms true
  18. machine AMD x86-64 architecture
  19. maxopsz 16
  20. minopsz 1
  21. nx true
  22. os linux
  23. pcalign 0
  24. pic false
  25. relocs true
  26. relro partial
  27. rpath NONE
  28. sanitiz false
  29. static false
  30. stripped false
  31. subsys linux
  32. va true
  33. [0x004004b0]>
  34. [0x004004b0]>

导入和导出

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

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

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

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

哈希信息

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

  1. $ cat adder.c
0

函数

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

  1. $ cat adder.c
1

交叉引用

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

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

  1. $ cat adder.c
2

寻找定位

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

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

  1. $ cat adder.c
3

十六进制视图

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

  1. $ cat adder.c
4

反汇编

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

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

  1. $ cat adder.c
5

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

  1. $ cat adder.c
6

字符串

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

  1. $ cat adder.c
7

交叉引用字符串

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

  1. $ cat adder.c
8

可视模式

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

  1. $ cat adder.c
9

 全功能的二进制文件分析工具 Radare2 指南 开源 开源工具 第2张

(Gaurav Kamathe,CC BY-SA 4.0)

调试器

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

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
0

反编译器

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

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

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
1

反编译器视图

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
2

配置设置

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
3

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
4

探索更多

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

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

  1. #include <stdio.h>
  2. int adder(int num) {
  3. return num + 1;
  4. }
  5. int main() {
  6. int res, num1 = 100;
  7. res = adder(num1);
  8. printf("Number now is : %d\n", res);
  9. return 0;
  10. }
5

转载请说明出处
知优网 » 全功能的二进制文件分析工具 Radare2 指南

发表评论

您需要后才能发表评论