jac3km4/zoltan
GitHub: jac3km4/zoltan
Zoltan是一款逆向工程辅助工具,通过结合C/C++源码中的字节模式定义与可执行文件,即时生成DWARF调试符号和头文件,帮助研究者在IDA等工具中获得函数和类型信息。
Stars: 17 | Forks: 3
# zoltan
本项目的目标是便于在逆向工程中即时生成调试符号和头文件。
它可以通过结合带有模式注释的 C/C++ 源代码和可执行二进制文件来生成调试符号。
## 用法
第一步是定义一个带有字节模式(类似于 IDA)的 C/C++ 函数 typedef,你可以在[这里](#patterns)阅读更多相关信息。
包含你定义的 typedef 的源文件将被解析,你所引用的任何类型(struct、enum 等)都可以通过使用 `--dwarf-output ` [CLI 选项](#cli)与你的函数一起存储在调试文件中。
当 Zoltan 运行时,它会在你提供的可执行文件中搜索你的模式,然后使用解析出的函数地址来生成与你的可执行文件兼容的调试符号。
底层的模式搜索使用了[非常快的 SIMD 加速多字符串搜索算法](https://github.com/BurntSushi/aho-corasick),因此通常能非常快地完成。
一旦你准备好了头文件,就可以像这样通过命令行调用 Zoltan:
```
zoltan-clang.exe '.\types.hpp' 'C:\Games\ELEX2\system\ELEX2.exe' -f 'std=c++20' --dwarf-output '.\dbg-symbols'
```
此命令会将调试符号写入名为 `dbg-symbols` 的文件中。Zoltan 使用 DWARF 格式对其进行编码。生成的符号文件可以加载到诸如 IDA 之类的逆向工具中(例如 Edit->Plugins->Load DWARF file)。
完成此操作后,你应该能够在反编译代码或指令列表中看到所有的函数和数据类型。
上面的示例使用了 clang 前端,你可以[在这里](#frontends)了解其他前端。
## cli
```
Zoltan Clang frontend for C/C++
Usage: [-o DWARF] [--c-output C] [--rust-output RUST] [[--strip-namespaces]] [[--eager-type-export]] -f FLAGS...
Available options:
-o, --dwarf-output DWARF file to write
--c-output C header with offsets to write
--rust-output Rust file with offsets to write
--strip-namespaces Strip namespaces from type names
--eager-type-export Export all types found in the sources
-f, --compiler-flag Flags to pass to the compiler
-h, --help Prints help information
```
## 模式
模式需要写在紧跟在函数 typedef 之前、以三个 '`/`' 为前缀的注释中。
Zoltan 支持标准的 IDA 风格模式:
```
// defines a pattern that matches function prologue exactly
/// @pattern 48 83 EC 30 48 8B 09 41 8B F1 41 8B E8 48 8B DA 48 85 C9 74 65
typedef void give_item(struct Object** target, struct Object** item, unsigned int quantity);
// defines a pattern that matches the 6th (0-based) result out of 24
/// @pattern 57 48 83 EC 30 48 C7 44 24 20 FE FF FF FF 48 89 5C 24 48 48 8B
/// @nth 5/24
typedef void remove_item(struct Object** item);
// defines a pattern with an offset to function prologue
/// @pattern 8B 0D ? ? ? ? BA 10 00 00 00 48 8B 0C C8 8B 04 0A 39 ? ? ? ? 01 7F 16
/// @offset 13
typedef struct FunctionRegistry* get_function_registry();
```
Zoltan 通过匹配组对 IDA 风格的模式进行了扩展,类似于正则表达式。例如,你可以捕获指向另一个函数的相对偏移量。
当使用匹配组时,你需要使用 @eval 参数来告知 Zoltan 如何计算最终的偏移量。
在下面的示例中,我们返回了一个函数的已解析地址:
```
// defines a pattern that retrieves the function address from a relative CALL instruction
/// @pattern E8 (fn:rel) 45 8B 86 70 01 00 00 33 C9 BA 05 00 00 00 C7 44 24 30 02 00 00 00
/// @eval fn
typedef struct Object* get_player();
```
@eval 参数接受表达式,这赋予了你极大的灵活性,例如匹配对虚函数表的访问并从中提取特定的函数:
```
// defines a pattern that retrieves the function address from the second slot of a virtual table
/// @pattern 46 58 00 00 00 00 48 8D 05 (vft:rel) 49 89 06 41 89 6E 60 49 8B C6 4C
/// @eval *(vft + 2)
typedef char* get_name(struct Object* npc);
```
## 生成头文件
Zoltan 还可以生成包含已解析函数偏移量的头文件。你可以使用 `--c-output` 和 `--rust-output` 选项来实现。
生成的文件如下所示:
```
// This file has been generated by zoltan (https://github.com/jac3km4/zoltan)
#define GET_PLAYER_ADDR 0x40B820
#define GET_FUNCTION_REGISTRY_ADDR 0x867310
#define GIVE_ITEM_ADDR 0xB15170
```
结合你的 typedef,你可以在运行时使用它们来调用这些函数:
```
((get_player*)(IMAGE_BASE + GET_PLAYER_ADDR))()
```
## 前端
目前有两个可用的前端:
- zoltan-saltwater
- 附带一个用纯 Rust 编写的 C 编译器([saltwater](https://github.com/jac3km4/saltwater)),无外部依赖
- 速度非常快,适合原型开发
- 无法编译 C++
- zoltan-clang
- 使用 libclang,因此可用于利用现代标准的复杂 C++ 代码库
- 由于 Clang 解析器的缘故,速度可能会相对较慢
- 允许使用 C++ 的 `using` 类型别名代替 typedef
- 需要在本地设置 libclang
- 在较新版本的 Windows 上,你可以执行以下命令
winget install llvm
$env:PATH += ";C:\Program Files\LLVM\bin"
- 上面的 `PATH` 修改不是持久化的,如果你想永久更新 `PATH`,需要在控制面板中进行

标签:C/C++, Clang, DAST, DWARF格式, IDA插件, SEO词: 符号恢复, SEO词: 逆向工具, SIMD加速, UML, Wayback Machine, 事务性I/O, 二进制分析, 云安全监控, 云安全运维, 云资产清单, 代码注入分析, 内存特征码, 动态生成, 反汇编, 可视化界面, 恶意软件分析, 游戏逆向, 特征码搜索, 调试符号生成, 逆向工程, 通知系统, 静态分析