赛派号

茶吧机一般买多少钱比较实用 使用 Radare2 逆向二进制文件

一、Radare2 是什么?

官方说明:https://www.radare.org/n/radare2.html

Github:https://github.com/radareorg/radare2?tab=readme-ov-file

一个免费、自由的工具链,主要用于电子取证、软件逆向工程、利用、调试…可以使用几乎任何编程语言实现自动化。功能十分强大!

Radare 项目最初是一个专注于取证的简单命令行十六进制编辑器,现如今是一款功能强大的命令行工具,它支持你耳熟能详的所有平台 (Linux、Windows、macOS、Unix、Android、iOS等等…)

其可以编辑本地硬盘上的文件、查看内核内存以及在本地或通过远程 gdb、windbg 服务器调试程序。其广泛体系结构支持允许您分析、模拟、调试、修改和反汇编任何二进制文件!

二、什么是逆向?

通俗来讲,逆向也叫逆向工程,英文叫做 Reverse Engineering, 就是将机器才能够懂的机器代码(也就是0101二进制代码)翻译成人能够读懂的代码的一个过程(汇编代码)

2.1、逆向所需要的必备前置知识:

C 语言、汇编语言

逆向软件的时候往往面对的是汇编代码, 所以对于指令集要有个大致的认识, 另外对于一些比如函数入口、出口、函数调用约定(calling convention) 等也要有所了解

掌握汇编语言是学会逆向的充分必要条件,汇编语言基本上是最接近机器代码的编程语音了,基本上所有的逆向工具都是先将二进制文件反汇编成汇编代码,供给逆向工程师分析

在分析汇编语言时,会比较枯燥,我们往往不需要一行一行的仔细去分析,只需要关注一些关键的代码即可,理出代码片段的大致思路然后再着重分析关键代码即可;

实际情况往往是,我们只寻找关键的代码去翻译或仅了解其业务逻辑找到正确的点,毕竟要将所有的机器代码翻译出来,工作量是极其巨大的且繁琐的!

逆向时一般可以借助工具(如IDA Pro,Hopper Disassembler,ollydbg 等),IDA 可将汇编代码转换成C的伪代码,这样分析起来效率会更高。

当然了解了基本汇编知识后(目前x86就足矣), 就可以开始准备工具了!

2.2、为什么要逆向?

这个原因就比较多了,可能每个人的初衷都不一样:

1、兴趣 (最好的老师)

2、探寻软件运行原理,欲知其是如何运作的

3、恶意软件或病毒木马后门分析

4、破解、外挂、免杀等等

三、什么是二进制文件?

二进制文件是一种基于值的编码文件

不同的应用程序对二进制文件中的每个值会有不同的解读,常见的二进制文件有可执行程序、图形、图像、声音等等

3.1、二进制文件是把内存中的数据按其在内存中存储的形式原样输出到磁盘中存放,即存放的是数据的原形式

3.2、二进制文件一般由文件头和数据区组成,文件头描述了文件格式、大小等信息,数据区里存的就是数据

3.3、二进制文件与普通文本文件的区别,比如:

文本文件在Windows下可以直接用Notepad打开,是可以直接看得懂(如 :1.txt)

二进制文件用Notepad是打不开的,或者说能打开,但打开后大部分都是乱码。又或者说双击鼠标能打开运行的(如: qq.exe)

四、逆向工具准备

虽然很多逆向工具都支持多平台,但不同的环境用不同的工具,这样才比较顺手

4.0、常用的逆向分析工具如下:

IDA Pro:逆向工程师的灵魂,静态分析、功能强大 【倚天剑】 ollydbg: Windows下动态调试 【屠龙刀】 Hopper Disassembler:功能和IDA类似,不如IDA强大,但价格比IDA便宜太多,对objective-c支持更友好。【macOS】 apktool:Android APK反编译工具。 dex2jar:dex文件转jar工具。 JD-GUI:直接jar反编译成Ja代码浏览。 Android Studio:smali代码浏览,Android APK断点debug。 Charles/Flidder、Wireshark、BurpSuit:网络抓包工具。 UltraEdit: 查看、修改二进制文件等。

4.1、Linux环境工作, 常用到的工具如下:

