Dump the clean, deobfuscated IL2CPP global-metadata header — and the whole metadata file behind it — from a running Unity app, using Frida.
Quick start · How it works · Documentation · Testing · License
A protected Unity build ships a global-metadata.dat whose header is not the real one; the runtime rebuilds the real header in memory during il2cpp_init.
il2cpp-re catches it there and writes a file that Il2CppDumper (or a similar tool) can open.
The target app is never hardcoded — pass it with --target or an environment variable at run time.
- Several independent ways to find the header — an offset hook, a Stalker register scan with a native (CModule) filter, a memory search, and a trace of the file's own reads — plus a report on how well they agree. → Strategies and agreement
- The whole metadata file, parse-checked against its header before it is trusted. → Usable metadata
- Offline patching — put the clean header into the app's own
global-metadata.dat(or its.ipa) with no device attached. → Usable metadata - Remembers each build — where the header was (build profiles) and how it was scrambled (unshuffle).
- Validates what it captures — a magic number is not enough; the header must fit the known layouts (metadata v16–v31). → Header validation
Purpose. A personal research / reverse-engineering tool for understanding IL2CPP internals and metadata layout. It is not a cheat or hack tool and ships with no feature that modifies app state. Only use it on apps you have the legal right to analyze (your own builds, or apps you are explicitly authorized to test). Running it against a third-party app may violate that app's Terms of Service; that is on you.
Status — v1.0.0, research prototype. Verified against a real Frida only on x86-64 Linux, with a synthetic protected runtime (
tests/fake_unity/). Not yet run on a real Unity app, iOS, Android or arm64, and the whole-file parse check and--merge-withhave only seen synthetic metadata files. Expect rough edges. Details: What has and has not been run.
- Python 3.9+ and
pip install -r requirements.txt(frida≥ 16.7 and < 18; the agents needProcess.attachModuleObserver). - A
frida-serveron the device, with the same version as the Pythonfrida— the server's version also decides whether Stalker may be used (Frida versions). Reach it on a jailbroken iOS device, a rooted Android device or emulator, or this machine: Connecting to a device. - A checkout. The controller loads
agents/anddata/from its own directory, so it is run from the repository, not installed as a package.
pip install -r requirements.txt
# iOS (jailbroken): forward the USB port to frida-server, then dump
pymobiledevice3 usbmux forward 45861 45861
python dump_clean_header.py --target com.example.unityapp
# Android: Frida reaches frida-server through adb
python dump_clean_header.py --device usb --target com.example.unityapp
# Header only, or: refuse to write unless two independent kinds of method found the same header
python dump_clean_header.py --target com.example.unityapp --header-only
python dump_clean_header.py --target com.example.unityapp --require-agreement 2
# Offline, no device and no frida: patch a clean header from an earlier run into the app's own file
python dump_clean_header.py --header-in clean_header.bin --merge-with Game.ipaA run spawns the app, injects the agent, resumes the process and waits (--timeout, 45 s by default) for il2cpp_init to reach the point where the header has been deobfuscated.
The controller validates the result again and writes clean_header.bin and global-metadata.dat only if it passes.
Exit status 0 means success; 1–6 say what went wrong → Exit status. More recipes: Usage.
- The controller spawns the app, loads the agent and arms every hook before the process resumes, so nothing depends on a timer.
- While
il2cpp_initrebuilds the real header, the agent looks for it — by an offset hook, a Stalker scan of the registers, a memory search, or a trace of the metadata file's reads. - Every candidate goes through the same header validator. The first one that passes ends the run, or — with several strategies — they are compared and a confidence level is reported.
- The controller reads the rest of the metadata, overlays the clean header snapshot, runs the parse check and only then writes the file.
→ How it works · Strategies and agreement · The native scan filter
| Page | What is in it |
|---|---|
| Usage | Connecting to a device, a recipe for every mode, output files, exit status |
| Options | Every flag: default, which agent honours it, what it does |
| Usable metadata | --full-metadata, --merge-with, --header-in, and the parse check behind them |
| Build profiles | Offset + register remembered per build; verified / failed states |
| Unshuffle | Learning how a header is scrambled and undoing it offline |
| How it works | Agents, hook timing, the dynamic and memory scans, lifecycle and cleanup |
| Strategies and agreement | The four strategies, confidence levels, --require-agreement, the file trace |
| The native scan filter | The CModule callout, and how to measure the scan on your target |
| Platform and limits | iOS / Android / x64, hook-point limits, which Frida versions may use Stalker |
| Header validation | What validateHeader() checks; header_layout.json and struct_layout.json |
| Testing | The four kinds of evidence, CI, releasing, what has been run |
il2cpp-re/
├── dump_clean_header.py # controller: spawn the app, load the agent, validate and write the header
├── metadata_file.py # the metadata file: parse check, patching a header into the original
├── build_profile.py # build profiles (offset + register per build)
├── unshuffle.py # learn / undo header scrambling
├── agreement.py # do the strategies that ran agree? findings -> confidence
├── agents/ # dump_clean_header_v4.js (recommended) and legacy/
├── data/ # header_layout.json, struct_layout.json
├── tools/ # bench_scan.py, record_fixtures.py, gen_struct_layout.py
├── tests/ # unit, fake-Frida, real-Frida, golden and opt-in device tests
├── docs/ # the documentation linked above
├── requirements*.txt, pyproject.toml, .github/workflows/ci.yml
└── LICENSE # MIT
pip install -r requirements-dev.txt # optional: capstone, keystone (tests that need them skip without)
python -m unittest discover -s tests -v # Python, including the real-Frida suites
node --test tests/test_agent.js tests/test_strategies.js tests/test_native_filter.js tests/test_golden.jsFour kinds of evidence — real Frida against a synthetic app, golden recordings, a fake Frida, and an opt-in real app — are described in Testing, together with CI and what has not been run yet.
Released under the MIT License — Copyright (c) 2026 ndhn27.
data/header_layout.json and data/struct_layout.json are generated from the MetadataClass.cs of Il2CppDumper, which is also MIT-licensed.