Symbol database and analysis tools for WoW Classic client binaries.
Based on the binana project (which covers WoW 3.3.5a), binanana extends the approach to the Classic client family: Classic (1.13.x), Classic Era (1.14.x, 1.15.x), TBC Classic (2.5.x), and Wrath Classic (3.4.x).
All Classic clients are 64-bit x86-64 Windows PE binaries with:
- Obfuscated import tables (all API calls resolved at runtime)
- Control Flow Guard (CFG)
- TLS callbacks
- MSVC RTTI with 2,000-3,400 type_info entries
- 700-900 embedded source file paths
- TACT/CASC content delivery (statically linked)
- Overview
- Dependencies
- Project structure
- Symbol files
- Header files
- Ghidra
- Ghidra extension
- Binary Ninja
- Cross-version propagation
- Available profiles
The project has three layers:
- Profiles -- Per-build symbol files (
.sym), C headers, and metadata (info.json). This is the version-controlled knowledge base. - Ghidra scripts -- Python scripts for automated analysis: RTTI chain walking, Lua API string resolution, symbol export/import, cross-version function matching.
- Tools -- Python CLI for compiling symbols, validating profiles, and generating tool-specific output formats.
- Python 3.13 (PyGhidra requires JPype1 which has no 3.14+ wheels)
- Ghidra >= 12.0 (for PyGhidra CPython 3 script support)
- JDK 21+ (installed by setup-ghidra)
- Gradle 8.5+ (for building the Ghidra extension)
- Make
- Bash shell
For Binary Ninja workflows:
- Binary Ninja with binary_ninja_mcp plugin
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/
export_symbols.py # Export named symbols from Ghidra
import_symbols.py # Import symbols into Ghidra
analyze_rtti.py # Batch RTTI chain walker
analyze_lua_api.py # Lua API Usage: string resolver
analyze_strings.py # Source path and debug string extractor
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
extension/
build.gradle # Gradle build for Ghidra extension
extension.properties # Extension metadata
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
install-extension # Build and install extension to Ghidra
compile-symbols # Shell wrapper for symbol compilation
export-from-binja # Export symbols from Binary Ninja
setup-ghidra # Install Ghidra + PyGhidra + ghidra-mcp
Makefile
Symbol files map addresses to functions and data labels. The format is
compatible with Ghidra's 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*)
Fields:
- Name: Symbol name (no spaces)
- Address: 16-digit hex address (64-bit)
- Kind:
ffor function,lfor data label - end=ADDR: End address (one past last instruction)
- type="...": C type signature
- ; comment: Human-readable note (e.g., demangled name)
Symbols are organized by category in symbol/{category}/func.sym and
symbol/{category}/label.sym. The script/compile-symbols script
merges all category files into symbol/main.sym.
C header files describe struct layouts matching the binary's memory representation. They use conditional compilation for tool-specific handling:
#ifdef GHIDRA
// Ghidra-specific includes
#endifInstall Ghidra and PyGhidra (Fedora):
# Full install (Ghidra GUI + PyGhidra + ghidra-mcp)
make setup-ghidra
# Headless only (Ghidra + PyGhidra, no GUI plugins)
make setup-ghidra-headlessThis installs:
- Ghidra 12.0.3 to
/opt/ghidra - JDK 25
- Python 3.13 (PyGhidra requires JPype1; 3.14+ is not supported)
- PyGhidra 3.0.2 + JPype1 (from Ghidra's bundled wheels)
- ghidra-mcp 2.0.2 (full mode only)
After installation, load the environment:
source /etc/profile.d/ghidra.shGhidra has two headless launchers:
| Launcher | Scripts | Use case |
|---|---|---|
analyzeHeadless |
Java and Jython (Python 2.7) only | Legacy scripts |
| PyGhidra headless | CPython 3.13 | binanana scripts |
The binanana scripts use Python 3 and must run through PyGhidra.
Run the full analysis pipeline:
./script/analyze <binary-path> <profile-dir>Example:
./script/analyze ~/Downloads/wow_classic/Wow.exe \
profile/classic-1.13.2-31650-windows-win64The script creates a temporary Ghidra project directory that is automatically cleaned up on exit.
This executes four post-scripts:
analyze_rtti.py-- Walk RTTI type_info -> COL -> vtable chainsanalyze_lua_api.py-- Resolve Lua API Usage: strings to native functionsanalyze_strings.py-- Extract source paths and debug stringsexport_symbols.py-- Export all named symbols to .sym format
To run individual scripts:
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 -deleteProjectScript arguments are passed after the script path. Each script accepts an optional output file path as its first argument. Without an argument, results print to stdout only.
Launch Ghidra:
ghidraRunThe binanana scripts are in the ghidra/ directory. To use them:
- Window -> Script Manager
- Script Directories -> Add:
<repo>/ghidra/ - Filter by "binanana" category
- Run any script (they prompt for file paths in GUI mode)
All scripts work in both GUI and headless modes. In GUI mode they use
askFile() prompts; in headless mode they accept getScriptArgs().
The Ghidra scripts use PyGhidra-compatible imports:
# Correct (works in PyGhidra and Jython):
from ghidra.program.model.symbol import SourceType
# Broken in PyGhidra (JPype enum wildcard import issue):
from ghidra.program.model.symbol.SourceType import *JPype does not support wildcard imports from Java enum types. Use direct
imports (import SourceType) and qualify constants as
SourceType.DEFAULT, SourceType.ANALYSIS, etc.
- Open Ghidra -> Window -> Script Manager
- Run
import_symbols.pyfrom the binanana category - Select
profile/<version>/symbol/main.sym
Or in headless mode:
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- Open Ghidra -> File -> Parse C Source...
- Select
clib.prfas parse configuration - Add
profile/<version>/include/main.hto source files - Add
profile/<version>/includeto include paths - Add
-DGHIDRAto parse options - Press Parse to Program
The Ghidra archive includes a server (/opt/ghidra/server/) for
collaborative multi-user repository sharing. This is not required for
binanana's headless analysis workflow. It is useful if multiple analysts
need to share a Ghidra project database.
See /opt/ghidra/server/svrREADME.md for server setup.
The extension/ directory contains an installable Ghidra extension
that bundles both a Java binary detector and the Python analysis
scripts into a single package.
When installed, the extension adds:
- WoW Binary Detector -- A Java analyzer that runs during
Ghidra's auto-analysis. It scans
.rdatafor theCObjectRTTI signature to identify WoW binaries and sets program properties with the RTTI entry count. - Script Manager integration -- All Python scripts from
ghidra/appear in Script Manager automatically, without manual directory configuration.
The Java code is limited to binary detection (~100 lines). All analysis logic (RTTI chain walking, Lua API resolution, symbol management) remains in Python.
Requirements: Gradle 8.5+, JDK 21+, GHIDRA_INSTALL_DIR environment
variable set.
# Build the extension zip
make build-extension
# Build and install to the system Ghidra installation
make install-extensionThe built zip is in extension/dist/. It can also be installed via
Ghidra's File -> Install Extensions dialog (green + button).
Ghidra's auto-analysis extension points (AbstractAnalyzer,
AbstractProgramWrapperLoader, ProgramPlugin) require Java.
Ghidra's ClassSearcher scans compiled .class files on the
classpath; Python classes created via JPype are not visible to it.
This is why binary detection uses Java while analysis logic stays
in Python.
For binaries loaded in Binary Ninja with the binary_ninja_mcp plugin:
# Export user-named symbols from BN to binanana format
./script/export-from-binja profile/classic-1.13.2-31650-windows-win64
# Or via Make
make export-from-binja PROFILE=profile/classic-1.13.2-31650-windows-win64This connects to BN's HTTP API at localhost:9009, exports user-named
functions and data labels, and writes to symbol/export/func.sym and
symbol/export/label.sym.
The propagate_symbols.py script matches functions across binary
versions using instruction-level hashing. Workflow:
- Analyze one version thoroughly (e.g., 1.13.2)
- Open the analyzed binary in Ghidra, run
propagate_symbols.pywith mode "export" to save function hashes - Open the target binary, run
propagate_symbols.pywith mode "import" and the hash file from step 2 - Review matched symbols and commit to the target profile
In headless mode:
# Export hashes from source binary
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"
# Import and match against target binary
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"| Version | Build | Product | Platform | Binary |
|---|---|---|---|---|
| 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 |