PWNTools的各种函数的使用[官网翻译]

ROP函数的使用

pwnlib.rop.rop - 面向返回的编程

ROP工具可以用来构建非常简单的堆栈。让我们创建一个假二进制,其中包含一些可能有用的符号。

1
2
3
>>> context.clear(arch='i386')
>>> binary = ELF.from_assembly('add esp, 0x10; ret')
>>> binary.symbols = {'read': 0xdeadbeef, 'write': 0xdecafbad, 'exit': 0xfeedface}

创建一个查找二进制符号的ROP对象非常简单

1
>>> rop = ROP(binary)

使用ROP对象,可以手动添加堆栈帧。

1
2
3
>>> rop.raw(0)
>>> rop.raw(unpack('abcd'))
>>> rop.raw(2)

检查ROP堆栈很容易,并且以易于阅读的方式显示

1
2
3
4
>>> print rop.dump()
0x0000: 0x0
0x0004: 0x64636261
0x0008: 0x2

ROP模块还知道如何使用标准LinuxABI进行函数调用。

1
2
3
4
5
6
7
8
9
10
>>> rop.call('read', [4,5,6])
>>> print rop.dump()
0x0000: 0x0
0x0004: 0x64636261
0x0008: 0x2
0x000c: 0xdeadbeef read(4, 5, 6)
0x0010: 'eaaa' <return address>
0x0014: 0x4 arg0
0x0018: 0x5 arg1
0x001c: 0x6 arg2

你可以用略写的方式去快速调用,这个栈会自动调整到下一个栈帧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> rop.write(7,8,9)
>>> rop.exit()
>>> print rop.dump()
0x0000: 0x0
0x0004: 0x64636261
0x0008: 0x2
0x000c: 0xdeadbeef read(4, 5, 6)
0x0010: 0x10000000 <adjust @0x24> add esp, 0x10; ret
0x0014: 0x4 arg0
0x0018: 0x5 arg1
0x001c: 0x6 arg2
0x0020: 'iaaa' <pad>
0x0024: 0xdecafbad write(7, 8, 9)
0x0028: 0x10000000 <adjust @0x3c> add esp, 0x10; ret
0x002c: 0x7 arg0
0x0030: 0x8 arg1
0x0034: 0x9 arg2
0x0038: 'oaaa' <pad>
0x003c: 0xfeedface exit()

ROP案例

让我们假设我们有一个简单的二进制文件,它只是将一些数据读入堆栈并返回。

1
2
3
4
>>> context.clear(arch='i386')
>>> c = constants
>>> assembly = 'read:'+ shellcraft.read(c.STDIN_FILENO, 'esp', 1024)
>>> assembly += 'ret\n'

让我们提供一些简单的gadgets:

1
>>> assembly += 'add_esp: add esp, 0x10; ret\n'

也许还有一个不错的“write”函数。

1
2
3
4
5
6
7
8
>>> assembly += 'write: enter 0,0\n'
>>> assembly += ' mov ebx, [ebp+4+4]\n'
>>> assembly += ' mov ecx, [ebp+4+8]\n'
>>> assembly += ' mov edx, [ebp+4+12]\n'
>>> assembly += shellcraft.write('ebx', 'ecx', 'edx')
>>> assembly += ' leave\n'
>>> assembly += ' ret\n'
>>> assembly += 'flag: .asciz "The flag"\n'

还有一种干净退出的方法。

1
2
>>> assembly += 'exit: ' + shellcraft.exit(0)
>>> binary = ELF.from_assembly(assembly)

最后,让我们构建ROP堆栈

1
2
3
4
5
6
7
8
9
10
11
>>> rop = ROP(binary)
>>> rop.write(c.STDOUT_FILENO, binary.symbols['flag'], 8)
>>> rop.exit()
>>> print rop.dump()
0x0000: 0x10000012 write(STDOUT_FILENO, 0x10000026, 8)
0x0004: 0x1000000e <adjust @0x18> add esp, 0x10; ret
0x0008: 0x1 arg0
0x000c: 0x10000026 flag
0x0010: 0x8 arg2
0x0014: 'faaa' <pad>
0x0018: 0x1000002f exit()

ROP堆栈中的原始数据可通过str。

1
2
3
>>> raw_rop = str(rop)
>>> print enhex(raw_rop)
120000100e000010010000002600001008000000666161612f000010

让我们试试看!

1
2
3
4
>>> p = process(binary.path)
>>> p.send(raw_rop)
>>> print p.recvall(timeout=5)
The flag

ROP 案例(amd64)

对于amd64二进制文件,寄存器从堆栈中加载。Pwntols可以对简单的“pop;pop;add;ret”风格的小工具进行基本推理,并满足需求,使一切“just work”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
>>> context.clear(arch='amd64')
>>> assembly = 'pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret; target: ret'
>>> binary = ELF.from_assembly(assembly)
>>> rop = ROP(binary)
>>> rop.target(1,2,3)
>>> print rop.dump()
0x0000: 0x10000000 pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret
0x0008: 0x3 [arg2] rdx = 3
0x0010: 0x1 [arg0] rdi = 1
0x0018: 0x2 [arg1] rsi = 2
0x0020: 'iaaajaaa' <pad 0x20>
0x0028: 'kaaalaaa' <pad 0x18>
0x0030: 'maaanaaa' <pad 0x10>
0x0038: 'oaaapaaa' <pad 0x8>
0x0040: 0x10000008 target
>>> rop.target(1)
>>> print rop.dump()
0x0000: 0x10000000 pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret
0x0008: 0x3 [arg2] rdx = 3
0x0010: 0x1 [arg0] rdi = 1
0x0018: 0x2 [arg1] rsi = 2
0x0020: 'iaaajaaa' <pad 0x20>
0x0028: 'kaaalaaa' <pad 0x18>
0x0030: 'maaanaaa' <pad 0x10>
0x0038: 'oaaapaaa' <pad 0x8>
0x0040: 0x10000008 target
0x0048: 0x10000001 pop rdi; pop rsi; add rsp, 0x20; ret
0x0050: 0x1 [arg0] rdi = 1
0x0058: 'waaaxaaa' <pad rsi>
0x0060: 'yaaazaab' <pad 0x20>
0x0068: 'baabcaab' <pad 0x18>
0x0070: 'daabeaab' <pad 0x10>
0x0078: 'faabgaab' <pad 0x8>
0x0080: 0x10000008 target

ROP+Sigreturn

在某些情况下,所需寄存器的控制不可用。但是,如果您可以控制堆栈EAX,并且可以找到int 0x80小工具,则可以使用sigreturn。
更妙的是,这是自动发生的。
我们的示例二进制文件会将一些数据读取到堆栈中,而不会执行任何其他有趣的操作。

1
2
3
4
5
6
7
8
>>> context.clear(arch='i386')
>>> c = constants
>>> assembly = 'read:' + shellcraft.read(c.STDIN_FILENO, 'esp', 1024)
>>> assembly += 'ret\n'
>>> assembly += 'pop eax; ret\n'
>>> assembly += 'int 0x80\n'
>>> assembly += 'binsh: .asciz "/bin/sh"'
>>> binary = ELF.from_assembly(assembly)

让我们创建一个ROP对象并调用该调用。

1
2
3
4
>>> context.kernel = 'amd64'
>>> rop = ROP(binary)
>>> binsh = binary.symbols['binsh']
>>> rop.execve(binsh, 0, 0)

这就是它的全部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> print rop.dump()
0x0000: 0x1000000e pop eax; ret
0x0004: 0x77 [arg0] eax = SYS_sigreturn
0x0008: 0x1000000b int 0x80
0x000c: 0x0 gs
0x0010: 0x0 fs
0x0014: 0x0 es
0x0018: 0x0 ds
0x001c: 0x0 edi
0x0020: 0x0 esi
0x0024: 0x0 ebp
0x0028: 0x0 esp
0x002c: 0x10000012 ebx = binsh
0x0030: 0x0 edx
0x0034: 0x0 ecx
0x0038: 0xb eax
0x003c: 0x0 trapno
0x0040: 0x0 err
0x0044: 0x1000000b int 0x80
0x0048: 0x23 cs
0x004c: 0x0 eflags
0x0050: 0x0 esp_at_signal
0x0054: 0x2b ss
0x0058: 0x0 fpstate

Let’s try it out!

