当前位置: 首页 > news >正文

用Pwndbg恢复二进制文件调试信息:Binary Ninja集成与Go调试增强

用Pwndbg恢复二进制文件调试信息

当调试缺乏调试符号的"剥离二进制文件"时,GDB会丧失大量功能:函数和变量名变成无意义的地址,设置断点需要从外部查找函数地址,查看结构化值需要手动解析内存转储。为此,我在Trail of Bits实习期间扩展了Pwndbg——这是由我的导师Dominik Czarnota维护的GDB插件,新增两项功能使剥离二进制文件的调试体验接近IDE调试器。

Binary Ninja集成

通过在Binary Ninja内部安装XML-RPC服务器并从Pwndbg查询,我实现了Pwndbg与这款流行反编译器的集成。这使得Pwndbg可以访问Binary Ninja的分析数据库,用于同步符号、函数签名、栈变量偏移等,恢复大部分调试体验。

对于反编译功能,我直接从Binary Ninja提取语法标记而非序列化为文本,实现完全语法高亮的反编译,可配置使用Binary Ninja的3种中间语言级别。反编译结果直接显示在Pwndbg上下文中,当前行高亮显示,与汇编视图一致。

我还实现了在Binary Ninja中显示程序计数器(PC)寄存器箭头的功能,以及从Binary Ninja内部设置断点的功能,减少在两者间切换的频率。

最复杂的集成组件是栈变量名同步。Pwndbg中任何出现栈地址的地方(如寄存器视图、栈视图或函数参数预览),集成功能都会检查是否为Binary Ninja中的命名栈变量,若是则显示正确标签。该功能还会检查父栈帧,确保调用者的变量也能正确标记。

实现该功能的主要困难在于Binary Ninja仅提供相对于栈帧基址的偏移量,因此需要推导帧基址才能计算绝对地址。虽然x86等架构有帧指针寄存器,但编译器可能将其作为普通寄存器使用。幸运的是,Binary Ninja具有常量值传播功能,可以判断寄存器是否是帧基址的可预测偏移量。

Go调试增强

调试非C语言(有时甚至是C)编译的可执行文件时,复杂的内存布局使得值转储变得困难。例如转储Go切片需要一个命令转储指针和长度,另一个命令检查切片内容;而转储map对小map需要十余个命令,大map则需要数百个。

为此我创建了go-dump命令。参考Go编译器源码,我实现了所有Go内置类型的转储,包括整数、字符串、复数、指针、切片、数组和map。内置类型采用Go原生表示法,无需学习新语法。

该命令还能解析和转储任意嵌套类型,使得任何类型都只需一个命令即可完成转储。

解析Go运行时类型

虽然Go专用转储比手动内存转储更方便,但仍存在可用性问题:需要知道待转储值的完整类型,这在处理多字段或嵌套结构体时尤其困难。此外,结构体字段名和用户定义类型名等编译无关信息也无法获知。

Go编译器会为程序中使用的每个类型生成运行时类型对象(供reflect包使用),包含任意嵌套结构的布局、类型名、大小和对齐等信息。这些类型对象还可与对应值匹配:接口值存储类型指针和数据指针,堆分配值在分配函数(通常是runtime.newobject)中传入类型对象。

我编写了能递归提取这些信息的解析器,通过go-type命令显示给定地址的运行时类型信息。对于结构体,包括每个字段的类型、名称和偏移量。

这可通过两种方式转储值。第一种方式仅适用于接口值,因其直接存储类型指针和数据指针,便于自动检索。使用Go的any类型转储空接口(无方法的接口),用interface类型转储非空接口。转储时命令会自动检索和解析类型,无需输入类型信息。

第二种方式适用于所有值,但需要指定类型指针。虽然查找类型指针可能涉及猜测,但仍比手动推导类型布局简单,能转储最复杂的类型。我在Go编译器(最大最复杂的开源Go代码库之一)的剥离构建上测试了几个大型结构体类型,都能成功转储。

展望未来

这个夏天,我增强了Pwndbg与Binary Ninja的集成以获取丰富调试信息,并添加了go-dump命令转储Go值。所有功能已在Pwndbg开发分支和最新版本(2024.08.29)中提供。

未来还有更多改进空间:Binary Ninja集成采用模块化设计,便于未来支持更多反编译器;Go调试可增强对goroutine的支持,目前这是Delve调试器(专用于Go的调试器)相对于GDB/Pwndbg的主要优势。

致谢

感谢Trail of Bits提供这个绝佳的实习机会,特别感谢我的导师Dominik Czarnota对代码审查和反馈的快速响应,以及Pwndbg社区在开发过程中解答我的各种问题。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

http://www.sczhlp.com/news/2221/

相关文章:

  • 无向图联通分量总结
  • Redis命令:列表模糊删除详解
  • 工控机使用SysVinit 启动脚本
  • 【汽车科普】汽车点火开关的四个档位:LOCK、ACC、ON(IGN)、START
  • 代码管理平台选择指南
  • LED灯闪烁
  • 31、制作菜单
  • 博客园书写格式示例
  • Windows nodejs多版本安装
  • springboot当中ConfigurationProperties注解作用跟数据库存入有啥区别
  • mysql导出表字段
  • 图像生成-Conditional Flow Matching-12 - jack
  • 从技术到合规:璞华科技与传神语联合作完成大模型数据资产入表,「璞华易表」赋能 AI 资产化实践
  • SQL Server 中 CROSS APPLY 使用教程
  • 联想拯救者电脑睡眠模式关闭呼吸灯
  • centos系统清理docker日志文件
  • go 语言特性
  • electron-egg实现全量更新和增量更新(下)
  • 【刷题笔记】P2824 [HEOI2016/TJOI2016] 排序
  • BT134-600-ASEMI双向可控硅BT134-600
  • JUC学习-22-源码解读(线程池如何创建线程)
  • 以dotnet为例,创建软路由
  • MyEMS开源能源管理系统核心代码解读026
  • 面试官说:在区块链交易所的高并发环境下,不依靠数据库事务保持一致性,对大的事务进行拆分,比如用对账系统保证一致性,最终数据库仅仅是持久化的功能。如何理解这种思想 - Charlie
  • 范畴论基础概念和 Yoneda Lemma 定理
  • API分享:利用API接口实现批量获取淘宝商品详情的主图视频
  • 点亮LED灯
  • 【汽车电子】一个系统
  • AtCoder Beginner Contest 416 - F - Paint Tree 2 题解
  • 认识Arduino 电路基础知识