radare2工具集 (本文主角) gdb (用于调试) peda (一个gdb的插件) IDA Pro

五、分析手段

常用的分析手段有以下三种:

静态分析 动态调试 网络流量分析 猜

六、准备目标

本着简单的目的,就准备一个简单的 C 语言程序练练手吧

代码如下所示:

1 #include 2 #include 3 4 int main(void) 5 { 6 const char pass[] = "P@ssw0rd"; 7 char buf[128] = "0"; 8 9 puts("enter password:"); 10 scanf("%s", buf); 11 12 if (strcmp(buf, pass) == 0){ 13 puts("Pwned!"); 14 }else{ 15 puts("Failed!"); 16 } 17 18 return 0; 19 } 20

程序功能很简单,运行后提示输入密码:如果正确的话会提示 “Pwned!”,反之“Failed”,然后退出。

6.1、先编译, 本菜用的WSL环境安装的是Kali, 编译需要用到的gcc及其它工具都已经自带了。

gcc -o pwn pwn.c # 默认不开户栈保护机制

编译、执行结果如下图所示:

6.2、祭出本文神器 Radare2, 拿它来开刀

1、 pwn 这是第一个目标二进制程序, 首先了解你的对手

# 查看文件详细属性及信息 rabin2 -I pwn

注意:rabin2是radare2套件中的一个工具, 主要用来提取二进制文件中的信息, 输出如下:

arch x86 baddr 0x0 binsz 14071 bintype elf bits 64 canary false class ELF64 compiler GCC: (Debian 12.2.0-14) 12.2.0 crypto false endian little hecode true intrp /lib64/ld-linux-x86-64.so.2 laddr 0x0 lang c linenum true lsyms true machine AMD x86-64 architecture nx true os linux pic true relocs true relro partial rpath NONE sanitize false static false stripped false subsys linux va true

如上所示,可以很清楚的看到文件的详细信息:

包括平台架构(x686)、文件大小(14071)、二进制文件格式(elf)、位数(64)、canary(栈保护)、编译器(GCC及版本号)、大小端(little)、编程语言(c)、操作系统(linux),等等。

2、查询目标是否存在有用的字符串或者明文密码

# 提取二进制字符串 rabin2 -z ./pwn

输出结果如下:

[Strings] nth paddr vaddr len size section type string ――――――――――――――――――――――――――――――――――――――――――――――――――――――― 0 0x00002004 0x00002004 15 16 .rodata ascii enter password: 1 0x00002017 0x00002017 6 7 .rodata ascii Pwned! 2 0x0000201e 0x0000201e 7 8 .rodata ascii Failed!

看来没有什么有用的信息,没有发现明文密码!(暂时先假装不知道)

3、初试牛刀,逆向分析

3.1、用Radare2打开目标

# 提示:r2 是Radare2 的一个链接,以下命令等效 radare2 -A ./pwn r2 -A ./pwn

进入后自动跳转到了函数入口 [0x00001070 ], 然后用pdf命令来查看汇编代码:

