Skip to content
ndhn27Public

About

Dump the clean, deobfuscated IL2CPP global-metadata header (and the full metadata file) from a running Unity app with Frida. Multiple detection strategies, build profiles, offline header patching. Research prototype for studying IL2CPP internals.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

il2cpp-re

Dump the clean, deobfuscated IL2CPP global-metadata header — and the whole metadata file behind it — from a running Unity app, using Frida.

License: MIT Version 1.0.0 Python 3.9+ Frida 16.7 to 17.x

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.

✨ Features

  • 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

📌 Scope and status

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-with have only seen synthetic metadata files. Expect rough edges. Details: What has and has not been run.

🧰 Requirements

  • Python 3.9+ and pip install -r requirements.txt (frida ≥ 16.7 and < 18; the agents need Process.attachModuleObserver).
  • A frida-server on the device, with the same version as the Python frida — 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/ and data/ from its own directory, so it is run from the repository, not installed as a package.

🚀 Quick start

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.ipa

A 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.

🧭 How it works

  1. The controller spawns the app, loads the agent and arms every hook before the process resumes, so nothing depends on a timer.
  2. While il2cpp_init rebuilds 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.
  3. 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.
  4. 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

📚 Documentation

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

📁 Repository layout

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

🧪 Testing

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.js

Four 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.

📄 License

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.

About

Dump the clean, deobfuscated IL2CPP global-metadata header (and the full metadata file) from a running Unity app with Frida. Multiple detection strategies, build profiles, offline header patching. Research prototype for studying IL2CPP internals.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages