pwntools使用
pwntools使用
安装
$ apt-get update
$ apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential
$ pip install --upgrade pip
$ pip install --upgrade pwntools
如果想要在本地测试,可以这么做:
$ git clone https://github.com/Gallopsled/pwntools
$ pip install --upgrade --editable ./pwntools
使用时这样导入:
from pwn import *
连接
r = = process('/bin/sh') #本地文件
r = remote('ftp.ubuntu.org',21) #远程,hostname + port
r.send(data) #发送数据
r.sendline(data) #发送数据 + '\n'
r.recv(numb=4096,timeout=default) #接受指定字节数据,timeout指定超时
r.recvuntil(delims, drop=False) #接收到delims的pattern
r.recvline(keepends=True) #接收到'\n',keepends指定保留'\n'
r.recvall() #接收到EOF
r.recvrepeat(timeout=default) #接收到EOF或timeout
r.interactive() #与shell交互
r.close() #断开连接
数据
>>> import struct
>>> p32(0xdeadbeef) == struct.pack('I', 0xdeadbeef) #p32为打包,p64同理,小端序
True
>>> leet = '37130000'.decode('hex')
>>> u32('abcd') == struct.unpack('I', 'abcd')[0] #u32为解包,u64同理,小端序
True
>>> u8('A') == 0x41
True
设置目标架构和操作系统
全局设置context,可以包括字大小和端序。
>>> context.arch = 'i386'
>>> context.os = 'linux'
>>> context.endian = 'little'
>>> context.word_size = 32
or
>>> context(arch='i386', os='linux', endian='little', word_size=32)
还有一个实用的可以显示调试信息:
context.log_level = "debug"
ELF操作
>>> e = ELF('/bin/cat') #获取这个文件的句柄
>>> print hex(e.address) #基地址
0x400000
>>> print hex(e.symbols['write']) #函数地址
0x401680
>>> print hex(e.got['write']) #GOT表的地址
0x60b070
>>> print hex(e.plt['write']) #PLT表的地址
0x401680
小工具
栈溢出找偏移
>>> print cyclic(20) #生成20字节每4个字节(默认,可以给出参数如cyclic(20,n=8)指定)不随机的字符串
aaaabaaacaaadaaaeaaa
>>> # Assume EIP = 0x62616166 ('faab' which is pack(0x62616166)) at crash time
>>> print cyclic_find('faab') #根据4个字符查找偏移,若n!=4必须指明n的参数
120
汇编与反汇编
>>> asm('mov eax, 0') #汇编
'\xb8\x00\x00\x00\x00'
>>> disasm('\xb8\x0b\x00\x00\x00') #反汇编
' 0: b8 0b 00 00 00 mov eax,0xb'
Shellcode生成
shellcraft模块是shellcode的模块,包含一些生成shellcode的函数。其中的子模块声明架构,比如:
- ARM架构: shellcraft.arm
- AMD64架构: shellcraft.amd64
- Intel 80386架构: shellcraft.i386
- 通用: shellcraft.common
可以先声明框架直接汇编shellcraft.sh():
context(arch='i386', os='linux')
shellcode = asm(shellcraft.sh())
DynELF
这里举了一个32位的例子:
p = process('./pwnme')
# 声明一个只需要一个地址的函数,并在该地址至少泄漏(返回)一个字节
def leak(address):
data = p.read(address, 4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
# 因为是举例,假设以下地址我们都知道。其中一个指向目标二进制文件里,另两个指向so库里
main = 0xfeedf4ce
libc = 0xdeadb000
system = 0xdeadbeef
#通过构造的leak函数和一个指向目标二进制文件里的指针,可以解析任何地址
#而且可以看到我们不需要这个目标二进制文件副本,直接解析
d = DynELF(leak, main)
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
#但是,如果我们确实有目标二进制文件的副本,可以解析更快
d = DynELF(leak, main, elf=ELF('./pwnme'))
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
#或者,我们可以解析另一个库中的符号,并给出一个指针
d = DynELF(leak, libc + 0x1234)
assert d.lookup('system') == system
fmtstr
payload = fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
- offset (int) – 第一个格式化偏移
- writes (dict) – 字典,向哪个地址写入什么值 {addr: value, addr2: value2}
- numbwritten (int) – 输出字符串已经写入字节数
- write_size (str) – byte, short or int.即每次写一个字节、两个字节还是四个字节
attach
该模块用于调用gdb调试 在python文件中直接设置断点,当运行到该位置之后就会断下
import pwnlib
from pwn import *
p = process('./c')
pwnlib.gdb.attach(p)