BinFlip/undelphi
GitHub: BinFlip/undelphi
用于 Delphi / C++Builder / Free Pascal 编译二进制的 Rust 静态分析库,无需运行即可提取 RTTI、类层次、DFM 表单及嵌入资源等完整元数据。
Stars: 2 | Forks: 0
# undelphi
[](https://crates.io/crates/undelphi)
[](LICENSE)
用于已编译的 **Delphi**、**C++Builder** 和 **Free Pascal / Lazarus** 二进制文件的 Rust 静态分析库。它能够识别工具链,恢复完整的类层次结构和每个类的 RTTI,解析嵌入的表单流(form streams),并提取出运行时元数据可达的所有 blob 和交叉引用 —— 而无需执行二进制文件。
```
use undelphi::DelphiBinary;
let bytes = std::fs::read("my_app.exe")?;
if let Ok(bin) = DelphiBinary::parse(&bytes) {
if let Some(info) = bin.compiler() {
println!("{:?} {} on {:?}/{:?}", info.compiler, info.raw, info.os, info.arch);
}
println!("{} classes, {} forms", bin.classes().len(), bin.forms().len());
}
```
## 提取内容
### 工具链识别
| 信号 | 置信度 | 恢复信息 |
| --- | --- | --- |
| Embarcadero 构建字符串 (`Embarcadero Delphi for Win64 compiler version 36.0 …`) | 高 | 精确的编译器版本 → 市场发布名称(例如 *Delphi 12 Athens*)、目标操作系统、目标架构 |
| FPC 构建字符串 (`FPC 3.2.2 [2021/05/15] for i386 - Win32`) | 高 | FPC 版本、目标操作系统、目标架构 |
| `DVCLAL` 资源 | 高 | SKU(个人版 / 专业版 / 企业版) |
| `PACKAGEINFO` 资源 | 高 | unit 列表 + 所需 package 列表 |
| `SOFTWARE\Borland\Delphi\RTL` 注册表路径 | 中 | XE2 之前的 Delphi(仅限年代) |
| 带命名空间的 unit 名称 (`Vcl.Controls`, `System.SysUtils`, …) | 中 | XE2 或更高版本的 Delphi(仅限年代) |
| `TPF0` 魔数(magic-byte)计数 | 中 | 非零 ⇒ 二进制文件包含表单(form-bearing) |
| 验证后的 VMT 扫描 | 中 | 非空 ⇒ 在结构上属于 Delphi/FPC(即使所有字符串都被剥离) |
### 类发现
- 适用于 32 位和 64 位、跨 Windows PE / macOS Mach-O / Linux ELF 的 Delphi 和 FPC 布局的 **VMT 形状测试扫描器**。
- 带有 FPC `PClass` 风格间接回退的 **父类链解析**。检测外部父类(`vmtParent` 指向依赖 package 的 VMT),以便 BPL 运行时 package 能够清晰地报告为 `roots=0, external=N`,而不是出现大量孤立类。
- 按类名和 VMT 虚拟地址进行 **索引查找**。
- 类树渲染、祖先遍历(防止循环)、子类枚举、最大深度、根/孤儿/外部计数。
### 每个类的元数据
对于每个发现的类:
- **Published 方法** —— 名称 + 代码 VA,以及现代 Delphi 的扩展尾部。
- **Published 字段** —— 名称、相对于实例的偏移量、字段类型对应的已解析 `PPTypeInfo`。支持 Delphi 遗留字段表和现代字段表布局,以及 FPC 的 `TVmtFieldTable`。
- **Published 属性** —— 完整的 `TPropInfo`:PropType 指针、get/set/stored 访问器(Delphi field/virtual/static 或 FPC `ptField`/`ptStatic`/`ptVirtual`/`ptConst`)、Index、Default、NameIndex。
- **实现的接口** —— IID GUID(规范格式化)、vtable VA、实例偏移量、getter VA。FPC Corba 接口额外提取 `IIDStr` 名称。
- **虚方法指针表** —— 每个槽位一个条目,自动以下一个类的 VMT 基址为边界。
- **Init(受托管字段)表** —— 运行时进行引用计数管理(字符串、接口、动态数组、托管记录)的每个偏移量。
- **动态分发表** —— `dynamic` 和 `message` 槽位及其 ID 和处理程序 VA。
### 各 Kind 的 RTTI 解码器
对我们实际遇到的每一种类型 Kind 进行完整的结构化解码:
`tkClass` · `tkEnumeration` · `tkInteger` · `tkChar` · `tkWChar` ·
`tkFloat` · `tkSet` · `tkClassRef` · `tkDynArray` · `tkInterface` ·
`tkRecord` · `tkMethod` · `tkProcedure` · `tkLString` · `tkUString` ·
`tkWString`。
`decode_type_detail` 分发器遵循属性或字段的 `PPTypeInfo` 间接引用,并返回一个 `TypeDetail` enum,以便调用方可以直接匹配 Kind,而无需预先分发。Delphi 和 FPC 的字节值均受支持(FPC 重新排列了 `TTypeKind` enum —— `tkClass` 在 Delphi 中为 `7`,在 FPC 中为 `15`;两者均已针对 `reference/` 进行了验证)。
### 扩展 RTTI (Delphi 2010+)
- 针对超越经典仅 published 集合的属性,提供每个成员的 **可见性** 标志(private / protected / public / published)。
- **属性表** 解码器,支持提取构造函数参数(字符串 / 整数 / 原始字节)。
### 表单流
完整的 **TPF0** 和 **TPF1** 二进制表单流解析器。遍历的来源:
1. PE `RT_RCDATA` 资源(Delphi / C++Builder / FPC Windows)。
2. FPC 内部资源树(`fpc.resources` / `.fpc.resources` —— Mach-O、ELF 以及未定义 `FPC_HAS_WINLIKERESOURCES` 的 FPC PE 构建版本)。
递归组件树,每个已文档化的 `TValueType`(Null、List、Int8/16/32/64、QWord、Single、Double、Currency、Date、Extended、String、Ident、LString、WString、UString、Utf8String、Binary、Set、Collection、Nil、True、False),filer 标志(`ffInherited`、`ffChildPos`、`ffInline`),以及处理起来棘手的带有 `vaList` 包装器的 Collection 语法(已针对 FPC `writer.inc` 进行了验证,并在回归测试中进行了复现)。
符号化的 `render` 模块根据声明的 RTTI 类型对原始值进行美化打印(例如输出 `Align = alClient` 而不是 `Align = 2`,输出 `BorderIcons = [biSystemMenu, biMaximize]` 而不是 `BorderIcons = 7`)。
### 嵌入 blob 提取
`bin.blobs()` 遍历每个已解析的表单,提取每个 `vaBinary` 叶子节点,并根据魔数对其进行分类:
ICO · CUR · BMP · PNG · JPEG · GIF · WebP · TIFF · ZIP · GZIP · 7Z · RAR
· PDF · PE · ELF · Mach-O · WAV · MP3 · WMF/EMF · RIFF · ShortString。
每个 `EmbeddedBlob` 都携带其原始表单资源、点分组件路径、属性名称、前导字节分类以及原始字节切片(从输入中借用)。这对于恶意软件分类非常有用 —— 它可以直接提供一份现成的清单,列出二进制表单流中包含的所有被释放的图标、嵌入的脚本、打包的 payload 或隐藏的 PE 文件。
```
use undelphi::DelphiBinary;
let bin = DelphiBinary::parse(&bytes).unwrap();
for blob in bin.blobs() {
println!("{:>6} bytes {:?} {}.{}",
blob.data.len(), blob.kind, blob.path, blob.property_name());
}
```
### 交叉引用
构建在已经提取的元数据之上,无需额外扫描二进制文件:
- **接口实现者** —— `BTreeMap`。
- **DFM 类实例化** —— `BTreeMap`。
- **事件处理程序绑定** —— 每个表单中的每个 `OnClick` / `OnCreate` 等,并在处理程序可以链接到某个 published 方法时提供解析后的代码 VA(遍历该类的祖先链)。
- **每个 unit 的汇总统计** —— 类计数、字段 / 方法 / 属性 / 接口总数、每个声明 unit 的总实例字节数。
- **外部父类报告** —— 继承自依赖 package 导出 VMT 的本地声明类列表。
### 实例内存布局
针对给定类逐字节重建实例 —— 将 published 字段表、init-table 受托管字段偏移量和声明的 `InstanceSize` 进行融合。标记命名字段、间隙(private/非 published 成员或填充)以及仅受托管条目(类保持为 private 的引用计数字段)。
## 支持的目标
| 工具链 | 版本 | 容器 |
| --- | --- | --- |
| Embarcadero / Borland Delphi | Delphi 2 → Delphi 12 Athens (`dcc` v9 → v36) | PE32 / PE32+, Mach-O, ELF |
| Embarcadero C++Builder | 解析方式与 Delphi 相同(使用相同的 VCL/FMX RTTI) | PE32 / PE32+ |
| Free Pascal / Lazarus | FPC 1.0 → FPC 3.2+(包含处于 trunk 阶段的 3.3 版本) | PE32 / PE32+, Mach-O, ELF |
| 架构 | x86, x86_64, ARM, AArch64 | (根据构建字符串和/或容器头文件判定) |
## 性能
在一个 25 MB 的 Delphi 12 Athens PE(`heidisql.exe` —— 包含 2,661 个类,42 个表单资源,以及数十万次 VA 解引用)上,在 release 模式下,于现代笔记本电脑上执行完整的解析 + `forms()` + 所有 `xref::*` 视图大约仅需 ~100 毫秒。
## 示例
- [`examples/dump.rs`](examples/dump.rs) — 针对单个二进制文件的详尽元数据转储器。打印出该 crate 知道如何提取的每一项信息:
cargo run --release --example dump -- path/to/binary.exe
基于 [Apache License, Version 2.0](LICENSE) 授权。
标签:Delphi, Rust, 云安全监控, 云资产清单, 元数据提取, 可视化界面, 网络流量审计, 进程保护, 逆向工程, 通知系统, 静态分析