- 网络安全
- 开发工具
【免费下载链接】gef
GEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs & reverse engineers on Linux
在漏洞利用与逆向分析中,经常需要把目标进程某段内存(shellcode、GOT 表项、构造好的 payload 等)以可直接粘贴的代码字面量形式取出来。GEF 的print-format命令(别名pf)正是为此设计:它能按指定语言、指定位宽把任意地址开始的内存转储为 Python、C、JavaScript、汇编或十六进制字面量,还支持一键复制剪贴板,以及与patch byte命令配合的$_gefN便利变量写回工作流。读完本文,你将掌握print-format的全部参数、各输出格式的代码级生成逻辑、剪贴板与便利变量的底层实现,以及"导出—检视—写回"内存的实战用法。
1. 命令概览与参数说明
print-format的完整用法如下(与 官方命令文档 一致):
gef➤ print-format -h [+] print-format [--lang LANG] [--bitlen SIZE] [(--length,-l) LENGTH] [--clip] LOCATION --lang LANG specifies the output format for programming language (available: ['py', 'c', 'js', 'asm', 'hex'], default 'py'). --bitlen SIZE specifies size of bit (possible values: [8, 16, 32, 64], default is 8). --length LENGTH specifies length of array (default is 256). --clip The output data will be copied to clipboard LOCATION specifies where the address of bytes is stored.各参数含义与取值:
| 参数 | 别名 | 默认值 | 说明 |
|---|---|---|---|
--lang LANG | — | py | 输出语言格式,可选py、c、js、asm、hex,另有一个特殊值bytearray(用于生成$_gef[N]字节访问,见第 4 节) |
--bitlen SIZE | — | 见下文说明 | 每个数组元素的位宽,可选 8 / 16 / 32 / 64 |
--length | -l | 256 | 导出的数组元素个数(不是字节数) |
--clip | — | 关闭 | 将输出结果复制到系统剪贴板 |
LOCATION | — | $pc | 起始地址,支持任意 GDB 地址表达式,如$rsp、$rsp+8、0x401000 |
其中--bitlen的默认行为值得注意:帮助文本写的是 "default is 8",但从源码看,PrintFormatCommand 在do_invoke中实际执行的是args.bitlen = args.bitlen or gef.arch.ptrsize * 2(gef.py#L5647-L5650),即默认位宽 = 指针字节数 × 2——32 位目标上是 8 位(与帮助文本一致),而 64 位目标上是 16 位。因此在 64 位环境下显式指定--bitlen通常更稳妥。
命令由@only_if_gdb_running装饰器保护,必须存在活跃的调试会话,否则返回 "not running" 错误(tests/commands/print_format.py#L16-L18 中有对应断言)。
2. 六种输出格式及其生成逻辑
源码中格式集合定义为valid_formats = ("py", "c", "js", "asm", "hex", "bytearray")(gef.py#L5603),其中前五种是常规"打印字面量"格式,bytearray是面向 GDB 便利变量的特殊模式。各格式的输出样式:
| 格式 | 输出样式示例 |
|---|---|
py(默认) | buf = [0x87, 0xfa, 0xa3, ...] |
c | unsigned char buf[N] = {0x87, 0xfa, ...}; |
js | var buf = [0x87, 0xfa, ...] |
asm | buf db 0x87, 0xfa, ... |
hex | 87faa3f7ff7f000030e6(无分隔的纯 hex 串) |
bytearray | Saved data b'...'... in '$_gefN'(不直接打印全部数据) |
生成这些字面量时,--bitlen决定了解码粒度。源码中的format_matrix属性(gef.py#L5625-L5633)把位宽映射为三样东西:
--bitlen | struct 格式符(含端序前缀) | C 类型 | 汇编指令 |
|---|---|---|---|
| 8 | <B/>B(依端序) | char | db |
| 16 | <H/>H | short | dw |
| 32 | <I/>I | int | dd |
| 64 | <Q/>Q | long long | dq |
从源码结构看,非bytearray路径的实现是:按bitlen/8字节步进读取每个元素地址,用struct.unpack(fmt, gef.memory.read(addr, size))解码(fmt已根据gef.arch.endianness自动带<小端或>大端前缀),再把所有数值以hex()拼接;C 格式在此基础上额外套用类型名并生成unsigned <type> buf[长度] = {...};声明,asm格式则生成buf <db/dw/dd/dq> ...的 Nasm/Masm 风格伪指令。hex格式不走 struct 解码,直接对整个地址区间gef.memory.read(start, end-start).hex()(gef.py#L5675-L5692)。这一设计保证了在小端机上按 16/32/64 位读取多字节元素时得到的是按机器序解释后的值,而非错位拼接。
3. 实战示例
3.1 转储并按位宽取数 + 剪贴板复制
官方文档给出的第一个例子:从$rsp处取 10 字节并复制到剪贴板:
gef➤ print-format --lang py --bitlen 8 -l 10 --clip $rsp [+] Copied to clipboard buf = [0x87, 0xfa, 0xa3, 0xf7, 0xff, 0x7f, 0x0, 0x0, 0x30, 0xe6][+] Copied to clipboard由--clip触发,输出本体仍会打印在控制台,两者并不互斥。
3.2 bytearray 模式配合 patch byte 写回内存
这是该命令最有实战价值的用法,也是 官方文档 重点演示的场景——把$rsp+8的前 10 字节"搬运"到$rip处:
gef➤ print-format --lang bytearray -l 10 $rsp+8 Saved data b'\xcb\xe3\xff\xff\xff\x7f\x00\x00\x00\x00'... in '$_gef0' gef➤ display/x $_gef0[5] 4: /x $_gef0[5] = 0x7f gef➤ patch byte $rip $_gef0流程拆解:
--lang bytearray不打印完整数组,而是把原始字节存入 GDB 便利变量$_gef0,并给出前若干字节的预览(b'...'...);- 之后可以随时用 GDB 原生表达式按字节下标访问,如
display/x $_gef0[5]; patch byte是 patch 命令族 的成员,它内置了对$_gef前缀值的特殊解析:若值以$_gef开头,会先gdb.parse_and_eval把便利变量展开成逐项字节再逐字节按端序写回(gef.py#L9747-L9755)。
官方文档对这一组合的总结是:Very handy to copy-paste-execute shellcodes/data from different memory regions(非常适合在不同内存区之间复制、粘贴、执行 shellcode 或数据)。
3.3 常用变体
# 以 32 位元素打印 8 个(适合观察 dword 序列) gef➤ pf --bitlen 32 -l 8 $rsp # C 风格 64 位数组,直接粘进 exploit 脚本 gef➤ pf --lang c --bitlen 64 -l 4 0x404000 # 纯 hex 串,便于对比/校验 gef➤ pf --lang hex -l 16 $rax4. 源码级实现剖析
4.1 命令主体:PrintFormatCommand
命令主体位于 gef.py#L5599-L5701,关键实现点:
- 参数解析:由 GEF 的
@parse_arguments装饰器完成,location的位置参数默认$pc,--length默认 256,--bitlen默认 0 再在函数体内替换为ptrsize * 2(gef.py#L5635-L5650); - 校验:
bitlen不在{8,16,32,64}报Size of bit must be in: ...,lang不在白名单报Language must be in: ...(gef.py#L5652-L5659); - 读取区间:
end_addr = start_addr + length * size,其中size = bitlen / 8,注意--length的单位是"元素数",实际读到的字节数是它的 8/16/32/64 倍。
4.2 bytearray 模式与 $_gefN 便利变量
bytearray路径调用gef.memory.read拿到原始字节后交给gef_convenience()(gef.py#L4593-L4605):该函数按会话计数生成$_gef0、$_gef1…… 递增命名的 GDB 便利变量,把字节序列展开为{0xcb, 0xe3, ...}数组字面量执行set $_gefN = {...},并返回变量名。因此每个调试会话内变量名是递增不复用的,示例中的$_gef0在多次调用后会依次变为$_gef1等。
预览长度由命令自身配置项max_size_preview控制(默认 10,gef.py#L5620-L5623),可通过 GEF 的 config 命令 调整,例如把print-format.max_size_preview设得更大以看到更长的预览。
4.3 --clip 剪贴板机制
--clip最终调用copy_to_clipboard()(gef.py#L3984-L4000)。从源码看:
- Linux:通过
shutil.which查找xclip,执行xclip -selection clipboard -i; - macOS:使用
pbcopy; - 其他平台:抛出
NotImplementedError("copy: Unsupported OS")。
也就是说剪贴板功能有明确的外部依赖(X 环境下的xclip)与平台限制,在无图形环境时会收到 "There's a problem while copying" 的警告而非硬失败。
5. 测试用例验证
tests/commands/print_format.py 对该命令做了端到端覆盖,可作为行为基线参考:
- 无调试会话时执行返回 inactive session 错误(L12-L18);
print-format $sp输出以buf = [开头;--lang js输出以var buf = [开头(L22-L26);- 写入
p32(0x41414141)后--lang hex输出包含41414141(L28-L30); - 非法语言名触发
Language must be in:错误(L32-L33); --lang bytearray -l 4输出Saved data b'AAAA'... in '$_gefN',且随后p $_gefN可得到{0x41, 0x41, 0x41, 0x41}(L41-L50)。
6. 适用前提与限制
结合源码与文档,使用该命令时需注意:
- 必须有活跃调试会话(
only_if_gdb_running),LOCATION默认$pc,省略位置参数即从当前指令指针开始导出; --length是元素个数,实际读取字节数 = length × bitlen/8,导出大区间时留意是否越出目标页导致MemoryError;- 剪贴板仅限 Linux(xclip)与 macOS(pbcopy),且 Linux 侧要求
xclip已在 PATH 中; bytearray变量按会话递增编号,配合patch byte时请使用输出中实际给出的$_gefN名称;- 按 16/32/64 位解码时值按目标机器端序解释,跨端序机器直接比较字面量前需先确认
gef.arch.endianness。
参考文件索引:print-format 官方命令文档、PrintFormatCommand 实现、gef_convenience 实现、copy_to_clipboard 实现、patch 命令文档、命令测试、命令配置说明。
- 网络安全
- 开发工具
【免费下载链接】gef
GEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs & reverse engineers on Linux
相关推荐
x64dbg DataTbyte 命令详解:将内存标记为 Tbyte(10 字节)数据类型
x64dbg DataTbyte 命令详解:将内存标记为 Tbyte(10 字节)数据类型 导读 DataTbyte 是 x64dbg 内置的数据库/类型标记命
逆向工程调试器开发工具应用安全x64dbg DataAscii/da 命令详解:将内存区域标记为 ASCII 字符串数据
x64dbg DataAscii/da 命令详解:将内存区域标记为 ASCII 字符串数据 DataAscii(别名 da )是 x64dbg 命令集中用于数据
逆向工程调试器开发工具应用安全Arthas dump 命令详解:将 JVM 运行时 class 字节码批量导出到本地目录
Arthas dump 命令详解:将 JVM 运行时 class 字节码批量导出到本地目录 dump 是 Alibaba Arthas 提供的类级诊断命令,用于
开发工具可观测性调试器性能剖析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考