随时更新

pwntools

pwntools通用部分

头部字段

#当 context.log_level被设置为 "DEBUG" , 我们的输入和服务器的输出会被直接输出.通常需要注意的是架构(arch)64位写amd64,32位要写i386
context(arch = 'amd64',os = 'linux',log_level = 'debug')

连接程序

io = process('./pwn')	#本地加载程序
io = remote('host',port)	#远程连接程序
io.close()	#断开连接

发送

send(payload) #发送payload,有时候没回显再输的回车就有了
sendline(payload) #发送payload,并进行换行(末尾\n)
sendlineafter('string',payload) #在接收到指定string后发送payload,并进行换行(末尾\n)

接收

recv(N) #接受 N(数字) 字符
recvline() #接收一行输出
recvlines(N) #接收 N(数字) 行输出
recvuntil(some_string,drop =  true)#接收到 some_string 为止,drop参数为true时在返回值内丢弃some_string,仅保留前面内容。

getshell

interactive() #与进程交互

数据格式转换

p64() #可以让我们转换整数到小端序格式,p64转换8字节. p32和p16则分别转换4Byte和2Byte数字p8即1Byte
u64() #可以让我们转换小端序格式到整数,u64转换8字节. u32和u16则分别转换4Byte和2Byte数字u8即1Byte

动态调试

gdb.attach(p) 将进程attach到gdb上

汇编代码生成

shell = asm(shellcraft.sh()) #生成shellcode,shellcode针对的系统位数由头部字段决定
code = asm('''
汇编代码
''')
#asm可转变汇编代码为十六进制数字

函数地址调用

elf.symbols['a_function'] #找到 a_function 的地址
elf.got['a_function'] #找到 a_function的 got地址
elf.plt['a_function'] #找到 a_function 的plt地址
elf.sym['main'] #找到 main 的地址

libc相关

libc = ELF('./libc文件路径') #通过此方式确定的libc文件需要用
libc.symbols['函数名称']来确定所需函数地址
libc.search(“some_characters”) #找到包含 some_characters(字符串,汇编代码或者某个数值)的地址。
#使用时如
for address in elf.search('/bin/sh\x00'): 
	print hex(address)

libc=LibcSearcher(“gets”,gets_real_addr) #通过此方式确定的libc文件需要用
libc.dump('system') #表示system在libc中的偏移地址
libc.dump('str_bin_sh') #表示/bin/sh字符串在libc中的偏移地址
from pwn import * 
context.log_level = 'debug' 
io = process('./pwn') 
elf = ELF('./pwn') 
libc = ELF('/lib/i386-linux-gnu/libc.so.6') 
def exec_fmt(payload): 
	io.sendline(payload) 
	info = io.recv() 
	return info 
auto = FmtStr(exec_fmt) 
offset = auto.offset

pwntools具体攻击手法

格式化字符串

fmtstr_payload(offset, {printf_got: system_addr})
#(偏移,{写入地址:写入内容})

SROP

context.arch = 'i386' #32位程序
SigreturnFrame(Kernel = 'i386') #在32位系统运行32位程序
SigreturnFrame(Kernel = 'amd64') #在64位系统运行32位程序

context.arch = 'amd64' #64位程序
SigreturnFrame(Kernel = 'amd64') #在64位系统运行64位程序

frame.rax = constants.SYS_execve #修改栈中对应寄存器值
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall

ret2dlresolve

pwndbg

常用命令

加载程序: gdb pwn
运行程序: run
调试程序:
n next 单步到程序源代码的下一行,不进入函数。
ni nexti 单步一条机器指令,不进入函数。
s step 单步到下一个不同的源代码行(包括进入函数)。
si stepi 单步一条机器指令。
下断点:
b 函数名
b *地址
继续运行程序: c
查看所有函数名: info functions(i functions)
查看指定函数反汇编代码: disassemble 函数名
查看当前函数反汇编代码: disassemble $pc
查看libc动态地址: libc
查看字符串所在地址: search 字符串
查看程序源代码:
list(l) 反复输入表示继续向后输出
list 函数名 查看指定函数源代码
list 行号 查看指定行号前后十行源代码
寄存器
p $寄存器 查看指定寄存器值
reg 查看全部寄存器值
内存
info proc mappings 查看程序内存布局
x/字节数 地址 以反汇编形式查看数据
x/[数量]xw 地址以32位十六进制形式查看数据
x/[数量]gw 地址以64位十六进制形式查看数据
堆栈
backtrace(bt) 查看函数调用情况
stack 查看栈
heap 查看堆

gadget相关工具

ROPgadget

查找所有以ret结尾的Gadget:
ROPgadget --binary <文件名> --only "pop|ret"

查找特定寄存器(如rdi或rsi)相关的Gadget:
ROPgadget --binary <文件名> --only "pop|ret" | grep rdi
ROPgadget --binary <文件名> --only "pop|ret" | grep rsi

查找二进制文件中的特定字符串(如/bin/sh)的地址:
ROPgadget --binary <文件名> --string '/bin/sh'
ROPgadget --binary <文件名> --string 'cat flag.txt'

自动生成ROP链
ROPgadget --binary <文件名> --ropchain
注:得到的rop链脚本在exp中需要加入包才可使用(from struct import pack)

one_gadget

one_gadget <libc文件名>

ropper

ropper	#进入ropper终端
file pwn	#加载二进制文件
gadgets		#列出所有gadgets
search pop rdi; ret;	#搜索文件中的pop rdi; ret;gadget

IDA_Pro

快捷键

A: 将数据转换为字符串
G: 直接跳转到某个地址
N: 更改变量的名称
U: undefine,取消定义函数、代码、数据的定义
X: 对着某个函数、变量按该快捷键,可以查看它的交叉引用
Y: 更改变量的类型

F5: 反汇编出c伪代码
ESC: 回退键,能够倒回上一部操作的视图(只有在反汇编窗口才是这个作用,如果是在其他窗口按下esc,会关闭该窗口)

Ctrl+M: 列出所有标签
Ctrl+W: 保存ida数据库
Ctrl+S: 选择某个数据段,直接进行跳转
Ctrl+鼠标滚轮: 够调节流程视图的大小

Ctrl+Shift+W: 拍摄IDA快照
Shift+F12: 可以打开string窗口,一键找出所有的字符串,右击setup,还能对窗口的属性进行设置

Alt+T: 搜索文本
Alt+B: 搜索16进制
Alt+M: 添加标签

斜杠/ : 在反编译后伪代码的界面中写下注释
反斜杠\: 在反编译后伪代码的界面中隐藏/显示变量和函数的类型描述,有时候变量特别多的时候隐藏掉类型描述看起来会轻松很多
分号;: 在反汇编后的界面中写下注释