[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] Finding and parsing C++ vtables (rr) [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information (aanr) [x] Use -AA or aaaa to perform additional experimental analysis. [0x00001070]>pdf @sym.main

pdf表示p(打印)d(反汇编)f(函数), @表示取地址, sym.main为函数符号, 也可以用十六进制整数地址表示.在r2中查看这些指令的帮助只要在后面输入?即可, 比如p?查看打印类的命令, pd?查看打印反汇编类的命令

命令 pdf @sym.main 打印反汇编的输出如下所示:

┌ 273: int main (int argc, char **argv, char **envp); │ ; var char *s1 @ rbp-0x90 │ ; var int64_t var_88h @ rbp-0x88 │ ; var int64_t var_80h @ rbp-0x80 │ ; var int64_t var_78h @ rbp-0x78 │ ; var int64_t var_70h @ rbp-0x70 │ ; var int64_t var_68h @ rbp-0x68 │ ; var int64_t var_60h @ rbp-0x60 │ ; var int64_t var_58h @ rbp-0x58 │ ; var int64_t var_50h @ rbp-0x50 │ ; var int64_t var_48h @ rbp-0x48 │ ; var int64_t var_40h @ rbp-0x40 │ ; var int64_t var_38h @ rbp-0x38 │ ; var int64_t var_30h @ rbp-0x30 │ ; var int64_t var_28h @ rbp-0x28 │ ; var int64_t var_20h @ rbp-0x20 │ ; var int64_t var_18h @ rbp-0x18 │ ; var char *s2 @ rbp-0x9 │ ; var int64_t var_1h @ rbp-0x1 │ 0x00001159 55 push rbp │ 0x0000115a 4889e5 mov rbp, rsp │ 0x0000115d 4881ec900000. sub rsp, 0x90 │ 0x00001164 48b850407373. movabs rax, 0x6472307773734050 ; 'P@ssw0rd' │ 0x0000116e 488945f7 mov qword [s2], rax │ 0x00001172 c645ff00 mov byte [var_1h], 0 │ 0x00001176 48c78570ffff. mov qword [s1], 0x30 ; '0' │ 0x00001181 48c78578ffff. mov qword [var_88h], 0 │ 0x00001 48c745800000. mov qword [var_80h], 0 │ 0x00001194 48c745880000. mov qword [var_78h], 0 │ 0x0000119c 48c745900000. mov qword [var_70h], 0 │ 0x000011a4 48c745980000. mov qword [var_68h], 0 │ 0x000011ac 48c745a00000. mov qword [var_60h], 0 │ 0x000011b4 48c745a80000. mov qword [var_58h], 0 │ 0x000011bc 48c745b00000. mov qword [var_50h], 0 │ 0x000011c4 48c745b80000. mov qword [var_48h], 0 │ 0x000011cc 48c745c00000. mov qword [var_40h], 0 │ 0x000011d4 48c745c80000. mov qword [var_38h], 0 │ 0x000011dc 48c745d00000. mov qword [var_30h], 0 │ 0x000011e4 48c745d80000. mov qword [var_28h], 0 │ 0x000011ec 48c745e00000. mov qword [var_20h], 0 │ 0x000011f4 48c745e80000. mov qword [var_18h], 0 │ 0x000011fc 488d05010e00. lea rax, str.enter_password: ; 0x2004 ; "enter password:" │ 0x00001203 4889c7 mov rdi, rax ; const char *s │ 0x00001206 e825feffff call sym.imp.puts ; int puts(const char *s) │ 0x0000120b 488d8570ffff. lea rax, [s1] │ 0x00001212 4889c6 mov rsi, rax │ 0x00001215 488d05f80d00. lea rax, [0x00002014] ; "%s" │ 0x0000121c 4889c7 mov rdi, rax ; const char *format │ 0x0000121f b800000000 mov eax, 0 │ 0x00001224 e827feffff call sym.imp.__isoc99_scanf ; int scanf(const char *format) │ 0x00001229 488d55f7 lea rdx, [s2] │ 0x0000122d 488d8570ffff. lea rax, [s1] │ 0x00001234 4889d6 mov rsi, rdx ; const char *s2 │ 0x00001237 4889c7 mov rdi, rax ; const char *s1 │ 0x0000123a e801feffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) │ 0x0000123f 85c0 test eax, eax │ ┌─< 0x00001241 7511 jne 0x1254 │ │ 0x00001243 488d05cd0d00. lea rax, str.Pwned_ ; 0x2017 ; "Pwned!" │ │ 0x0000124a 4889c7 mov rdi, rax ; const char *s │ │ 0x0000124d e8defdffff call sym.imp.puts ; int puts(const char *s) │ ┌──< 0x00001252 eb0f jmp 0x1263 │ ││ ; CODE XREF from main @ 0x1241 │ │└─> 0x00001254 488d05c30d00. lea rax, str.Failed_ ; 0x201e ; "Failed!" │ │ 0x0000125b 4889c7 mov rdi, rax ; const char *s │ │ 0x0000125e e8cdfdffff call sym.imp.puts ; int puts(const char *s) │ │ ; CODE XREF from main @ 0x1252 │ └──> 0x00001263 b800000000 mov eax, 0 │ 0x00001268 c9 lee └ 0x00001269 c3 ret

如下图所示:

