pwndbg调试技巧

本文最后更新于 2022年5月24日 中午

官方文档在这里 ,主要总结常用的和容易遗忘的调试命令

网上的帮助文档: GDB break(b):设置断点 (biancheng.net)

常用技巧

  1. 在 GDB 里面用 magic 可查看后下断点在 hook 函数,之后继续运行即可断在 hook 函数之前,

    1
    2
    3
    pwndbg> magic
    pwndbg> b *__malloc_hook
    pwndbg> c
  2. 断点断在 IDA 中的地址再加随机偏移

    1
    pwndbg> b *$rebase(0x400123)
  3. 通过泄露双向列表 bins 中的 main_arena+0x88 的 fd 指针来得到 libc 基址,并查看:

    1
    2
    3
    4
    pwndbg> heap
    pwndbg> p &main_arena
    pwndbg> libc
    pwndbg> p 0x1 - 0x2
  4. 通过 realloc_hook 调整栈帧使 one_gadget 生效

    点打在 hook 函数,一步步走到 exec_comm ,然后查看堆栈,查看 realloc 调整偏移

    1
    2
    3
    4
    5
    6
    pwndbg> b *__malloc_hook
    pwndbg> c
    ...
    pwndbg> x/20gx $rsp + 0x30

    pwndbg> x/20i __libc_realloc

常用指令:

查看帮助信息: help

查看一些信息

1
2
3
4
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

执行指令

1
2
3
4
5
6
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

1
2
3
4
5
6
7
8
9
10
11
除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
1
2
3
4
5
6
7
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
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
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那样显示数据,带字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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 如果断不住,就这样断:

1
2
3
4
5
6
# 开头
gdb.attach(p)
...
# 输入之前暂停
pause()
p.sendline()

2 左边是交互,右边是程序在内存中的运行情况,可以查看堆栈、寄存器情况等
3 继续运行, 在右边按c或【ni + finish 】 ,在左边进行 IO 交互;
中断查看堆栈情况, 在右边按CTRL + c

查看 rop 链等运行时的信息

1
2
3
# 断点打在read之后
gdb.attach(p, 'b $rebase(0x400123)')

脚本预设

1
2
3
4
5
6
7
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 执行失败。 若第一种失败,那么可以这样做:

1
2
3
4
5
6
from pwn import *
# 断点打在read之后
# p=gdb.debug("./pwn","b *0x400123")
p=gdb.debug("./pwn","b main")
raw_input()

在 GDB 过程中的理解

堆栈形状

栈(高地址向低地址生长):

1
2
3
4
5
6
7
8
9
low

| new_space | <- rsp
| new_space |
| new_space |
| prev_rbp | <- rbp
| 0x400456 | return_addr

high

堆:

1
2
3
4
5
6
7
8
9
10
11
12
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 优化:

安装 gdb-peda-pwndbg-gef

~/.gdbinit 内容如下:

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
42
43
44
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

pwndbg调试技巧
https://k3ppf0r.github.io/2022/05/04/基础/pwndbg调试技巧/
作者
k3ppf0r
发布于
2022年5月4日
许可协议