pwndbg 的 eza 命令详解:在指定内存地址写入字符串(WinDbg 兼容)
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
eza是 pwndbg 为 WinDbg 用户提供的内存编辑命令,其作用是在指定的内存地址写入一个字符串。本文以 docs/commands/windbg/eza.md 为核心骨架,结合 pwndbg/commands/windbg.py 的源码实现,完整讲解 eza 的语法、参数、底层写入原理,以及与ez、eb/ew/ed/eq、da/ds等兄弟命令的分工与配合。读完本文,你将掌握如何在 GDB/LLDB 调试会话中快速向目标进程内存写入字符串(例如伪造 shellcode 参数、覆盖缓冲区数据或构造 payload),并理解其与 WinDbg 原生语义的对应关系。
eza 是什么:WinDbg 兼容层中的字符串写入命令
pwndbg 在 pwndbg/commands/windbg.py 中实现了一整套 WinDbg 兼容命令,供习惯使用 WinDbg 的逆向与漏洞利用工程师在 GDB / LLDB 环境中无缝迁移。这套命令按CommandCategory.WINDBG分类注册,涵盖断点操作(bp/bl/bd/be/bc)、内存读写(db/dw/dd/dq/dc/eb/ew/ed/eq/eza)、反汇编与步进(k/go/pc/ln/peb)等。
eza属于其中的内存写入(edit)命令族。与eb/ew/ed/eq以十六进制字节、字、双字、四字为单位写入不同,eza专门用于一次性写入一整段字符串,对应 WinDbg 中eza(Edit string, ASCII)的语义。
语法与参数
根据 docs/commands/windbg/eza.md 的自动生成文档,命令用法为:
usage: eza [-h] address data其参数定义如下表所示:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
address | 位置参数 | 是 | 要写入的目标内存地址 |
data | 位置参数 | 是 | 要写入的字符串内容 |
-h/--help | 可选参数 | 否 | 显示帮助信息并退出 |
两点值得注意:
- 与
eb等命令不同(eb [-h] address [data ...],data使用nargs="*"可传多个值),eza的data是单个必填字符串,源码中声明为parser.add_argument("data", type=str, help="The string to write.")(见 pwndbg/commands/windbg.py)。 address使用pwndbg.commands.HexOrAddressExpr类型解析(见 pwndbg/commands/windbg.py)。从 pwndbg/commands/init.py 的实现看,该解析器优先把参数当作十六进制数处理——例如输入1234会被解释为0x1234而不是十进制 1234;若按十六进制解析失败,则回退为 GDB 表达式求值,因此也可以直接传$rax、&buf这类寄存器或符号表达式。
实战用法:向内存写入字符串
以 GDB 调试一个目标程序为例,先查看某段可写内存(例如.bss段中的全局缓冲区):
pwndbg> db 0x404000 32 0x404000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x404010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................然后使用eza向该地址写入字符串:
pwndbg> eza 0x404000 /bin/sh再用db或ds验证写入结果:
pwndbg> ds 0x404000 0x404000 '/bin/sh'也可配合寄存器与符号使用(依赖HexOrAddressExpr的表达式回退能力):
pwndbg> eza $rsi "cat /flag"注意:eza带有@pwndbg.commands.OnlyWhenRunning装饰器(见 pwndbg/commands/windbg.py),即只有在调试会话正在运行(存在被调试进程)时才可用,未启动或已退出的程序无法执行写入。
源码实现:eza → ez → eX 的调用链
eza的真正实现非常精简——它在源码中直接复用了ez命令:
@pwndbg.commands.Command(parser, category=CommandCategory.WINDBG) @pwndbg.commands.OnlyWhenRunning def eza(address, data): """ Write a string at the specified address. """ return ez(address, data)(见 pwndbg/commands/windbg.py)
而ez调用公共写入函数eX,并显式传入hex=False:
def ez(address, data): """ Write a character at the specified address. """ return eX(1, address, data, hex=False)(见 pwndbg/commands/windbg.py)
eX是 WinDbg 内存写入命令族(eb/ew/ed/eq/ez/eza)共用的底层实现(见 pwndbg/commands/windbg.py),其关键逻辑如下:
- 空数据防护:
if not data:时直接打印Cannot write empty data into memory.并返回。 - 十六进制分支:
hex=True(eb等命令)时会对每个值做十六进制格式校验、按宽度size补零,并用codecs.decode(string, "hex")解码为字节;eza走hex=False分支,跳过该校验,直接把字符串原样作为待写入字节(data = string)。 - 字节序处理:若目标架构为小端(
pwndbg.aglib.arch.endian == "little"),写入前会将字节序反转(data = data[::-1])。 - 实际写入:调用
pwndbg.aglib.memory.write(address + (i * size), data)完成内存写入(size为 1,因此eza按字节连续写入字符串的每个字符)。 - 错误处理:若目标地址不可访问(抛出
pwndbg.dbg_mod.DebuggerError),打印Cannot access memory at address 0x...;若此前已成功写入部分数据,还会提示(Made N writes to memory; skipping further writes)并停止后续写入。
由此可见,eza的语义是将整个字符串视为按字节序列写入:先写地址address处的第一个字符,随后依次写入address+1、address+2……源码中的注释也印证了这一点——eX开头的说明写道"This relies on windbg's default hex encoding being enforced",而eza正是通过hex=False绕过了 hex 编码约束。
与 ez、eb/ew/ed/eq、da/ds 的分工
在 pwndbg/commands/windbg.py 的编辑命令族中:
ez(Edit Zero-terminated string):usage: ez [-h] address data,功能描述与eza完全一致("Write a string at the specified address."),且实现上也仅是多包了一层eX(1, address, data, hex=False)。源码中还留有一条开发者注释(见 pwndbg/commands/windbg.py):# TODO Is eza just ez? If so just alias. I had trouble finding windbg documentation defining ez——即 pwndbg 开发者认为eza与ez功能等价,eza在实现上直接调用ez。也就是说,在当前版本中,eza与ez可以互换使用。eb/ew/ed/eq:分别以 1/2/4/8 字节宽度写入十六进制数据(usage: eb [-h] address [data ...]),适合精确控制单个字节/字/双字/四字的数值;而eza一次性写入整段字符串,更适合覆盖字符串内容。da/ds:与eza方向相反,用于读取指定地址处的字符串(da [-h] address [max]、ds [-h] address [max]),其中ds默认最大读取 256 字节。实践中常用eza写入、用ds/db验证,形成"写—读"闭环。
此外,完整的 WinDbg 兼容命令清单可在 docs/commands/windbg/ 目录下查阅,每个命令均有同名的自动生成文档页(生成逻辑见 scripts/_docs/ 与 scripts/generate-docs.sh),eza的参数表正是由argparse解析结果自动生成的。
典型应用场景
eza在 Exploit Development 中的典型用途包括:
- 构造参数串:在
ret2libc/ret2syscall利用链中,先向可写内存写入/bin/sh,再让execve的第一个参数指向该地址。 - 伪造内存数据:覆盖全局缓冲区、堆上的字符串字段,或在 ROP 链中预置需要被
strcpy等函数读取的内容。 - 调试辅助:在不重新编译程序的情况下临时修改运行时的字符串常量或配置文件路径,快速验证假设。
注意事项与限制
- 仅在程序运行时可执行:
eza受OnlyWhenRunning约束,静态分析(无 inferior)阶段不可用。 - 地址解析语义:
address默认按十六进制解析,1234等价于0x1234;若需要十进制或表达式,可传入 GDB 表达式(如$rdi+8)。 - 不可写地址会报错:对只读段(如
.text)或未映射地址写入时,会打印Cannot access memory at address ...,且已写入部分不会回滚(仅提示跳过后续写入)。 eza与ez等价:当前源码中eza直接委托给ez,功能与报错行为一致,二者可互换。
小结
eza是 pwndbg WinDbg 兼容层中"按字符串写入内存"的入口:语法简单(eza address data),底层通过eX(1, address, data, hex=False)按字节连续写入,并处理好小端字节序与不可访问内存的报错。它和ez等价,与eb/ew/ed/eq(写十六进制值)、da/ds(读字符串)共同构成了完整的内存编辑工具集,是 GDB/LLDB 环境下延续 WinDbg 操作习惯的实用命令之一。
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考