radare2 入门与反汇编
0. 简介
如果现有的 Linux 原生工具也能做类似的事情,你自然会问为什么需要另一个工具。嗯,这和你用手机做闹钟、做笔记、做相机、听音乐、上网、偶尔打电话和接电话的原因是一样的。以前,使用单独的设备和工具处理这些功能 —— 比如拍照的实体相机,记笔记的小记事本,起床的床头闹钟等等。对用户来说,有一个设备来做多件(但相关的)事情是_方便的_。另外,杀手锏就是独立功能之间的_互操作性_。
同样,即使许多 Linux 工具都有特定的用途,但在一个工具中捆绑类似(和更好)的功能是非常有用的。这就是为什么我认为 Radare2 应该是你需要处理二进制文件时的首选工具。
根据其 GitHub 简介,Radare2(也称为 r2)是一个“类 Unix 系统上的逆向工程框架和命令行工具集”。它名字中的 “2” 是因为这个版本从头开始重写的,使其更加模块化。
1. 安装radare
这里建议读者从 Git 安装它,或者您可以从 Github 选择最后一个版本(每6周一次)。这里提供了发布版本。值得一提的是其需要20.04及以上的版本的,否则libc是过不了的
sudo apt install -y gcc make cmake curl git gcc-multilib
git clone https://github.com/radare/radare2.git
cd radare2
./sys/install.sh
在遇到构建问题时,请尝试以下操作:
sudo make purgerm -rf shlr/capstonegit clean -xdfgit reset --hard @~50sys/install.sh
2. radare2常用帮助信息指令
下面是radare帮助信息的摘录:
$ radare2 -h
用法:r2 [-ACdfLMnNqStuvwzX] [-P patch] [-p prj] [-a arch] [-b bits] [-i file][-s addr] [-B baddr] [-m maddr] [-c cmd] [-e k=v] file|pid|-|--|=-- 运行 radare2 而不打开任何文件- 同 'r2 malloc://512'= 从标准输入读取文件(使用 -i 和 -c 来运行命令)-= 执行 !=! 命令以远程运行所有命令-0 在初始化和每个命令后打印 \x00-2 关闭 stderr 文件描述符(静默警告信息)-a [arch] 设置 asm.arch-A 运行 'aaa' 命令以分析所有引用的代码-b [bits] 设置 asm.bits-B [baddr] 设置 PIE 二进制文件的基地址-c 'cmd..' 执行 radare 命令-C 文件是 host:port(-c+=http://%s/cmd/ 的别名)-d 调试可执行文件 'file' 或运行进程 'pid'-D [backend] 启用调试模式(e cfg.debug=true)-e k=v 评估配置变量-f 块大小 = 文件大小-F [binplug] 强制使用该 rbin 插件-h, -hh 显示帮助信息,-hh 为详细信息-H ([var]) 显示变量-i [file] 运行脚本文件-I [file] 在打开文件之前运行脚本文件-k [OS/kern] 设置 asm.os(linux, macos, w32, netbsd, ...)-l [lib] 加载插件文件-L 列出支持的 IO 插件-m [addr] 在给定地址映射文件(loadaddr)-M 不对符号名称进行去修饰-n, -nn 不加载 RBin 信息(-nn 仅加载二进制结构)-N 不加载用户设置和脚本-q 安静模式(无提示),并在 -i 后退出-Q 安静模式(无提示),并更快退出(quickLeak=true)-p [prj] 使用项目,如果没有参数则列出,如果没有文件则加载-P [file] 应用 rapatch 文件并退出-r [rarun2] 指定要加载的 rarun2 配置文件(与 -e dbg.profile=X 相同)-R [rr2rule] 指定自定义 rarun2 指令-s [addr] 初始寻址-S 以沙箱模式启动 r2-t 在线程中加载 rabin2 信息-u 设置 bin.filter=false 以获取原始符号/节/类名称-v, -V 显示 radare2 版本(-V 显示库版本)-w 以写入模式打开文件-x 在没有执行标志的情况下打开(asm.emu 将无法工作),请参见 io.exec-X 与 -e bin.usextr=false 相同(对 dyldcache 有用)-z, -zz 不加载字符串或即使在原始状态下也加载它们
3. radare2全功能二进制文件分析
一旦安装完成,运行 r2 -v
命令来查看 Radare2 是否被正确安装:
# 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
$
3.1 获取二进制测试样本
现在 r2
已经安装好了,你需要一个样本二进制程序来试用它。你可以使用任何系统二进制文件(ls
、bash
等),但为了使本教程的内容简单,请编译以下 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
3.2 加载二进制文件
要分析二进制文件,你必须在 Radare2 中加载它。通过提供文件名作为 r2
命令的一个命令行参数来加载它。你会进入一个独立的 Radare2 控制台,这与你的 shell 不同。要退出控制台,你可以输入 Quit
或 Exit
或按 Ctrl+D
:
$ r2 ./adder-- Learn pancake as if you were radare!
[0x004004b0]> quit
$
3.3 分析二进制
在你探索二进制之前,你必须让 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]>
3.4 获取一些关于二进制的基本信息
在开始分析一个二进制文件之前,你需要一些背景信息。在许多情况下,这可以是二进制文件的格式(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]>
3.5 导入和导出
通常情况下,当你知道你要处理的是什么样的文件后,你就想知道二进制程序使用了什么样的标准库函数,或者了解程序的潜在功能。在本教程中的示例 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]>
3.6 哈希信息
如何知道两个二进制文件是否相似?你不能只是打开一个二进制文件并查看里面的源代码。在大多数情况下,二进制文件的哈希值(md5sum、sha1、sha256)是用来唯一识别它的。你可以使用 it
命令找到二进制的哈希值:
[0x004004b0]> it
md5 7e6732f2b11dec4a0c7612852cede670
sha1 d5fa848c4b53021f6570dd9b18d115595a2290ae
sha256 13dd5a492219dac1443a816ef5f91db8d149e8edbf26f24539c220861769e1c2
[0x004004b0]>
3.7 函数
代码按函数分组;要列出二进制中存在的函数,请运行 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]>
3.8 交叉引用
在 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]>
3.9 寻找定位
在处理文本文件时,你经常通过引用行号和行或列号在文件内移动;在二进制文件中,你需要使用地址。这些是以 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]>
3.10 十六进制视图
通常情况下,原始二进制没有意义。在十六进制模式下查看二进制及其等效的 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 .........@.....A
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]>