编译程序
GCC编译参数:
-m32
:编译为32位程序
-fstack-protector
:启用栈溢出保护,不过只为局部变量中含有数组的函数插入保护
-fstack-protector-all
:启用栈溢出保护,为所有函数插入保护
-fno-stack-protector
:禁用栈溢出保护
-z noexecstack
:开启NX保护
-z execstack
:关闭NX保护
-z norelro
:关闭relro
-z lazy
:开启relro,等级为1
-z now
:开启relro,等级为2
-fpie -pie
:开启pie,此时强度为1
-fPIE -pie
:开启pie,此时最高强度为2
-no-pie
:关闭pie
文件信息
查看文件属性:file 文件名
查看程序防护:checksec 文件名
Arch
:文件属性
RELRO
:开启后,符号重定向表格属性为只读,或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击,图中Partial RELRO表示我们对GOT表具有写的权限
Stack
:Canary Stack保护(俗称金丝雀),开启后,程序在函数返回前会设置好的金丝雀值进行检查,从而达到堆栈保护的目的
NX
:即No-eXecute(不可执行)的意思,开启后,就算溢出到shellcode位置也不可执行,cpu会抛出异常
PIE
:地址随机化
查看文件头信息:readelf -h 文件名
查看十六进制数据:
-
完整显示:
xxd 文件名
|od -A x -t x1z 文件名
-
部分显示:
od --skip bytes=偏移地址 --read-bytes=字节数 -A x -t x1z 文件名
x1z表示每显示一个十六进制数输出一个空格;x2z表示每显示两个十六进制数输出一个空格*
检索程序导入的函数:objdump -R 文件名
查看程序所有函数及反汇编:objdump -d 文件名
反汇编单个函数:
#!/bin/bash
vmlinux=$1
symbol=$2
if [ -z "$vmlinux" ]; then
echo "usage : $0 vmlinux symbol"
exit
fi
startaddress=$(nm -n $vmlinux | grep "\w\s$symbol" | awk '{print "0x"$1;exit}')
endaddress=$(nm -n $vmlinux | grep -A1 "\w\s$symbol" | awk '{getline; print "0x"$1;exit}')
if [ -z "$symbol" ]; then
echo "dump all symbol"
objdump -d $vmlinux
else
echo "start-address: $startaddress, end-address: $endaddress"
objdump -d $vmlinux --start-address=$startaddress --stop-address=$endaddress
fi
反汇编整个文件:ndisasm -u 文件名
查找ROP:ROPgadget --binary 文件名
过滤ROP:ROPgadget --binary 文件名 --only '指令1|指令2'
本地测试
运行程序:./文件名
通过python构造payload:python -c 'print "payload"' | ./文件名
将文件内容作为输入:cat payload文件 -| ./文件名
远程调试
连接远程服务器:nc ip port
下载远程目录中的程序:scp -P 端口号 -p IP地址:路径/* ./
cyclic
生成字符串队列:cyclic 字符数
计算字符串偏移:cyclic -l 四个字母
ROPgadget
搜索ROP指令:ROPgadget --binary 文件名 --only '指令1|指令2' | grep '需包含的字符串'
自动生成ROP链(若存在),只适用于静态链接程序:ROPgadget --binary 文件名 --ropchain
gdb
安装pwndbg插件
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
sudo ./setup.sh
选择文件
gdb 文件名
gdb
;file 文件名
附加进程:attach 进程ID
运行:run
将文件数据作为运行参数:run < file
运行至程序入口:start
单步步入:si
单步步过:ni
在指定函数设置断点:b 函数名
在指定地址设置断点:b *地址
继续运行程序:continue
| c
查看所有寄存器的值:reg
查看指定寄存器的值:p $寄存器代号
查看内存布局:i proc mappings
查看内存数据:
- 以反汇编形式查看数据:
x/[字节数]i 地址
- 以32位十六进制形式查看数据:
x/[字节数]wx 地址
- 以64位十六进制形式查看数据:
x/[字节数]gx 地址
查看函数调用情况:backtrace
| bt
查看栈数据:stack
查看所有函数定义:i functions
查看指定函数反汇编代码:disassemble 函数名
查看当前函数反汇编代码:disassemble $pc
查看libc动态地址:libc
查找字符串所在地址:search '字符串'
pwntools
安装pwntools:pip install pwntools
导入pwntools:from pwn import *
输出调试信息:context.log_level = 'debug'
设置cpu架构:context.arch = 'i386'/'amd64'
远程连接:sh = remote('IP',PORT)
本地运行:sh = process('./elf')
发送数据:sh.send(data)
发送数据,并在末尾加上换行符(\n):sh.sendline(data)
在指定字符出现后输入指定字符串:sh.sendafter('等待的字符串', '输入的字符串')
在指定字符出现后输入指定字符串与回车:sh.sendlineafter('等待的字符串', '输入的字符串')
接收数据,numb指定字节数,timeout指定超时时间:sh.recv(numb=4096, timeout=default)
接收一行数据,keepends表示是否保留行尾的换行符(\n):sh.recvline(keepends=True)
一直接收到字符串delims出现,drop表示是否保留参数1:sh.recvuntil(delims, drop=False)
一直接收数据直到EOF:sh.recvall()
进入shell模式:sh.interactive()
附加gdb调试:gdb.attach(sh)
加载elf文件:elf = ELF('[文件名]')
查看指定函数的PLT地址:function_plt = elf.plt['函数名']
定位字符串静态偏移:elf.search('字符串').next()
将整数转换成32位的小端字节序:p32(整数)
将4个字节转换成最多4字节的整数:u32(字符串.ljust(4, '\x00'))
将整数转换成64位的小端字节序:p64(整数)
将8个字节转换成最多8字节的整数:u64(字符串.ljust(8, '\x00'))
生成指定字符数量的字符串:str = cyclic(字符数)
查找指定字符串偏移:index = cyclic_find('四个字母')
生成一段shellcode汇编代码:shellcode = shellcraft.sh()
编译shellcode汇编代码为字节码:shellcode = asm(shellcraft.sh())