通过反汇编的代码可以看到,R2已经识别出了正确的明文密码了!但这不是本文想要的结果…

我们现在跳转到main并打印本地局部变量:

[0x00001070]> s main [0x00001159]> afv var char * s1 @ rbp-0x90 var int64_t var_88h @ rbp-0x88 var int64_t var_80h @ rbp-0x80 var int64_t var_78h @ rbp-0x78 var int64_t var_70h @ rbp-0x70 var int64_t var_68h @ rbp-0x68 var int64_t var_60h @ rbp-0x60 var int64_t var_58h @ rbp-0x58 var int64_t var_50h @ rbp-0x50 var int64_t var_48h @ rbp-0x48 var int64_t var_40h @ rbp-0x40 var int64_t var_38h @ rbp-0x38 var int64_t var_30h @ rbp-0x30 var int64_t var_28h @ rbp-0x28 var int64_t var_20h @ rbp-0x20 var int64_t var_18h @ rbp-0x18 var char * s2 @ rbp-0x9 var int64_t var_1h @ rbp-0x1 [0x00001159]>

s表示seek, 跳转后发现我们已经到了0x00001159

afv表示a(分析)f(函数)v(变量), 可以看到有很多的局部变量.先不用管。

小技巧:

0x1、先输入2个大写的字母V进入视图模式,再按p键可以切换不同的代码视图,可看到程序的流程控制图

0x2、输入1个小写的v,是命令行界面的gui视图,上面有菜单可以用鼠标操作

结合程序流程控制视图和反汇编视图可以清晰的看到:

如果汇编代码 jne 0x1254发生跳转的话,就表示失败了

# 内存地址 机器码 反汇编指令 0x00001241 7511 jne 0x1254

4、解决方案

汇编代码 jne 0x1254

表示:j (jump) n(not) e(equ) ,组合起来就是 “不相等就跳转”,行业大佬都称这个为关键跳,要逆向一款程序或软件其实关键就是找关键跳(对于爆破来说)

好,现在已经找到关键跳了,上面也提到了需要做的就是不能让它跳转。

【1】 注释掉那条关键跳的汇编指令 (把它 nop 掉)

【2】不让它跳转 (把汇编指令改成 je 0x1254, 让它相等就OK了)

那如何注释和修改汇编指令呢?

这时候要重新以 -w 参数的方式重新打开目标程序,

-w 参数表示以可写的方式打开程序, 而之前打开方式是只读的

# 或者在之前的会话中输入,也同样能获得修改文件的权限. eval cfg.write=true

以写方式打开目标后,先跳转到想要修改指令的地方:

│ ┌─< 0x00001241 7511 jne 0x1254 │ │ 0x00001243 488d05cd0d00. lea rax, str.Pwned_ ; 0x2017 ; "Pwned!" │ │ 0x0000124a 4889c7 mov rdi, rax ; const char *s │ │ 0x0000124d e8defdffff call sym.imp.puts ; int puts(const char *s) │ ┌──< 0x00001252 eb0f jmp 0x1263 │ ││ ; CODE XREF from main @ 0x1241 │ │└─> 0x00001254 488d05c30d00. lea rax, str.Failed_ ; 0x201e ; "Failed!" │ │ 0x0000125b 4889c7 mov rdi, rax ; const char *s │ │ 0x0000125e e8cdfdffff call sym.imp.puts ; int puts(const char *s) │ │ ; CODE XREF from main @ 0x1252 │ └──> 0x00001263 b800000000 mov eax, 0 │ 0x00001268 c9 lee └ 0x00001269 c3 ret [0x00001154]> s 0x00001241 [0x00001241]>

这里的机器码是0x7511, 反汇编为jne 0x13, 不懂怎么反?

这时候就要介绍radare2中的另一个工具rasm2了:

$ rasm2 -a x86 -b 64 -d "0x7511" jne 0x13 $ rasm2 -a x86 -b 64 "jne 0x13" 7511 # -a为CPU架构, -b为CPU寄存器位数, -d表示反汇编, 本机是64位的

接下来我们要把jne改为je

$ rasm2 -a x86 -b 64 "je 0x13"7411

