pwndbg go-dump 命令详解:在 GDB/LLDB 中原生解析与转储 Go 内存值
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
go-dump(别名god)是 pwndbg 内存(memory)分类下专为 Go 二进制设计的命令,它能在调试会话中按 Go 类型语法直接解析指定地址上的内存,并以接近 Go 语言字面量的形式输出结构体、切片、map、接口等复合值。读完本文,你将掌握go-dump的完整用法、全部可选参数与格式控制配置项,理解其底层"运行期类型解码"(runtime type decoding)机制,并能结合go-type命令对任意 Go 类型地址进行自省与转储。
命令概览:用法、别名与参数
go-dump的完整帮助信息位于 docs/commands/memory/go-dump.md,命令实现位于 pwndbg/commands/godbg.py。其用法为:
usage: go-dump [-h] [-x] [-f [DECIMALS]] [-d] [-p] ty address命令接受两个必选位置参数与若干可选参数,详见下表:
| 位置参数 | 说明 |
|---|---|
ty | 要转储值的 Go 类型,例如map[int]string;也可以是运行期类型对象的地址(如0x408860),pwndbg 会在运行时解析该地址处的反射类型 |
address | 要转储的内存地址,由pwndbg.commands.AddressExpr解析,支持表达式(如&x、寄存器等) |
| 短参数 | 长参数 | 说明 |
|---|---|---|
-h | --help | 显示帮助信息并退出 |
-x | --hex | 以十六进制显示非指针整数 |
-f | --decimals | 配置浮点数显示的小数位数 |
-d | --debug | 显示调试信息,例如切片/map 元素的地址、切片容量等 |
-p | --pretty | 启用美化输出(pretty printing) |
从命令实现(pwndbg/commands/godbg.py)可以看到,命令处理器会先尝试把ty按整数解析为运行期类型地址并调用pwndbg.aglib.godbg.decode_runtime_type()解码;若解析失败(ValueError),则退化为调用parse_type()按类型字符串语法解析,最终统一构造FormatOpts并调用parsed_ty.dump(address, fmt)完成内存转储。
支持的 Go 类型语法
go-dump的类型语法与 Go 语言本身高度一致,可以转储如下类型(详见 docs/tutorials/go-debugging.md):
- 整数类型:
int、int8、int16、int32、int64、int128以及对应的uint系列; - 杂项类型:
bool、rune、uintptr、string; - 浮点类型:
float32、float64; - 复数类型:
complex64、complex128; - 接口类型:
any(表示空接口interface{}),interface(表示所有非空接口); - 函数类型:
funcptr(表示任意函数类型); - 指针:
*ELEM; - 切片:
[]ELEM; - 数组:
[LEN]ELEM; - map:
map[KEY]VAL。
注意:Go 中的 map 变量本质上是内部 map 结构的指针,因此要转储一个 map 变量时通常需要使用
*map[KEY]VAL(即先解引用一次)。
结构体也受支持,但语法与 Go 略有不同:为了免去用户自行计算字段偏移(并支持只提供部分字段信息),结构体以偏移:字段名:类型三元组、用分号分隔后包裹在struct(SIZE){}中。例如struct(24){0:foo:string;16:bar:int64}表示 24 字节的 Go 结构体struct { foo string; bar int64 }。
在 pwndbg/aglib/godbg.py 中,parse_type()依次尝试_parse_runtime_ty、_parse_struct_ty、_parse_map_ty、_parse_array_ty、_parse_pointer_ty、_parse_slice_ty、_parse_basic_ty七种语法解析器,全部失败时抛出ValueError。类型名称解析失败时会给出提示,例如源码中提示未知类型可改用type hexdump[n]以指定大小的方式查看(pwndbg/aglib/godbg.py)。
实战示例
以下示例来自 docs/tutorials/go-debugging.md,可直接在 pwndbg 中复现:
pwndbg> go-dump map[string]int 0xc0000b20f0 {"a": 1, "b": 2, "c": 3} pwndbg> go-dump any 0xc0000ace40 ([]struct { a int; b string }) [struct {a: 1, b: "first"}, struct {a: 2, b: "second"}] pwndbg> go-dump struct(24){0:a:int;8:b:string} 0xc000108120 struct {a: 1, b: "first"}第一个示例直接按map[string]int解析 map 内存;第二个示例用any转储一个空接口值——由于接口内部携带运行期类型信息,pwndbg 能自动还原出底层类型(一个[]struct{...}切片)并递归转储;第三个示例展示了结构体的struct(SIZE){...}语法。测试用例 tests/library/dbg/tests/test_go.py 验证了go-dump any &x输出(map[uint8]uint64) &{1: 2, 3: 4, 5: 6}、(map[string]int) &{"a": 1, "b": 2, "c": 3}以及go-dump -f 1 any &x输出复数数组([3]complex64) [(1.1 + 2.2i), (-2.5 - 5.0i), (4.2 - 2.1i)]等行为。
可选参数与输出格式控制
-x / --hex:将非指针整数以十六进制显示。对应FormatOpts.fmt_int()(pwndbg/aglib/godbg.py),开启后str(val)变为hex(val)。-f / --decimals N:浮点数(含复数实部/虚部)保留N位小数,对应FormatOpts.fmt_float()中的format(val, f".{N}f")。-d / --debug:在转储结果中为每个元素附加内存地址信息——切片元素、map 的 key/val、字符串的数据指针与长度、指针的解引用地址、接口的类型与数据地址等都会以单独颜色标注。该颜色的主题参数为go-dump-debug(默认蓝色,定义见 pwndbg/aglib/godbg.py)。-p / --pretty:启用美化输出。当某一行超过软宽度上限时会换行并按缩进量排版,相关配置项为go-dump-line-width(默认 80,软行宽)与go-dump-indent-amount(默认 4,缩进量),二者定义于 pwndbg/aglib/godbg.py,可在 pwndbg 中通过set命令调整。
运行期类型解析(Runtime Type Parsing)原理
Go 编译器会为程序使用到的每一个类型发射类型对象(type object),这正是go-dump能自动转储接口值、甚至无需用户提供类型名即可转储任意类型的基础。定位类型的一种常用方法是找到传入堆分配函数(如runtime.newobject、runtime.makeslice)的类型指针。
找到类型指针后,可以用配套命令go-type(别名goty,见 docs/commands/memory/go-type.md)检查该类型:
pwndbg> go-type 0x49fbc0 Name: struct { a int; b string } Kind: STRUCT Size: 24 (0x18) Align: 8 Parse: struct(24){0:a:int;8:b:string} Field a: Offset: 0 (0x0) Type name: int Type addr: 0x498ce0 Field b: Offset: 8 (0x8) Type name: string Type addr: 0x498aa0go-type输出名称、种类(Kind)、大小、对齐方式、可直接交给go-dump使用的 Parse 字符串,以及每个字段的偏移与类型信息(实现见 pwndbg/commands/godbg.py)。go-dump同样可以直接接受类型地址作为ty参数:
pwndbg> go-dump 0x49fbc0 0xc000108120 struct {a: 1, b: "first"}从源码结构看,类型解码的核心是decode_runtime_type()(pwndbg/aglib/godbg.py),它按 Go 官方src/internal/abi/type.go中Type结构体的内存布局读取Size_、PtrBytes、Hash、TFlag、Align_、FieldAlign_、Kind_、Equal、GCData、Str、PtrToThis等字段,其中Kind_低 5 位映射到GoTypeKind枚举(pwndbg/aglib/godbg.py),涵盖BOOL、INT、FLOAT32、ARRAY、CHAN、FUNC、INTERFACE、MAP、POINTER、SLICE、STRING、STRUCT等全部种类。解码过程使用cache配合BackrefType占位符处理递归类型(如type a []a),最后统一替换回真实类型。
几个值得注意的底层细节:
- 类型名称读取:Go 1.17 之前类型名是 1 字节位标志后跟 2 字节大端长度前缀的字符串,1.17 之后改为 varint 长度前缀编码,
read_type_name()会根据检测到的 Go 版本选择解码路径(pwndbg/aglib/godbg.py)。 - Go 版本探测:优先读取
runtime.buildVersion符号,否则回退到.go.buildinfo段解析(pwndbg/aglib/godbg.py)。版本不仅影响类型名格式,还影响结构体字段偏移的移位(Go 1.19 前后)与 map 的布局。 - 类型区段定位:类型名以相对
moduledata的偏移存储,get_type_start()优先遍历runtime.firstmoduledata符号链表获取types/etypes区间,找不到调试符号时退化为用type:*符号或.rodata段启发式猜测(pwndbg/aglib/godbg.py)。 - map 的两种布局:Go 1.24 之前 map 采用
hmap/bucket 结构(dump_noswiss),Go 1.24 起采用新的 Swiss Table 布局(dump_swiss),pwndbg 根据探测到的 Go 版本自动切换解析路径(pwndbg/aglib/godbg.py)。 - 架构相关字长与对齐:
word_size()与max_align()依据当前架构返回 Go 的字长与最大对齐值(如 x86-64/aarch64/rv64 为 8 字节,i386/arm 为 4 字节,见 pwndbg/aglib/godbg.py),所有布局计算均以此为基础。
类型系统的源码对应关系
go-dump的类型模型是一个抽象基类Type(定义dump()、size()、align()、get_typename()四个抽象方法,见 pwndbg/aglib/godbg.py)下的具体子类:
BasicType:基本类型。复数按实部+虚部两个浮点布局,字符串按"指针+长度"布局,空接口记为any、带方法接口记为interface、函数指针记为funcptr(pwndbg/aglib/godbg.py);SliceType:切片,按"指针、长度、容量"三个字布局,-d时可输出容量信息(pwndbg/aglib/godbg.py);PointerType:指针,空指针输出nil,否则解引用并递归转储(pwndbg/aglib/godbg.py);ArrayType:定长数组,按连续内存布局逐个转储元素(pwndbg/aglib/godbg.py);MapType:map,内部按前述两种布局之一遍历所有桶/组并按键排序输出(pwndbg/aglib/godbg.py);StructType:结构体,按字段偏移定位各字段内存(pwndbg/aglib/godbg.py);RuntimeType:以runtime(SIZE)ADDRESS表示"运行期类型地址 + 值大小"的类型,用于序列化循环类型(pwndbg/aglib/godbg.py)。
每种类型都实现了additional_metadata(),供go-type命令输出字段偏移、元素/键值类型地址等元数据。
使用提示与适用前提
go-dump只有在程序运行中才可用(命令注册了OnlyWhenRunning装饰器,见 pwndbg/commands/godbg.py)。- 类型字符串解析不依赖调试符号;但运行期类型解码(
ty传地址)在部分路径上依赖符号(如runtime.firstmoduledata),缺失时 pwndbg 会打印警告并退化为启发式定位,见 pwndbg/aglib/godbg.py。 - map 转储对 Go 1.24+(Swiss Table)与旧版(hmap)分别处理,且 map 实际为指针语义,注意使用
*map[KEY]VAL解引用。 - 从源码结构看,channel(
CHAN)类型当前尚不支持转储(pwndbg/aglib/godbg.py 注释标明 "currently channels and functions are unsupported",其中函数可通过funcptr处理)。 - 若遇到非法或无法解析的类型,
parse_type会抛出ValueError,命令会给出类型解析失败的错误信息。
结合配套的 docs/tutorials/go-debugging.md 教程,你可以在 pwndbg 调试 Go 程序时,用go-type自省任意类型对象、用go-dump直接以 Go 字面量形式读取内存中的复合值,显著提升 Go 逆向与漏洞调试效率。
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考