1
2
3
4
5
6
>>> p = process(binary.path)
>>> p.send(str(rop))
>>> time.sleep(1)
>>> p.sendline('echo hello; exit')
>>> p.recvline()
'hello\n'

class pwnlib.rop.rop.ROP(_elfs,base = None,badchars_)[源代码]

一个简化生成ROP链的类

实例:

1
2
3
4
5
6
7
8
9
10
elf = ELF('ropasaurusrex')
rop = ROP(elf)
rop.read(0, elf.bss(0x80))
rop.dump()
# ['0x0000: 0x80482fc (read)',
# '0x0004: 0xdeadbeef',
# '0x0008: 0x0',
# '0x000c: 0x80496a8']
str(rop)
# '\xfc\x82\x04\x08\xef\xbe\xad\xde\x00\x00\x00\x00\xa8\x96\x04\x08'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
>>> context.clear(arch = "i386", kernel = 'amd64')
>>> assembly = 'int 0x80; ret; add esp, 0x10; ret; pop eax; ret'
>>> e = ELF.from_assembly(assembly)
>>> e.symbols['funcname'] = e.address + 0x1234
>>> r = ROP(e)
>>> r.funcname(1, 2)
>>> r.funcname(3)
>>> r.execve(4, 5, 6)
>>> print r.dump()
0x0000: 0x10001234 funcname(1, 2)
0x0004: 0x10000003 <adjust @0x18> add esp, 0x10; ret
0x0008: 0x1 arg0
0x000c: 0x2 arg1
0x0010: 'eaaa' <pad>
0x0014: 'faaa' <pad>
0x0018: 0x10001234 funcname(3)
0x001c: 0x10000007 <adjust @0x24> pop eax; ret
0x0020: 0x3 arg0
0x0024: 0x10000007 pop eax; ret
0x0028: 0x77 [arg0] eax = SYS_sigreturn
0x002c: 0x10000000 int 0x80
0x0030: 0x0 gs
0x0034: 0x0 fs
0x0038: 0x0 es
0x003c: 0x0 ds
0x0040: 0x0 edi
0x0044: 0x0 esi
0x0048: 0x0 ebp
0x004c: 0x0 esp
0x0050: 0x4 ebx
0x0054: 0x6 edx
0x0058: 0x5 ecx
0x005c: 0xb eax
0x0060: 0x0 trapno
0x0064: 0x0 err
0x0068: 0x10000000 int 0x80
0x006c: 0x23 cs
0x0070: 0x0 eflags
0x0074: 0x0 esp_at_signal
0x0078: 0x2b ss
0x007c: 0x0 fpstate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
>>> r = ROP(e, 0x8048000)
>>> r.funcname(1, 2)
>>> r.funcname(3)
>>> r.execve(4, 5, 6)
>>> print r.dump()
0x8048000: 0x10001234 funcname(1, 2)
0x8048004: 0x10000003 <adjust @0x8048018> add esp, 0x10; ret
0x8048008: 0x1 arg0
0x804800c: 0x2 arg1
0x8048010: 'eaaa' <pad>
0x8048014: 'faaa' <pad>
0x8048018: 0x10001234 funcname(3)
0x804801c: 0x10000007 <adjust @0x8048024> pop eax; ret
0x8048020: 0x3 arg0
0x8048024: 0x10000007 pop eax; ret
0x8048028: 0x77 [arg0] eax = SYS_sigreturn
0x804802c: 0x10000000 int 0x80
0x8048030: 0x0 gs
0x8048034: 0x0 fs
0x8048038: 0x0 es
0x804803c: 0x0 ds
0x8048040: 0x0 edi
0x8048044: 0x0 esi
0x8048048: 0x0 ebp
0x804804c: 0x8048080 esp
0x8048050: 0x4 ebx
0x8048054: 0x6 edx
0x8048058: 0x5 ecx
0x804805c: 0xb eax
0x8048060: 0x0 trapno
0x8048064: 0x0 err
0x8048068: 0x10000000 int 0x80
0x804806c: 0x23 cs
0x8048070: 0x0 eflags
0x8048074: 0x0 esp_at_signal
0x8048078: 0x2b ss
0x804807c: 0x0 fpstate
1
2
3
4
5
6
7
8
9
>>> elf = ELF.from_assembly('ret')
>>> r = ROP(elf)
>>> r.ret.address == 0x10000000
True
>>> r = ROP(elf, badchars='\x00')
>>> r.gadgets == {}
True
>>> r.ret is None
True

