
pwndbg spray 命令实战用 cyclic 模式或自定义值批量填充目标内存【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg导读spray是 pwndbg 提供的内存喷洒memory spray命令用于向指定地址连续写入由cyclic()生成的去重字节序列或写入用户自定义的字节值是漏洞利用开发与崩溃分析阶段快速浇灌栈、堆、BSS 段等内存区域的实用工具。读完本文你将掌握spray的完整语法与参数语义、其默认喷洒到所在内存段末尾的行为、大端十六进制与 UTF-8 两种自定义值的编码规则以及-x模式只覆盖指向可执行页的指针的底层实现并能够结合cyclic命令完成填充内存 → 触发崩溃 → 反查溢出偏移的完整调试闭环。spray 与 cyclic一对孪生命令pwndbg 中与spray关系最密切的命令是cyclic两者的文档位于同一目录spray 命令文档cyclic 命令文档cyclic负责生成与查找循环去重模式其实现位于 pwndbg/commands/cyclic.py底层复用 pwntools 的pwnlib.util.cyclic而spray负责把模式或指定值写入内存。在cyclic命令的帮助说明中官方明确给出了两者分工的提示If you want to write the cyclic pattern to memory, use thespraycommand!也就是说cyclic负责生成与反查spray负责落地写入二者配合才能完成先灌模式、再定位偏移的经典流程。命令语法与参数总览spray的完整用法来源于命令解析器定义见 pwndbg/commands/spray.pyusage: spray [-h] [--value VALUE] [-x] addr [length]位置参数说明addr要喷洒的目标起始地址length要写入的字节序列长度缺省为 0此时会一直喷洒到addr所属 vmmap内存映射段的末尾可选参数短选项说明-h/--help无显示帮助信息并退出--value无用于喷洒内存的自定义值以0x前缀开头时按大端十六进制字符串解析否则按 UTF-8 字节串处理-x/--only-funcptrs有只覆盖其值指向可执行页的地址即只喷洒函数指针spray在命令分类中属于CommandCategory.MISC杂项并且带有pwndbg.commands.OnlyWhenRunning装饰器——即必须附着在正在运行/挂起的调试会话GDB 或 LLDB中才能使用且要求目标进程存在。基础用法覆盖整段映射最基本的调用只需要给出起始地址spray 0x7fffffffe000此时length为默认值 0pwndbg 会先通过pwndbg.aglib.vmmap.find(addr)查询该地址所属的内存映射段然后把长度自动计算为page.end - addr从起始地址一直到该段末尾。这正是原文档中when unspecified sprays until the end of vmmap which address belongs to的含义实现于 pwndbg/commands/spray.py。需要说明的两个边界行为如果addr不在任何已知 vmmap 段内例如指向一个完全未映射的地址find()返回None命令会打印错误信息Invalid address ... cant find vmmap containing it to determine the spray length并直接返回不会产生任何写入。一旦确定了长度写入失败例如目标区域不可写会抛出pwndbg.dbg_mod.DebuggerError命令会捕获并打印错误信息见 pwndbg/commands/spray.py。如果需要限定长度可以显式传入第二个参数spray 0x7fffffffe000 0x400此时只写入 0x400 字节不会波及整个内存段。默认喷洒内容cyclic 循环去重模式在不指定--value时spray写入的是cyclic(length, nptrsize)生成的循环模式value_bytes cyclic(length, npwndbg.aglib.arch.ptrsize)其中ptrsize是当前架构的指针宽度x86-64 为 8 字节、i386 为 4 字节见 pwndbg/aglib/arch_mod.py 处的架构定义。也就是说模式中任意连续ptrsize个字节在其出现范围内都是全局唯一的。这一特性是定位溢出偏移的基础崩溃时寄存器的值、或栈上被覆盖的返回地址都可以通过cyclic -l value反查到它在模式中的偏移量从而精确算出距离缓冲区起始位置的字节数。一个完整的喷洒 → 崩溃 → 定位偏移流程在目标函数入口或溢出点前停住用cyclic生成一份模式并写入内存spray 0x7fffffffe000 0x1000这样缓冲区及其后 0x1000 字节全部被唯一模式填充。继续执行continue让程序发生崩溃或函数返回此时被覆盖的 RIP/返回地址、或寄存器中的值即为模式的一部分。使用cyclic -l 崩溃值反查偏移cyclic支持常量、寄存器与内存表达式作为查找目标见 pwndbg/commands/cyclic.pycyclic -l 0x6161616b输出Found at offset 37即可得知缓冲区从偏移 37 处开始覆盖返回地址。这一lookup能力在仓库的集成测试中有对应验证用例例如 tests/library/dbg/tests/test_command_cyclic.py 中的test_command_cyclic_value以cyclic -l {hex(val)}查找常量值并断言输出为Found at offset 37test_command_cyclic_address则通过cyclic -l *(unsigned long*)...直接对内存地址做表达式反查。注意spray与cyclic共享同一套 pwntools 模式生成逻辑因此cyclic命令生成的偏移量可以直接套用在spray写出的内存上两者天然兼容。自定义值喷洒--value 的两种编码当传入--value时spray不再生成 cyclic 模式而是按以下规则将参数编码为字节串见 pwndbg/commands/spray.pyif value.startswith(0x): value_bytes int(value, 16).to_bytes((len(value[2:]) 1) // 2, byteorderbig) else: value_bytes bytes(value, utf-8)规则拆解以0x前缀开头按十六进制字符串解析并转换为大端big-endian字节序。例如--value 0xdeadbeef会被写成字节序列de ad be ef高位字节在前。注意字节长度由去除0x后的十六进制位数决定(len 1) // 2表示按两个十六进制字符一组计算字节数奇数位时高位补一个半字节。例如0xabc会被编码为 2 字节的0a bc。不带0x前缀直接把参数字符串按 UTF-8 编码为字节串。例如--value AAAA会写入 4 个0x41字节。随后spray会把该字节串重复平铺到目标长度value_length len(value_bytes) value_bytes value_bytes * (int(length) // value_length) if length % value_length ! 0: value_bytes value_bytes[: (length % value_length)]即先整段重复铺满若长度不能整除则在末尾补上值串的前length % value_length个字节。也就是说--value的值不需要与length对齐命令会自行处理截断与补齐。应用示例# 把 0x1000 字节全部写成 A0x41 spray 0x7fffffffe000 0x1000 --value A # 把一段内存全部填充为大端 0xdeadbeef 值 spray 0x603000 0x200 --value 0xdeadbeef定向覆盖函数指针-x / --only-funcptrs-x--only-funcptrs是spray最有特色的参数。开启后spray不会盲目整段写入而是先读取目标区域的现有内容按指针宽度逐格检查只覆写那些其值指向可执行页的地址。其核心循环如下见 pwndbg/commands/spray.pyif only_funcptrs: mem pwndbg.aglib.memory.read(addr, length) addresses_written 0 ptrsize pwndbg.aglib.arch.ptrsize for i in range(0, len(mem) - (length % ptrsize), ptrsize): ptr_candidate pwndbg.aglib.arch.unpack(mem[i : i ptrsize]) page pwndbg.aglib.vmmap.find(ptr_candidate) if page is not None and page.execute: pwndbg.aglib.memory.write(addr i, value_bytes[i : i ptrsize]) addresses_written 1 print(message.notice(fOverwritten {addresses_written} function pointers))执行语义可以总结为先用pwndbg.aglib.memory.read(addr, length)读出目标区域的原始内容以指针宽度ptrsize为步长遍历步进为range(0, len(mem) - (length % ptrsize), ptrsize)忽略尾部不足一个指针的零头每次用pwndbg.aglib.arch.unpack见 pwndbg/aglib/arch_mod.py按当前架构与端序把 8/4 字节解包成一个整数作为候选指针值用vmmap.find(ptr_candidate)判断这个值落在哪个内存段并检查该段的execute属性即是否可执行权限定义在 pwndbg/lib/memory.py 的Page类中只有当该值指向一个可执行页时才把对应位置的ptrsize字节覆写为喷洒值最后打印Overwritten N function pointers汇总被覆盖的指针数量。这一模式非常适合定向篡改函数指针表、vtable 或回调地址的场景传统整段喷洒会把所有数据包括正常字符串、整数一并破坏而-x只精准命中长得像代码地址的槽位让攻击者可以在不破坏其他数据的前提下快速覆盖跳转目标。从源码看 spray 的完整调用链综合 pwndbg/commands/spray.py 全文件可以把spray的执行流程归纳为参数解析argparseaddr、length、--value、-x四类参数长度解析length 0时通过pwndbg.aglib.vmmap.find(addr)定位映射段并计算page.end - addr找不到映射则报错返回值编码有--value时按大端 hex / UTF-8 编码并平铺补齐无--value时走cyclic(length, nptrsize)写入非-x模式直接pwndbg.aglib.memory.write(addr, value_bytes)整段写入-x模式按指针步长过滤后选择性写入错误处理捕获pwndbg.dbg_mod.DebuggerError并以红色错误信息打印。值得注意的架构适配细节cyclic的n与-x的步长都取自pwndbg.aglib.arch.ptrsizeunpack则按pwndbg.aglib.arch.endian解析字节序因此spray在 x86-64、i386、ARM、AArch64 等不同架构与大小端平台上都会自动适配指针宽度无需用户手动指定。注意事项与适用前提仅限运行中的调试会话spray带OnlyWhenRunning装饰器必须附着在进程上才能使用包括 GDB 与 LLDB 后端两个后端的读写接口统一封装于pwndbg.aglib.memory。写入受内存权限约束spray本身不修改页权限目标段必须可写栈、堆、BSS、已映射的rw-段均可对只读段或未映射地址写入会触发DebuggerError并被命令捕获打印。默认喷洒范围较大省略length时会写满整个映射段对于大段映射如整个堆区域可能产生明显数据改动建议尽量显式指定长度。-x只做读改写它依赖先读内存再逐格判断因此目标区域必须可读同时它只覆盖满足指向可执行页的槽位其余数据保持原样。与cyclic配合使用spray默认模式与cyclic命令共享同一套 pwntools 生成逻辑生成-喷洒-反查偏移三者可以无缝衔接若使用--value自定义值则无法再用cyclic -l反查偏移。参考链接spray 命令实现源码cyclic 命令实现源码cyclic 命令文档架构指针宽度与 unpack 实现vmmap.find 实现Page 类与内存页权限定义cyclic 命令集成测试【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考