pwndbg调试技巧
本文最后更新于 2022年5月24日 中午
官方文档在这里 ,主要总结常用的和容易遗忘的调试命令
网上的帮助文档: GDB break(b):设置断点 (biancheng.net)
常用技巧
在 GDB 里面用 magic 可查看后下断点在 hook 函数,之后继续运行即可断在 hook 函数之前,
pwndbg> magic pwndbg> b *__malloc_hook pwndbg> c
断点断在 IDA 中的地址再加随机偏移
pwndbg> b *$rebase(0x400123)
通过泄露双向列表 bins 中的 main_arena+0x88 的 fd 指针来得到 libc 基址,并查看:
pwndbg> heap pwndbg> p &main_arena pwndbg> libc pwndbg> p 0x1 - 0x2
通过 realloc_hook 调整栈帧使 one_gadget 生效
点打在 hook 函数,一步步走到 exec_comm ,然后查看堆栈,查看 realloc 调整偏移
pwndbg> b *__malloc_hook pwndbg> c ... pwndbg> x/20gx $rsp + 0x30 pwndbg> x/20i __libc_realloc
常用指令:
查看帮助信息: help
查看一些信息
i //info,只输入info可以看可以接什么参数,下面几个比较常用
i b //常用,info break 查看所有断点信息(编号、断点位置)
i r //常用,info registers 查看各个寄存器当前的值
i f //info function 查看所有函数名,需保留符号
查看调用栈 backtrace
GDB’s follow-fork-mode
parameter can be used to set whether to trace parent or child after fork() calls
执行指令
s //单步步入,遇到调用跟进函数中,相当于step into,源码层面的一步
si //常用,同s,汇编层面的一步
n //单步补过,遇到调用永不跟进,相当于step over,源码层面的一步
ni //常用,同n,汇编层面的一步
c //continue,常用,继续执行到断点,没断点就一直执行下去
r //run,常用,重新开始执行
断点指令
涉及 IO, 断点打在 send 之前!这样在 IO 停止暂停后,GDB 就可以一步一步走程序 pause()就是 python 程序的断点
常用,给 0x123456 地址处的指令下断点 b *0x123456
给函数 fun_name 下断点,目标文件要保留符号才行 b fun_name
, 如b main
$rebase 在调试开PIE的程序的时候可以直接加上程序的随机地址 b *$rebase(0x123456)
给 file_name 的 15 行下断点,要有源码才行 b file_name:15
在程序当前停住的位置下 0x10 的位置下断点 b +0x10
pwndbg 不工作
条件断点,rdi 值为 5 的时候才断 break fun if $rdi==5
删除、禁用断点:
来查看断点编号 info break(简写: i b)
删除 5 号断点,直接 delete 不接数字删除所有 delete 5
禁用 5 号断点 disable 5
启用 5 号断点 enable 5
清除下面的所有断点clear
内存断点指令 watch:
0x123456 地址的数据改变的时候会断 watch 0x123456
变量 a 改变的时候会断 watch a
查看 watch 断点信息 info watchpoints
捕获断点 catch:
syscall 系统调用的时候断住 catch syscall
syscall 系统调用的时候断住,只断一次 tcatch syscall
info break
除syscall外还可以使用的有:
1)throw: 抛出异常
2)catch: 捕获异常
3)exec: exec被调用
4)fork: fork被调用
5)vfork: vfork被调用
6)load: 加载动态库
7)load libname: 加载名为libname的动态库
8)unload: 卸载动态库
9)unload libname: 卸载名为libname的动态库
10)syscall [args]: 调用系统调用,args可以指定系统调用号,或者系统名称
打印查看
巨牛逼的命令:查看泄露地址相关信息: xi 0x123
查看 libc 基地址:libc
栈的 offset:distance rbp rsp
垃圾数据生成:pi 0x110 > cyclic xxx+8
查看可读可写段:先start
一下 后vmmap
存储在堆中名为 arena 的空间的,直接用dq &main_arena 20
查看
查看bss
等数据段时,用dd 0x08048000
dq 0x08048000
查看
查看代码:u __malloc_hook 12
查看想要修改的位置附近是否有可能存在可以伪造的 chunk 内存地址:find_fake_fast &__malloc_hook
find_fake_fast &__free_hook
查看内存指令 x:
相当于自带了* or []
,自带地址解析
查看格式化的内存:x/20gx 0x7fabc
查看反汇编 1:x/20i $rip
查看反汇编 2:disassemble 符号
查看 hook:x &__malloc_hook
查看地址解释:x/20a 0x7fabc
打印指令 p(print):
打印 fun_name 的地址,需要保留符号 p fun_name
计算 0x10-0x08 的结果 p 0x10-0x08
地址相减: distance 0x1 0x2
查看变量 a 的地址 p &a
查看 0x123456 地址的值,注意和 x 指令的区别,x 指令查看地址的值不用星号 p *(0x123456)
打印 rdi 寄存器的值 p, 注意和 x 的区别,这只是显示 rdi 的值,而不是 rdi 指向的值 p $rdi
打印 rdi 指向的值,注意和 x 的区别,p 会打印出数字 p *($rdi)
修改和查找指令
修改数据指令 set
set $rdi=0x10 //把rdi寄存器的值变为0x10
set *(0x123456)=0x10 //0x123456地址的值变为0x10,注意带星号
set *0x7fffffffdbe8 = 0x5555555552d2 //更改返回地址
set args "abc" "def" "gh"//给参数1,参数2,参数3赋值
set args "python -c 'print(b"1234\x7f\xde")' " //使用python给参数赋值不可见字符
查找数据:
从当前位置向后查包含 rdi 的指令,返回若干 search rdi
寻找输入的字符串:search k3ppf0r3
帮助: search -h
堆操作指令(pwndbg 插件独有)
特别注意:
heap
命令显示的Addr
为 chunk 的首地址,含有 chunk 头部;Size
为内存中实际的内存值,往往最后面为 1,表示 prev_in_use
arena //显示arena的详细信息,过于详细
arenas //显示所有arena的基本信息
arenainfo //好看的显示所有arena的信息
bins //常用,查看所有种类的堆块的链表情况
fastbins //单独查看fastbins的链表情况
largebins //同上,单独查看largebins的链表情况
smallbins //同上,单独查看smallbins的链表情况
unsortedbin //同上,单独查看unsortedbin链表情况
tcachebins //同上,单独查看tcachebins的链表情况
tcache //查看tcache详细信息
heap //数据结构的形式显示所有堆块,会显示一大堆
heap -v // 查看完整数据结构
parseheap //显示堆结构,很好用,与heap大同小异
mp //查看堆相关结构体信息
heapbase //查看堆起始地址
heapinfo、heapinfoall //显示堆的信息,和bins的挺像的,没bins好用
tracemalloc //好用,会跟提示所有操作堆的地方
其他pwndbg插件独有指令
cyclc 50 //生成50个用来溢出的字符,如:aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
$reabse //开启PIE的情况的地址偏移
b *$reabse(0x123456) //断住PIE状态下的二进制文件中0x123456的地方
codebase //打印PIE偏移,与rebase不同,这是打印,rebase是使用
stack //查看栈
retaddr //打印包含返回地址的栈地址
canary //直接看canary的值
plt //查看plt表
got //查看got表
hexdump //想IDA那样显示数据,带字符串
# x /nuf 0x123 指令格式:
# number
# unit b(1 Byte),h(2 Bytes),w(4 Bytes),g(8 Bytes)
# format 见下表
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
s 按字符串显示。
b 按字符显示。
i 显示汇编指令。
调试备忘:
gdb查看技巧:最左边的地址编号代表的是所在那一格子(由dd dq决定)中最右边,故小端序整体从右往左读(明确数据按照地址从小往大填入),对于两位16进制需一体从左往右顺序读 在gdb排版的最右边它将顺序调整好后输出ASCII码
gdb.attach()
1 gdb 如果断不住,就这样断:
# 开头
gdb.attach(p)
...
# 输入之前暂停
pause()
p.sendline()
2 左边是交互,右边是程序在内存中的运行情况,可以查看堆栈、寄存器情况等
3 继续运行, 在右边按c
或【ni
+ finish
】 ,在左边进行 IO 交互;
中断查看堆栈情况, 在右边按CTRL + c
查看 rop 链等运行时的信息
# 断点打在read之后
gdb.attach(p, 'b $rebase(0x400123)')
脚本预设
from pwn import *
context.log_level = 'debug'
# context.terminal = ['tmux', 'splitw', '-h', '-F', '#{pane_pid}', '-P']
gdb.attach(p,'break main')
raw_input()
这样调试有一个缺点,那就是 gdb 在 attach 到程序之后,你要调试的断点可能已经早就过去了,来不及下断点,这就会导致 gdbscript 执行失败。 若第一种失败,那么可以这样做:
from pwn import *
# 断点打在read之后
# p=gdb.debug("./pwn","b *0x400123")
p=gdb.debug("./pwn","b main")
raw_input()
在 GDB 过程中的理解
堆栈形状
栈(高地址向低地址生长):
low
| new_space | <- rsp
| new_space |
| new_space |
| prev_rbp | <- rbp
| 0x400456 | return_addr
high
堆:
Allocated chunk | PREV_INUSE
Addr: 0x55555555f000
Size: 0x291
Free chunk (tcache) | PREV_INUSE
Addr: 0x55555555f290
Size: 0x51
fd: 0x00
Top chunk | PREV_INUSE
Addr: 0x55555555f2e0
Size: 0x20d21
数据流
二进制程序以 read 函数接收,当 socket 用 send 发过去的字节流(明确以字节为单位,debug 显示的是人类的阅读顺序),以发送时的字节排列顺序填入虚拟内存中(从小到大填);
二进制程序以 write 函数打印,将内存中数据 (以从小到大的顺序) 忠实的打印出来
x 命令的打印阅读顺序:
<- __\ 0 <- __\ 4/8
用db addr
指令可验证
寄存中显示字符串存在 bug,它将字符串当成指针反过来解析了
去掉符号表的混淆调试
- 对应着 ida 的汇编代码看,ida 中地址与 gdb 中的地址后三位一样
terminal 优化:
~/.gdbinit 内容如下:
define init-peda
source ~/peda/peda.py
end
document init-peda
Initializes the PEDA (Python Exploit Development Assistant for GDB) framework
end
define init-peda-arm
source ~/peda-arm/peda-arm.py
end
document init-peda-arm
Initializes the PEDA (Python Exploit Development Assistant for GDB) framework for ARM.
end
define init-peda-intel
source ~/peda-arm/peda-intel.py
end
document init-peda-intel
Initializes the PEDA (Python Exploit Development Assistant for GDB) framework for INTEL.
end
define init-pwndbg
source ~/pwndbg/gdbinit.py
source ~/Pwngdb/pwngdb.py
source ~/Pwngdb/angelheap/gdbinit.py
define hook-run
python
import angelheap
angelheap.init_angelheap()
end
end
end
document init-pwndbg
Initializes PwnDBG
end
define init-gef
source ~/gef/gef.py
end
document init-gef
Initializes GEF (GDB Enhanced Features)
end