参数

  • elfs(*list*)–用于挖掘的ELF对象列表

  • base(*int*)–ROP链的第一个字节所在的堆栈地址(如果已知)。

  • badchars(*str*)–不应出现在ROP小工具地址中的字符。

build(base=None,description=None)[源代码]

将ROP链构造成可以传递给flat()的元素列表。

参数:

  • base(*int*)–用于构建rop链的基地址。默认为ROP的base参数。
  • description(*dict*)–可选的输出参数,它将获取堆栈上每个地址address:description映射,从base开始。

call(resolvable, arguments=(), abi=None,**kwargs)[源代码]

向ROP链添加一个调用。

参数:

  • resolved(*str,int*)–可以通过“resolve”查找的值,或者已经是整数。
  • arguments(*list*)–可以传递给pack()的参数列表。或者,如果设置了基地址,则可以提供字符串或整数的任意嵌套结构。

chain()[源代码]

构建ROP链

返回:

  • 原始ROP字节

describe(object)[源代码]

返回ROP堆栈中对象的描述

dump()[源代码]

以容易读的方式打印出ROP链

find_gadget(instructions)[源代码]

返回具有指令参数中指定的精确指令序列的小工具。

generatePadding(offset, count)源代码]

生成要插入ROP堆栈的填充。

1
2
3
4
5
6
7
>>> rop = ROP([])
>>> val = rop.generatePadding(5,15)
>>> cyclic_find(val[:4])
5
>>> len(val)
15
>>> rop.generatePadding(0,0)

migrate(next_base)[源代码]

使用leave显式设置$sp;ret小工具

raw(value)[源代码]

将原始整数或字符串添加到ROP链。
如果您的架构需要对齐的值,请确保任何给定的字符串都对齐!

参数:

  • value(int/str)要放入rop链的原始值
1
2
3
4
5
6
7
8
9
10
11
>>> rop = ROP([])
>>> rop.raw('AAAAAAAA')
>>> rop.raw('BBBBBBBB')
>>> rop.raw('CCCCCCCC')
>>> print rop.dump()
0x0000: 'AAAA' 'AAAAAAAA'
0x0004: 'AAAA'
0x0008: 'BBBB' 'BBBBBBBB'
0x000c: 'BBBB'
0x0010: 'CCCC' 'CCCCCCCC'
0x0014: 'CCCC'

resolve(resolvable)[源代码]

将符号解析为地址

参数:

  • resolvable(*str,int*)–转换为地址的东西

返回:

  • int 包含resolvable的地址或者none

search(move=0,regs=None,order='size')[源代码]

搜索符合指定条件的小工具。

参数:

  • move(*int*)–调整堆栈指针的最小字节数。
  • regs(*list*)–从堆栈中弹出的寄存器的最小列表。
  • order(*str*)–字符串“size”或“regs”。决定如何订购满足要求的多个小工具。

搜索将尽量减少弹出的字节数超过请求的字节数、除了请求的寄存器数和地址之外的寄存器数。
如果order==“size”,则小工具按(total_moves,total_regs,addr)进行字典式比较,否则按(total _regs,total_maves,addr)进行比较。

返回:

  • 小工具对象

search_iter(move=None, regs=None)[源代码]

遍历所有小工具,这些小工具将堆栈指针至少move字节,并允许您设置regs中的所有regs。

setRegisters(registers)[源代码]

返回将设置指定寄存器上下文的地址/值列表。

参数

  • registers (dict) – {register name: value}类型的字典

返回:

  • 元组列表,对堆栈进行排序。
    每个元组的形式都是(value,name),其中value是要在堆栈上显示的小工具地址或文本值,name是字符串名称或其他可以“未解析”的项。

unresolve(value)[源代码]

反转“resolve”。给定一个地址,它会尝试在加载的ELF文件中找到它的符号。如果没有找到,它将搜索所有已知的小工具,并返回反汇编

参数:

  • value (int) – 寻找的地址

返回:

  • 包含地址符号名称的字符串、小工具的反汇编(如果该地址有)或空字符串。