wowemulation-dev/binanana
GitHub: wowemulation-dev/binanana
面向魔兽世界经典版客户端的 Ghidra 自动化分析工具与符号数据库,支持跨版本符号传播和 Arxan 保护绕过。
Stars: 4 | Forks: 0
# binanana
WoW Classic 客户端二进制文件的符号数据库和分析工具。
基于 [binana](https://github.com/thunderbrewhq/binana) 项目
(涵盖 WoW 3.3.5a),binanana 将该方法扩展到了 Classic
客户端家族:Classic (1.13.x)、Classic Era (1.14.x, 1.15.x)、TBC
Classic (2.5.x) 和 Wrath Classic (3.4.x)。
所有 Classic 客户端均为 64 位 x86-64 Windows PE 二进制文件,具有以下特征:
- 混淆的导入表(所有 API 调用均在运行时解析)
- Control Flow Guard (CFG)
- TLS 回调
- MSVC RTTI,包含 2,000-3,400 个 type_info 条目
- 700-900 个嵌入式源文件路径
- TACT/CASC 内容分发(静态链接)
## 目录
- [概述](#overview)
- [依赖项](#dependencies)
- [项目结构](#project-structure)
- [符号文件](#symbol-files)
- [头文件](#header-files)
- [Ghidra](#ghidra)
- [设置](#setup)
- [无头分析](#headless-analysis)
- [GUI 模式](#gui-mode)
- [脚本兼容性](#script-compatibility)
- [导入符号](#importing-symbols)
- [导入 C 头文件](#importing-c-headers)
- [Ghidra 服务器](#ghidra-server)
- [Ghidra 扩展](#ghidra-extension)
- [Binary Ninja](#binary-ninja)
- [跨版本传播](#cross-version-propagation)
- [可用配置文件](#available-profiles)
## 概述
该项目分为三层:
1. **Profiles** —— 每个构建的符号文件(`.sym`)、C 头文件和元数据(`info.json`)。这是受版本控制的知识库。
2. **Ghidra 脚本** —— 用于自动化分析的 Python 脚本:RTTI 链遍历、Lua API 字符串解析、符号导出/导入、跨版本函数匹配。
3. **工具** —— 用于编译符号、验证配置文件以及从内存中转储 Arxan 保护的二进制文件的 CLI 实用程序。
## 依赖项
- Python 3.13(PyGhidra 需要 JPype1,该库没有 3.14+ 的 wheels)
- Ghidra >= 12.0(用于 PyGhidra CPython 3 脚本支持)
- JDK 21+(由 setup-ghidra 安装)
- Gradle 8.5+(用于构建 Ghidra 扩展)
- Make
- Bash shell
对于 Binary Ninja 工作流:
- Binary Ninja 及 binary_ninja_mcp 插件
对于内存转储器(`tools/wow-dumper`):
- Rust 1.92+ 及 `x86_64-pc-windows-msvc` 目标
- cargo-xwin(从 Linux 交叉编译)
- Wine 9+(推荐 wine-staging)
## 项目结构
```
binanana/
profile/
{version}-{platform}-{arch}/
info.json # Binary metadata
symbol/
{category}/
func.sym # Function symbols
label.sym # Data label symbols
main.sym # Compiled (all symbols merged)
include/
main.h # Master header
{subsystem}/*.h # Per-subsystem C headers
ghidra/
analyze_rtti.py # Batch RTTI chain walker
analyze_vtables.py # Heuristic vtable scanner
analyze_lua_api.py # Lua API Usage: string resolver
analyze_lua_tables.py # luaL_Reg registration table scanner
analyze_lea_refs.py # LEA instruction reference scanner
analyze_strings.py # Source path and debug string extractor
export_symbols.py # Export named symbols from Ghidra
import_symbols.py # Import symbols into Ghidra
propagate_symbols.py # Cross-version function hash matching
tools/
compile_symbols.py # Merge category .sym files into main.sym
validate_profile.py # Check symbol integrity
wow-dumper/ # Rust tool: dump Arxan-protected binaries
extension/
build.gradle # Gradle build for Ghidra extension
settings.gradle # Project name (WowEmulation)
extension.properties # Extension metadata
Module.manifest # Empty (no special module requirements)
src/main/java/wowemulation/
WowBinaryAnalyzer.java # WoW binary detection (auto-analysis)
script/
analyze # Run Ghidra headless analysis pipeline
build-extension # Build the Ghidra extension zip
compile-symbols # Shell wrapper for symbol compilation
dump-client # Dump Arxan-protected binary via Wine
export-from-binja # Export symbols from Binary Ninja
install-extension # Build and install extension to Ghidra
setup-ghidra # Install Ghidra + PyGhidra + ghidra-mcp
Makefile
```
## 符号文件
符号文件将地址映射到函数和数据标签。该格式与 Ghidra 的 `ImportSymbolsScript.py` 兼容:
```
FunctionName 00000001400AD020 f end=00000001400AD0A3
DataLabel 0000000142B60E20 l
FunctionName 00000001400B1470 f end=00000001400B1590 type="int64_t __fastcall func(void*)"
SomeFunc 00000001400C0000 f ; demangled: SomeNamespace::SomeFunc(int, char const*)
```
字段:
- **Name**:符号名称(无空格)
- **Address**:16 位十六进制地址(64 位)
- **Kind**:`f` 表示函数,`l` 表示数据标签
- **end=ADDR**:结束地址(最后一条指令之后的一个地址)
- **type="..."**:C 类型签名
- **; comment**:人类可读的注释(例如,还原后的名称)
符号按类别组织在 `symbol/{category}/func.sym` 和 `symbol/{category}/label.sym` 中。`script/compile-symbols` 脚本将所有类别文件合并到 `symbol/main.sym` 中。
## 头文件
C 头文件描述了与二进制文件内存表示相匹配的结构布局。它们使用条件编译进行特定于工具的处理:
```
#ifdef GHIDRA
// Ghidra-specific includes
#endif
```
## Ghidra
### 设置
安装 Ghidra 和 PyGhidra(Fedora):
```
# 完整安装 (Ghidra GUI + PyGhidra + ghidra-mcp)
make setup-ghidra
# 仅 Headless (Ghidra + PyGhidra,无 GUI 插件)
make setup-ghidra-headless
```
此操作将安装:
- Ghidra 12.0.3 到 `/opt/ghidra`
- JDK 25
- Python 3.13(PyGhidra 需要 JPype1;不支持 3.14+)
- PyGhidra 3.0.2 + JPype1(来自 Ghidra 捆绑的 wheels)
- ghidra-mcp 2.0.2(仅限完整模式)
安装后,加载环境:
```
source /etc/profile.d/ghidra.sh
```
### 无头分析
Ghidra 有两个无头启动器:
| 启动器 | 脚本 | 用例 |
|----------|---------|----------|
| `analyzeHeadless` | 仅限 Java 和 Jython (Python 2.7) | 旧版脚本 |
| PyGhidra headless | CPython 3.13 | binanana 脚本 |
binanana 脚本使用 Python 3,必须通过 PyGhidra 运行。
运行完整的分析流水线:
```
./script/analyze
```
示例:
```
./script/analyze ~/Downloads/wow_classic/Wow.exe \
profile/classic-1.13.2-31650-windows-win64
```
该脚本会创建一个临时的 Ghidra 项目目录,退出时会自动清理。
这将执行七个后处理脚本:
1. `analyze_rtti.py` —— 遍历 RTTI type_info -> COL -> vtable 链
2. `analyze_vtables.py` —— 用于数据段的启发式 vtable 扫描器
3. `analyze_lua_api.py` —— 将 Lua API Usage: 字符串解析为原生函数
4. `analyze_lua_tables.py` —— 查找 luaL_Reg 注册表
5. `analyze_lea_refs.py` —— 扫描 LEA 指令以查找字符串引用
6. `analyze_strings.py` —— 提取源路径和调试字符串
7. `export_symbols.py` —— 将所有命名符号导出为 .sym 格式
运行单个脚本:
```
python3.13 -m pyghidra.ghidra_launch \
--install-dir /opt/ghidra \
ghidra.app.util.headless.AnalyzeHeadless \
/tmp/ghidra_project project_name \
-import /path/to/binary \
-postScript ghidra/analyze_rtti.py "/path/to/output.txt" \
-overwrite -deleteProject
```
脚本参数在脚本路径后传递。每个脚本接受一个可选的输出文件路径作为其第一个参数。如果没有参数,结果仅打印到 stdout。
### GUI 模式
启动 Ghidra:
```
ghidraRun
```
binanana 脚本位于 `ghidra/` 目录中。使用方法:
1. Window -> Script Manager
2. Script Directories -> Add: `/ghidra/`
3. 按 "binanana" 类别筛选
4. 运行任意脚本(它们会在 GUI 模式下提示输入文件路径)
所有脚本均可在 GUI 和无头模式下工作。在 GUI 模式下,它们使用 `askFile()` 提示;在无头模式下,它们接受 `getScriptArgs()`。
### 脚本兼容性
Ghidra 脚本使用 PyGhidra 兼容的导入:
```
# 正确 (在 PyGhidra 和 Jython 中可用):
from ghidra.program.model.symbol import SourceType
# 在 PyGhidra 中失效 (JPype enum wildcard import 问题):
from ghidra.program.model.symbol.SourceType import *
```
JPype 不支持从 Java 枚举类型进行通配符导入。请使用直接导入(`import SourceType`)并将常量限定为 `SourceType.DEFAULT`、`SourceType.ANALYSIS` 等。
### 导入符号
1. 打开 Ghidra -> Window -> Script Manager
2. 从 binanana 类别运行 `import_symbols.py`
3. 选择 `profile//symbol/main.sym`
或在无头模式下:
```
python3.13 -m pyghidra.ghidra_launch \
--install-dir /opt/ghidra \
ghidra.app.util.headless.AnalyzeHeadless \
/tmp/project project_name \
-process binary.exe \
-postScript ghidra/import_symbols.py "profile/version/symbol/main.sym" \
-noanalysis
```
### 导入 C 头文件
1. 打开 Ghidra -> File -> Parse C Source...
2. 选择 `clib.prf` 作为解析配置
3. 将 `profile//include/main.h` 添加到源文件
4. 将 `profile//include` 添加到包含路径
5. 将 `-DGHIDRA` 添加到解析选项
6. 按 Parse to Program
### Ghidra 服务器
Ghidra 归档文件包含一个服务器(`/opt/ghidra/server/`),用于协作式多用户仓库共享。binanana 的无头分析工作流不需要此服务。如果多名分析师需要共享 Ghidra 项目数据库,它会很有用。
有关服务器设置,请参阅 `/opt/ghidra/server/svrREADME.md`。
## Ghidra 扩展
`extension/` 目录包含一个可安装的 Ghidra 扩展,它将 Java 二进制检测器和 Python 分析脚本捆绑到一个包中。
### 提供的功能
安装后,该扩展将添加:
1. **WoW Binary Detector** —— 一个在 Ghidra 自动分析期间运行的 Java 分析器。它扫描 `.rdata` 以查找 `CObject` RTTI 签名,从而识别 WoW 二进制文件,并使用 RTTI 条目计数设置程序属性。
2. **Script Manager 集成** —— 来自 `ghidra/` 的所有 Python 脚本会自动出现在 Script Manager 中,无需手动配置目录。
Java 代码仅限于二进制检测(约 100 行)。所有分析逻辑(RTTI 链遍历、Lua API 解析、符号管理)均保留在 Python 中。
### 构建和安装
要求:Gradle 8.5+、JDK 21+、设置 `GHIDRA_INSTALL_DIR` 环境变量。
```
# 构建 extension zip
make build-extension
# 构建并安装到系统 Ghidra 安装目录
make install-extension
```
构建好的 zip 文件位于 `extension/dist/`。也可以通过 Ghidra 的 `File -> Install Extensions` 对话框(绿色 + 按钮)进行安装。
### 架构说明
Ghidra 的自动分析扩展点(`AbstractAnalyzer`、`AbstractProgramWrapperLoader`、`ProgramPlugin`)需要 Java。Ghidra 的 `ClassSearcher` 扫描 classpath 上的已编译 `.class` 文件;通过 JPype 创建的 Python 类对其不可见。
这就是二进制检测使用 Java 而分析逻辑保留在 Python 中的原因。
## Binary Ninja
对于通过 binary_ninja_mcp 插件加载到 Binary Ninja 中的二进制文件:
```
# 从 BN 导出用户命名符号为 binanana 格式
./script/export-from-binja profile/classic-1.13.2-31650-windows-win64
# 或通过 Make
make export-from-binja PROFILE=profile/classic-1.13.2-31650-windows-win64
```
这将连接到位于 `localhost:9009` 的 BN HTTP API,导出用户命名的函数和数据标签,并写入 `symbol/export/func.sym` 和 `symbol/export/label.sym`。
## 跨版本传播
`propagate_symbols.py` 脚本使用指令级哈希匹配跨二进制版本的函数。工作流程:
1. 彻底分析一个版本(例如 1.13.2)
2. 在 Ghidra 中打开分析后的二进制文件,运行 `propagate_symbols.py` 并使用模式 "export" 以保存函数哈希
3. 打开目标二进制文件,运行 `propagate_symbols.py` 并使用模式 "import" 以及第 2 步中的哈希文件
4. 检查匹配的符号并提交到目标配置文件
在无头模式下:
```
# 从源二进制文件导出 hashes
python3.13 -m pyghidra.ghidra_launch --install-dir /opt/ghidra \
ghidra.app.util.headless.AnalyzeHeadless /tmp/project src \
-process Wow_1.13.2.exe -noanalysis \
-postScript ghidra/propagate_symbols.py "export" "/tmp/hashes.txt"
# 导入并匹配目标二进制文件
python3.13 -m pyghidra.ghidra_launch --install-dir /opt/ghidra \
ghidra.app.util.headless.AnalyzeHeadless /tmp/project tgt \
-process Wow_1.14.0.exe -noanalysis \
-postScript ghidra/propagate_symbols.py "import" "/tmp/hashes.txt"
```
## 可用配置文件
| 版本 | 构建 | 产品 | 平台 | 二进制文件 |
|---------|-------|---------|----------|--------|
| 3.13.3 | 9370 | Agent | windows-i386 | Agent.exe |
| 1.13.2 | 31650 | Classic | windows-win64 | Wow.exe |
| 1.14.0 | 40618 | Classic Era | windows-win64 | WowClassic.exe |
| 1.14.1 | 41794 | Classic Era | windows-win64 | WowClassic.exe |
| 1.14.2 | 42597 | Classic Era | windows-win64 | WowClassic.exe |
| 1.15.2 | 55140 | Classic Era | windows-win64 | WowClassic.exe |
| 1.15.2 | 55140 | Classic Era | macos-arm64 | World of Warcraft Classic |
| 1.15.8 | 64272 | Classic Era | windows-win64 | WowClassic.exe |
| 2.5.3 | 42328 | TBC Classic | windows-win64 | WowClassic.exe |
| 3.4.3 | 53788 | Wrath Classic | windows-win64 | WowClassic.exe |
标签:Arxan, Binary Ninja, CASC, CFG, Ghidra, JS文件枚举, Lua API, Python, RTTI, Ruby on Rails, TACT, UML, Windows PE, World of Warcraft, WoW, x86-64, 二进制分析, 云安全监控, 云安全运维, 云资产清单, 内存转储, 反混淆, 可视化界面, 后台面板检测, 应用安全, 怀旧服, 控制流保护, 无后门, 游戏安全, 符号表, 类型库, 脚本开发, 逆向工具, 逆向工程, 静态分析