pwndbg cyclic 命令完全指南:循环模式生成、偏移查找与寄存器自动检测
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
导读
本文围绕 pwndbg 的cyclic命令展开,讲解如何利用 De Bruijn 序列(循环模式)快速定位栈溢出、缓冲区溢出等内存破坏漏洞中的偏移量。你将掌握cyclic的序列生成、-l/--lookup偏移反查、-d/--detect寄存器自动检测三大核心用法,并结合源码理解其内部实现原理,从而在真实漏洞利用(Exploit Development)调试中高效定位崩溃点与覆盖位置。
cyclic是 pwndbg 的命令行工具之一(位于 pwndbg/commands/cyclic.py),底层复用 pwntools 的pwnlib.util.cyclic模块,命令帮助文档由仓库脚本自动生成,见 docs/commands/misc/cyclic.md。
cyclic 是什么:用于崩溃偏移定位的循环模式
在缓冲区溢出调试中,常见的做法是向程序输入一串模式字符串,观察崩溃时被写入指令指针(如 x86-64 的RIP)的值,再反查该值在模式串中的位置,从而确定"从输入起点到返回地址/指令指针的距离"。cyclic命令生成的正是这种用于该目的的循环模式(cyclic pattern)。
其核心原理是De Bruijn 序列:给定一个字母表(默认小写字母a-z)和子序列长度n(默认 4),生成一个循环序列,使得其中任意连续的n个字符组合都是唯一的。这意味着:
- 只需 4 个字节即可唯一确定在序列中的位置;
- 任意截取一段
n字节,都能通过反查定位其绝对偏移。
cyclic命令本身并不发明这一算法,而是直接调用 pwntools 的pwnlib.util.cyclic.cyclic与pwnlib.util.cyclic.cyclic_find,见 pwndbg/commands/cyclic.py。
命令语法与参数总览
cyclic的完整用法如下(源自 docs/commands/misc/cyclic.md):
usage: cyclic [-h] [-a charset] [-n length] [-t seconds] [-l lookup_value] [-d] [count] [filename]其功能定位是"Cyclic pattern creator/finder",即既是模式生成器,也是偏移查找器。
位置参数(Positional arguments)
| 位置参数 | 说明 |
|---|---|
count | 从序列中打印的字符数量(默认打印整个序列,默认值为 100) |
filename | 将生成的循环模式保存到该文件的路径(写为二进制) |
可选参数(Optional arguments)
| 短选项 | 长选项 | 说明 |
|---|---|---|
-h | --help | 显示帮助信息并退出 |
-a | --alphabet | 循环模式使用的字母表(默认:abcdefghijklmnopqrstuvwxyz) |
-n | --length | 唯一子序列的长度(默认:4) |
-t | --timeout | --detect模式下每个寄存器检测的超时秒数(默认:2) |
-o | --lookup | 执行偏移查找而非打印序列(接受常量值以及表达式) |
-d | --detect | 检测寄存器中的循环模式(包括立即数和寄存器指向的内存) |
使用提示(Notes)
如果想把循环模式写入内存,请使用
spray命令!
从源码看,-l与-o、--offset、--lookup是同一组互斥参数(pwndbg/commands/cyclic.py),即-l是-o的兼容别名;而-l、-d与位置参数count三者构成argparse的互斥组,同一命令只能选择一种模式。
模式一:生成循环模式
在 GDB/LLDB 中直接执行:
pwndbg> cyclic 100 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa- 未指定
count时默认打印 100 个字符; - 未指定
-n时默认按 4 字节唯一子序列生成; - 未指定
-a时使用小写字母表a-z。
也可以生成自定义字母表、自定义子序列长度的模式:
pwndbg> cyclic -a ABCDEFGH 50 pwndbg> cyclic -n 5 100生成的结果可直接作为输入填充到崩溃程序(例如通过run < payload或文件注入)。
保存到文件
指定filename参数即可将模式以二进制形式写入文件(pwndbg/commands/cyclic.py):
pwndbg> cyclic 300 /tmp/payload Written a cyclic sequence of length 300 to file /tmp/payload随后可用run < /tmp/payload等方式将 payload 输入目标程序,或与spray命令配合写入进程内存。
模式二:-l偏移反查(lookup)
当程序崩溃、寄存器中出现模式片段时,用-l反查其偏移。-l的值可以是:
- 十六进制常量:
cyclic -l 0x6161616b - 寄存器表达式:
cyclic -l $rax - 内存地址解引用表达式:
cyclic -l '*(unsigned long*)0x7fffffffdf00'
源码中对-l的处理流程如下(pwndbg/commands/cyclic.py):
- 先用
pwndbg.commands.fix()对参数求值(该函数会依次尝试调试器表达式求值、寄存器名解析,见 pwndbg/commands/init.py); - 若求值结果是整数,则按当前架构字节序(
endian)转为-n长度的字节;若超出长度则截断; - 若求值结果是字符串,则按 UTF-8 编码并截断为
-n长度; - 校验长度与字母表合法性后,调用
cyclic_find()计算偏移并输出。
输出格式:
pwndbg> cyclic -l 0x6161616b Finding cyclic pattern of 4 bytes: b'aaak' (hex: 0x6161616b) Found at offset 37- 命中:打印
Found at offset N,N即该 4 字节在模式中的偏移; - 未命中:打印
Given lookup pattern does not exist in the sequence; - 长度不足:打印
Lookup pattern must be at least N bytes (use '-n <length>' to lookup pattern of different length); - 包含字母表外的字符:打印
Pattern contains characters not present in the alphabet。
小技巧:如果崩溃时
RIP中的 4 字节是aaak,而输入从RSP附近开始填充,那么偏移 37 基本就是"从输入起点到被覆盖返回地址的距离",可据此精准构造后续利用载荷。
模式三:-d寄存器循环模式自动检测
当崩溃现场寄存器较多、逐个手工反查太繁琐时,cyclic -d会自动遍历当前架构的所有寄存器,检测两种来源的循环模式(pwndbg/commands/cyclic.py):
- 寄存器立即值:寄存器本身的值(取前
-n字节)是否是模式的一部分; - 寄存器指向的内存:若寄存器值指向可读内存,读取该地址的
-n字节进行匹配,输出行以reg->前缀标注。
对每个寄存器,源码会用signal.SIGALRM设置--timeout(默认 2 秒)的超时保护,防止对不可读内存的匹配导致卡死;超时会输出SKIPPED (Timeout)(见 pwndbg/commands/cyclic.py)。
输出为表格形式:
pwndbg> cyclic -d Register Value Offset ---------- -------------------- ------ rax 0x6161616b 37 rbx-> 0x6161616d 45其中:
Register:寄存器名,->后缀表示是寄存器指向的内存;Value:命中模式的寄存器值/内存值;Offset:该值在循环模式中的偏移。
-d也支持-a与-n定制字母表与子序列长度,例如使用数字字母表检测:
pwndbg> cyclic -d -a 0123456789ABCDEF如果没有任何寄存器命中,则输出No cyclic patterns found.;若进程未运行,则输出Error: Process is not running.。
-d的源码级工作流程
- 通过
pwndbg.aglib.proc.alive()检查进程存活; - 从
pwndbg.aglib.arch获取指针宽度与字节序; - 遍历
pwndbg.lib.regs.reg_sets[arch].all中的所有寄存器名(该架构全集定义见 pwndbg/lib/regs.py); - 对每个寄存器:读取值 → 按指针宽度编码为字节 →
cyclic_find匹配; - 若地址可读(
pwndbg.aglib.memory.is_readable_address),再读内存匹配,见 pwndbg/commands/cyclic.py。
与spray命令的配合:把模式写入内存
cyclic文档特别提示:需要把循环模式写入内存时,应使用spray命令。spray定义于 pwndbg/commands/spray.py,其默认行为就是用cyclic()序列填充指定内存区域:
usage: spray [-h] [--value VALUE] [-x] addr [length]常用示例:
pwndbg> spray 0x7fffffffde00 200 # 向该地址写入 200 字节循环模式 pwndbg> spray $rsp 200 # 从 RSP 开始喷洒 200 字节参数要点:
addr:起始地址;length:写入长度,不指定则一直写到该地址所在 vmmap 页的末尾(源码中通过pwndbg.aglib.vmmap.find(addr)定位页并计算page.end - addr,见 pwndbg/commands/spray.py);--value:自定义填充值,0x前缀视为十六进制字符串并按大端编码,否则按 UTF-8 处理并循环填充;-x/--only-funcptrs:只覆盖指向可执行页(page.execute)的指针,用于函数指针喷射场景(见 pwndbg/commands/spray.py)。
spray与cyclic的组合典型场景是:先用spray把循环模式铺满栈/堆,触发崩溃后用cyclic -l或cyclic -d反查偏移,实现"填充—崩溃—定位—利用"的完整闭环。
实战示例:一次完整的溢出定位流程
假设有一个存在栈溢出的程序,把 300 字节输入拷贝到栈缓冲区。步骤如下:
第一步:生成模式文件
pwndbg> cyclic 300 /tmp/cyclic_payload Written a cyclic sequence of length 300 to file /tmp/cyclic_payload第二步:触发崩溃
pwndbg> run < /tmp/cyclic_payload程序崩溃后,RIP中会是类似0x6161616b(对应aaak)的模式片段。
第三步:反查偏移
pwndbg> cyclic -l $rip Finding cyclic pattern of 4 bytes: b'aaak' (hex: 0x6161616b) Found at offset 37第四步(可选):全寄存器自动检测
pwndbg> cyclic -d如果多个寄存器都残留了模式值,-d会一次性列出全部命中偏移,比逐个-l更高效。
得到偏移 37 后,即可构造b"A"*37 + ret_addr + payload形式的利用载荷。
边界情况与错误处理
从源码 pwndbg/commands/cyclic.py 和测试用例中可以归纳出以下边界行为:
- 查找值长度不足
-n:报错并提示改用-n调整,对应测试见 tests/library/dbg/tests/test_command_cyclic.py; - 查找值含字母表外字符:报错,对应测试
test_command_cyclic_wrong_alphabet(同上文件 L82-L90); - 查找值不在序列中:输出
Given lookup pattern does not exist in the sequence; --detect在进程未运行时:直接报错退出;--detect内存读取超时:该寄存器标记SKIPPED (Timeout)后继续处理下一个,不会卡死整个命令。
测试覆盖:行为有据可查
仓库对cyclic的三种模式均有自动化测试,可作为行为规范的权威参考:
- 常量值反查:
test_command_cyclic_value验证cyclic -l 0x...输出Found at offset 37,见 tests/library/dbg/tests/test_command_cyclic.py; - 寄存器反查:
test_command_cyclic_register验证cyclic -l $reg输出Found at offset 45(同文件 L32-L56); - 内存地址解引用反查:
test_command_cyclic_address验证cyclic -l '*(unsigned long*)...'输出Found at offset 48(同文件 L59-L79); - 自定义字母表检测:GDB 侧测试
test_command_cyclic_detect同时验证了寄存器立即值、寄存器指向的内存(rbx->)以及自定义字母表三种检测场景,见 tests/library/gdb/tests/test_command_cyclic.py。
这些测试同时覆盖了默认小写字母表与自定义字母表(如0123456789ABCDEF),确认-a参数确实会改变模式空间。
小结
cyclic是 pwndbg 中"快速定位溢出偏移"的核心工具:count/filename负责生成与保存模式,-l/--lookup负责精确反查单个值(支持常量、寄存器与内存表达式),-d/--detect负责全寄存器自动扫描(含指针解引用与超时保护),而-a、-n、-t提供了字母表、子序列长度与超时的定制能力。结合 pwndbg/commands/cyclic.py 的源码实现与 tests/library/dbg/tests/test_command_cyclic.py 等测试用例,你可以精确预期每种输入下的输出行为,并在真实漏洞调试与利用开发中稳定复现。
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考