也就是说把75改为74就行了! 只需要改一个字节, 怎么操作?

如下:

│ ┌─< 0x00001241 7511 jne 0x1254 │ │ 0x00001243 488d05cd0d00. lea rax, str.Pwned_ ; 0x2017 ; "Pwned!" │ │ 0x0000124a 4889c7 mov rdi, rax ; const char *s │ │ 0x0000124d e8defdffff call sym.imp.puts ; int puts(const char *s) │ ┌──< 0x00001252 eb0f jmp 0x1263 │ ││ ; CODE XREF from main @ 0x1241 │ │└─> 0x00001254 488d05c30d00. lea rax, str.Failed_ ; 0x201e ; "Failed!" │ │ 0x0000125b 4889c7 mov rdi, rax ; const char *s │ │ 0x0000125e e8cdfdffff call sym.imp.puts ; int puts(const char *s) │ │ ; CODE XREF from main @ 0x1252 │ └──> 0x00001263 b800000000 mov eax, 0 │ 0x00001268 c9 lee └ 0x00001269 c3 ret [0x00001159]> s 0x00001241 [0x00001241]> px 20 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00001241 7511 488d 05cd 0d00 0048 89c7 e8de fdff u.H......H...... 0x00001251 ffeb 0f48 ...H [0x00001241]> wx 74 [0x00001241]> px 20 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00001241 7411 488d 05cd 0d00 0048 89c7 e8de fdff t.H......H...... 0x00001251 ffeb 0f48 ...H [0x00001241]>

px表示以 hexdump 形式打印当前位置的N个字节, wx 表示在当前位置写入如果有疑问, 可输入命令加问号来查询用法:如 px? 和 wx? 都能查看对应的帮助.

再次输入 pdf @sym.main 查看已经看到机器码 7511已经改成了7411,反汇编指令也成功修改为 je 0x1254了!Bigo!

┌─< 0x00001241 7411 je 0x1254 │ │ 0x00001243 488d05cd0d00. lea rax, str.Pwned_ ; 0x2017 ; "Pwned!" │ │ 0x0000124a 4889c7 mov rdi, rax ; const char *s │ │ 0x0000124d e8defdffff call sym.imp.puts ; int puts(const char *s) │ ┌──< 0x00001252 eb0f jmp 0x1263 │ ││ ; CODE XREF from main @ 0x1241 │ │└─> 0x00001254 488d05c30d00. lea rax, str.Failed_ ; 0x201e ; "Failed!" │ │ 0x0000125b 4889c7 mov rdi, rax ; const char *s │ │ 0x0000125e e8cdfdffff call sym.imp.puts ; int puts(const char *s) │ │ ; CODE XREF from main @ 0x1252 │ └──> 0x00001263 b800000000 mov eax, 0 │ 0x00001268 c9 lee └ 0x00001269 c3 ret [0x00001241]>

现在改好了, 按q或Ctrl+d回车退出r2, 再运行下试试:

linux@Linux:~/c$ ./pwn enter password: 1234 Pwned! linux@Linux:~/c$ linux@Linux:~/c$ linux@Linux:~/c$ linux@Linux:~/c$ ./pwn enter password: hello Pwned! linux@Linux:~/c$ linux@Linux:~/c$ linux@Linux:~/c$ ./pwn enter password: okok Pwned! linux@Linux:~/c$

Nice, 酷~ 现在输入任意字符都成功输入Pwned!

注释的话只需要在同样的地址修改为 9090就好了:

[0x00001159]> s 0x00001241 [0x00001241]> wx 9090 [0x00001241]> px 20 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00001241 9090 488d 05cd 0d00 0048 89c7 e8de fdff ..H......H...... 0x00001251 ffeb 0f48 ...H

OK, 同样完成一样的效果~

当然了条条大路通罗马,还有其它方案可以解决,此程序有一个BOF漏洞(缓冲区溢出),需要自己编写及测试了。

本文简单的说明了使用 Radare2逆向二进制文件,文字及描述大多来自网上大佬,本菜自己整理消化理解并编辑,感谢前辈们的分享~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lsinopec@gmail.com举报,一经查实,本站将立刻删除。

上一篇 没有了

下一篇没有了