[GH-ISSUE #3607] [Feature] Comprehensive Unity extraction and reverse engineering: IL2CPP metadata recovery, Mono CIL decompilation, and serialized asset analysis #1338

Open
opened 2026-10-16 21:04:26 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @matbrgz on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3607

Type: Feature Domain: Game & Engine RE Status: Proposed Ecosystem: REA

Current limitation

REA currently classifies Unity artifacts through shallow surface heuristics:

  • Matching UnityEngine or UnityEngine.* assembly references in managed .NET inspection (src/dotnet/ManagedArtifactInspector.ts), marking the runtime family as unity-mono.
  • Matching filename segments (libunity.so on Android or UnityFramework.framework on iOS/macOS), tagging the runtime convention as unity or unity-convention.

However, modern real-world Unity applications across Windows, Android, iOS, macOS, and WebGL predominantly use the IL2CPP scripting backend (or Mono CIL). For these targets, REA cannot currently recover:

  1. IL2CPP Metadata & Function Symbols: In an IL2CPP build, C# scripts are transpiled to C++ and compiled into native executables (GameAssembly.dll, libil2cpp.so, or Mach-O binaries). High-level method names, types, properties, and field layouts are stripped from the native binary and stored separately in global-metadata.dat (usually in Data/Managed/Metadata/ or APK assets/bin/Data/Managed/Metadata/). In REA's Ghidra/Rizin workflows, these functions remain anonymous subroutines (FUN_1800...), making automated agent reasoning and reverse engineering tedious.
  2. Unity Serialized Files (.assets) & AssetBundles: Game logic, configuration data, API endpoints, encryption keys, and scene graphs are stored in Unity proprietary serialized files (.assets, sharedassets*.assets, level*) and AssetBundle archives (LZ4/LZMA compressed). REA currently has no parser for SerializedFile headers, TypeTrees, or Object Tables.
  3. MonoBehaviour & Component Topology: In both Mono and IL2CPP builds, GameObject hierarchies and MonoBehaviour component bindings (linking serialized m_Script pointers to C# classes) cannot be mapped.

Proposed outcome

Introduce dedicated Unity reverse engineering and extraction capabilities into REA, partitioned into modular, static analysis layers:

1. IL2CPP Metadata Recovery & Native Symbol Hydration

  • Metadata Parser: Implement a safe, inert parser for global-metadata.dat supporting metadata versions 24 through 31+ (covering Unity 2018 through 2023 / Unity 6). Extract:
    • String literals and field default values.
    • Type definitions (namespaces, class names, interfaces, attributes).
    • Method definitions (method names, return types, parameter signatures, generic instances).
    • Field definitions and memory offsets.
  • RVA & Method Table Correlation: Correlate metadata method indices with the code registration tables (g_CodeRegistration / g_MetadataRegistration) in GameAssembly.dll, libil2cpp.so, or Mach-O binary to recover exact Virtual Addresses (RVAs) for all methods.
  • Provider Hydration (Ghidra / Rizin): Automatically feed recovered symbols, method signatures, parameter dossiers, and Il2CppClass struct definitions into the headless provider analysis database.
    • Native functions recovered through inspect_native_functions receive their qualified C# names (e.g. AssemblyCSharp.PlayerController$$Awake).
    • Native data types recovered through inspect_native_data_type receive typed field layouts with accurate offsets.
  • Synthetic Symbol Export: Optionally produce C# dummy assembly headers or JSON symbol maps (script.json / il2cpp.h) for external inspection.

2. Unity Serialized File (.assets) & AssetBundle Extraction

  • SerializedFile & AssetBundle Readers: Add inert parsers for Unity serialized containers (SerializedFile format versions 9-22+) and AssetBundle archives (supporting uncompressed, LZ4, and LZMA chunks with bounded decompression memory budgets).
  • TypeTree Reconstruction:
    • When TypeTree metadata is embedded in the .assets file, decode serialized structures (e.g., TextAsset, Transform, GameObject, MonoBehaviour) into structured JSON observations without executing code.
    • When TypeTree is stripped (standard in production builds), correlate Class IDs (m_ClassID) and script hashes with recovered IL2CPP / Mono class schemas to re-synthesize field layouts.
  • Artifact Exposure: Expose extracted configuration assets, embedded strings, remote API URLs, network endpoints, and third-party SDK configurations (Firebase, AppsFlyer, Photon, etc.) as first-class analysis evidence.

3. Scene & GameObject Hierarchy Mapping

  • Reconstruct the parent-child scene graph from GameObject and Transform records.
  • Map serialized MonoBehaviour components to their underlying script definitions (m_Script), identifying active gameplay, networking, and telemetry controllers.
  • Parse serialized UnityEvent listeners to trace user-interaction and trigger callbacks directly to target method names.

4. Managed Mono Backend (Assembly-CSharp.dll) Enhancement

  • For classic Mono deployments, expand REA's src/dotnet/ inspector to decompile and extract structured CIL method bodies, properties, and embedded resource blobs from Assembly-CSharp.dll.

Safety Boundaries & Error Handling

  • Inert Analysis: All parsing remains non-executing and offline; no native code or Unity engine runtime is loaded or executed.
  • Decompression Bombs & Resource Caps: Enforce strict byte, depth, and entry limits on AssetBundle LZ4/LZMA expansion and TypeTree depth to protect against malformed or malicious inputs.
  • Obfuscation & Encryption Detection: When global-metadata.dat has an invalid magic number (not 0xFAB11BAF) due to XOR encryption, packer shuffling (e.g., Medusa, Beebyte, ArmDot), or custom loaders, return typed, actionable diagnostics (metadata_encrypted or unsupported_format) with offset and header findings rather than unhandled exceptions.

Acceptance Criteria

  • Automatically classify artifacts as unity-il2cpp or unity-mono across PE, ELF, Mach-O, and Android APK/AAB inputs.
  • Parse valid global-metadata.dat files headless in REA without requiring manual external GUI tooling (such as Il2CppDumper or AssetStudio).
  • Successfully resolve method pointers and annotate function symbols in Ghidra/Rizin test fixtures.
  • Provide structured asset extraction for .assets and AssetBundle files with verifiable SHA-256 evidence integrity.
  • Define explicit failure contracts for stripped, encrypted, or unsupported metadata versions.
  • Include unit and integration tests using bounded synthetic Unity fixtures.
  • #743 [Feature] Recover .NET NativeAOT metadata in Ghidra workflows
  • #492 Inspect recovered native data types and layouts
  • #41 Add mobile application inspection
Originally created by @matbrgz on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3607 [![Type: Feature](https://img.shields.io/badge/type-feature-blue.svg?style=flat-square)](https://github.com/morluto/rea) [![Domain: Game & Engine RE](https://img.shields.io/badge/domain-game_&_engine_RE-green.svg?style=flat-square)](https://github.com/morluto/rea) [![Status: Proposed](https://img.shields.io/badge/status-proposed-blue.svg?style=flat-square)](https://github.com/morluto/rea) [![Ecosystem: REA](https://img.shields.io/badge/ecosystem-morluto%2Frea-8A2BE2.svg?style=flat-square)](https://github.com/morluto/rea) ## Current limitation REA currently classifies Unity artifacts through shallow surface heuristics: - Matching `UnityEngine` or `UnityEngine.*` assembly references in managed .NET inspection (`src/dotnet/ManagedArtifactInspector.ts`), marking the runtime family as `unity-mono`. - Matching filename segments (`libunity.so` on Android or `UnityFramework.framework` on iOS/macOS), tagging the runtime convention as `unity` or `unity-convention`. However, modern real-world Unity applications across Windows, Android, iOS, macOS, and WebGL predominantly use the **IL2CPP** scripting backend (or Mono CIL). For these targets, REA cannot currently recover: 1. **IL2CPP Metadata & Function Symbols:** In an IL2CPP build, C# scripts are transpiled to C++ and compiled into native executables (`GameAssembly.dll`, `libil2cpp.so`, or Mach-O binaries). High-level method names, types, properties, and field layouts are stripped from the native binary and stored separately in `global-metadata.dat` (usually in `Data/Managed/Metadata/` or APK `assets/bin/Data/Managed/Metadata/`). In REA's Ghidra/Rizin workflows, these functions remain anonymous subroutines (`FUN_1800...`), making automated agent reasoning and reverse engineering tedious. 2. **Unity Serialized Files (.assets) & AssetBundles:** Game logic, configuration data, API endpoints, encryption keys, and scene graphs are stored in Unity proprietary serialized files (`.assets`, `sharedassets*.assets`, `level*`) and `AssetBundle` archives (LZ4/LZMA compressed). REA currently has no parser for `SerializedFile` headers, TypeTrees, or Object Tables. 3. **MonoBehaviour & Component Topology:** In both Mono and IL2CPP builds, GameObject hierarchies and MonoBehaviour component bindings (linking serialized `m_Script` pointers to C# classes) cannot be mapped. ## Proposed outcome Introduce dedicated Unity reverse engineering and extraction capabilities into REA, partitioned into modular, static analysis layers: ### 1. IL2CPP Metadata Recovery & Native Symbol Hydration - **Metadata Parser:** Implement a safe, inert parser for `global-metadata.dat` supporting metadata versions 24 through 31+ (covering Unity 2018 through 2023 / Unity 6). Extract: - String literals and field default values. - Type definitions (namespaces, class names, interfaces, attributes). - Method definitions (method names, return types, parameter signatures, generic instances). - Field definitions and memory offsets. - **RVA & Method Table Correlation:** Correlate metadata method indices with the code registration tables (`g_CodeRegistration` / `g_MetadataRegistration`) in `GameAssembly.dll`, `libil2cpp.so`, or Mach-O binary to recover exact Virtual Addresses (RVAs) for all methods. - **Provider Hydration (Ghidra / Rizin):** Automatically feed recovered symbols, method signatures, parameter dossiers, and `Il2CppClass` struct definitions into the headless provider analysis database. - Native functions recovered through `inspect_native_functions` receive their qualified C# names (e.g. `AssemblyCSharp.PlayerController$$Awake`). - Native data types recovered through `inspect_native_data_type` receive typed field layouts with accurate offsets. - **Synthetic Symbol Export:** Optionally produce C# dummy assembly headers or JSON symbol maps (`script.json` / `il2cpp.h`) for external inspection. ### 2. Unity Serialized File (.assets) & AssetBundle Extraction - **SerializedFile & AssetBundle Readers:** Add inert parsers for Unity serialized containers (`SerializedFile` format versions 9-22+) and AssetBundle archives (supporting uncompressed, LZ4, and LZMA chunks with bounded decompression memory budgets). - **TypeTree Reconstruction:** - When TypeTree metadata is embedded in the `.assets` file, decode serialized structures (e.g., `TextAsset`, `Transform`, `GameObject`, `MonoBehaviour`) into structured JSON observations without executing code. - When TypeTree is stripped (standard in production builds), correlate Class IDs (`m_ClassID`) and script hashes with recovered IL2CPP / Mono class schemas to re-synthesize field layouts. - **Artifact Exposure:** Expose extracted configuration assets, embedded strings, remote API URLs, network endpoints, and third-party SDK configurations (Firebase, AppsFlyer, Photon, etc.) as first-class analysis evidence. ### 3. Scene & GameObject Hierarchy Mapping - Reconstruct the parent-child scene graph from `GameObject` and `Transform` records. - Map serialized `MonoBehaviour` components to their underlying script definitions (`m_Script`), identifying active gameplay, networking, and telemetry controllers. - Parse serialized `UnityEvent` listeners to trace user-interaction and trigger callbacks directly to target method names. ### 4. Managed Mono Backend (Assembly-CSharp.dll) Enhancement - For classic Mono deployments, expand REA's `src/dotnet/` inspector to decompile and extract structured CIL method bodies, properties, and embedded resource blobs from `Assembly-CSharp.dll`. ## Safety Boundaries & Error Handling - **Inert Analysis:** All parsing remains non-executing and offline; no native code or Unity engine runtime is loaded or executed. - **Decompression Bombs & Resource Caps:** Enforce strict byte, depth, and entry limits on AssetBundle LZ4/LZMA expansion and TypeTree depth to protect against malformed or malicious inputs. - **Obfuscation & Encryption Detection:** When `global-metadata.dat` has an invalid magic number (not `0xFAB11BAF`) due to XOR encryption, packer shuffling (e.g., Medusa, Beebyte, ArmDot), or custom loaders, return typed, actionable diagnostics (`metadata_encrypted` or `unsupported_format`) with offset and header findings rather than unhandled exceptions. ## Acceptance Criteria - Automatically classify artifacts as `unity-il2cpp` or `unity-mono` across PE, ELF, Mach-O, and Android APK/AAB inputs. - Parse valid `global-metadata.dat` files headless in REA without requiring manual external GUI tooling (such as Il2CppDumper or AssetStudio). - Successfully resolve method pointers and annotate function symbols in Ghidra/Rizin test fixtures. - Provide structured asset extraction for `.assets` and `AssetBundle` files with verifiable SHA-256 evidence integrity. - Define explicit failure contracts for stripped, encrypted, or unsupported metadata versions. - Include unit and integration tests using bounded synthetic Unity fixtures. ## Related - #743 [Feature] Recover .NET NativeAOT metadata in Ghidra workflows - #492 Inspect recovered native data types and layouts - #41 Add mobile application inspection
Author
Owner

@8rulerstar commented on GitHub (Oct 20, 2026):

For anyone following the IL2CPP and Mono parts, the related PRs so far:

  • Merged: #3807 (metadata inventory), #5326 (member names, string literals, metadata 24 and 27), #5327 (native method addresses and --ghidra-seeds).
  • Open: #5511 (desktop triage suggests inspect_managed_members for Unity Mono script assemblies), #5726 (truncated metadata header error).

Unity Mono builds already work through the existing managed tools (inspect_managed_artifact, inspect_managed_members).

<!-- gh-comment-id:6291189071 --> @8rulerstar commented on GitHub (Oct 20, 2026): For anyone following the IL2CPP and Mono parts, the related PRs so far: - Merged: #3807 (metadata inventory), #5326 (member names, string literals, metadata 24 and 27), #5327 (native method addresses and `--ghidra-seeds`). - Open: #5511 (desktop triage suggests `inspect_managed_members` for Unity Mono script assemblies), #5726 (truncated metadata header error). Unity Mono builds already work through the existing managed tools (`inspect_managed_artifact`, `inspect_managed_members`).
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

A small neutral fixture for the prepared SerializedFile inventory slice of this request. This is a support qualification for REA 6.7.0, not a regression report.

The generated carrier is 148 bytes, SHA-256 464f2335deaeef05a440605a8a678f7db757eb838874065579e59253890a72d8. It has a 48-byte v22 header, little-endian metadata, metadata size 85, file size 148, data offset 144, and type tree disabled. It contains one type (class ID 49), one object (path ID 1), and the four opaque payload bytes DEMO. Script, external and reference-type counts are zero. It contains no application-derived bytes.

import base64
from pathlib import Path
Path('neutral-unity-v22.assets').write_bytes(base64.b64decode(
    'AAAAAAAAAAAAAAAWAAAAAAAAAAAAAABVAAAAAAAAAJQAAAAAAAAAkAAAAAAAAAAANjAwMC4wLjBmMQATAAAAAAEAAAAxAAAAAP//AAAAAAAAAAAAAAAAAAAAAAEAAAAAAQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREVNTw=='
))

Public CLI reproduction (the observed run used the locally installed, pinned 6.7.0 entrypoint):

npx --yes rea-agents@6.7.0 inspect-artifact "$PWD/neutral-unity-v22.assets" --format json --full-output

Observed exit code: 1. Envelope: ok: false, code target_unavailable, category unsupported_target, reason unsupported binary format, constraint file_format_rejected.

This could serve as a minimal acceptance fixture for a first inert inventory: headers, type records, object descriptors with physical ranges/SHA, and declared references. Useful boundaries include exact metadata consumption, absolute alignment, type index versus class ID, lossless int64 path IDs, conditional script IDs, explicit version/type-tree admission, and byte/count bounds. Missing type trees should remain unknown schemas; no implicit dependency loads, object-property parsing, native execution or address linking are needed for this slice. SerializedFile v22 is a separate layout from IL2CPP metadata versions.

<!-- gh-comment-id:6292630300 --> @matbrgz commented on GitHub (Oct 20, 2026): <!-- neutral-serialized-v22-464f2335deaeef05 --> A small neutral fixture for the prepared **SerializedFile inventory** slice of this request. This is a support qualification for REA 6.7.0, not a regression report. The generated carrier is 148 bytes, SHA-256 `464f2335deaeef05a440605a8a678f7db757eb838874065579e59253890a72d8`. It has a 48-byte v22 header, little-endian metadata, metadata size 85, file size 148, data offset 144, and type tree disabled. It contains one type (class ID 49), one object (path ID 1), and the four opaque payload bytes `DEMO`. Script, external and reference-type counts are zero. It contains no application-derived bytes. ```python import base64 from pathlib import Path Path('neutral-unity-v22.assets').write_bytes(base64.b64decode( 'AAAAAAAAAAAAAAAWAAAAAAAAAAAAAABVAAAAAAAAAJQAAAAAAAAAkAAAAAAAAAAANjAwMC4wLjBmMQATAAAAAAEAAAAxAAAAAP//AAAAAAAAAAAAAAAAAAAAAAEAAAAAAQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREVNTw==' )) ``` Public CLI reproduction (the observed run used the locally installed, pinned 6.7.0 entrypoint): ```sh npx --yes rea-agents@6.7.0 inspect-artifact "$PWD/neutral-unity-v22.assets" --format json --full-output ``` Observed exit code: 1. Envelope: `ok: false`, code `target_unavailable`, category `unsupported_target`, reason `unsupported binary format`, constraint `file_format_rejected`. This could serve as a minimal acceptance fixture for a first inert inventory: headers, type records, object descriptors with physical ranges/SHA, and declared references. Useful boundaries include exact metadata consumption, absolute alignment, type index versus class ID, lossless int64 path IDs, conditional script IDs, explicit version/type-tree admission, and byte/count bounds. Missing type trees should remain unknown schemas; no implicit dependency loads, object-property parsing, native execution or address linking are needed for this slice. SerializedFile v22 is a separate layout from IL2CPP metadata versions.
Author
Owner

@8rulerstar commented on GitHub (Oct 21, 2026):

I tried this fixture on an unmerged branch of mine (feat/unity-assets-v2, no PR), in case the result is useful. Released 6.7.0 and current main behave as you described: inspect-artifact rejects the file with file_format_rejected.

On that branch, rea inspect-unity-assets neutral-unity-v22.assets --format json --full-output exits 0 and reports:

  • serialized-file version 22, little-endian, metadata size 85, file size 148, data offset 144, Unity 6000.0.0f1, target platform 19, type tree disabled
  • one type: class ID 49 (TextAsset), no script type
  • one object: path ID "1", class ID 49, offset 144, size 4
  • no issues; object bodies are located but not decoded

It does not produce a per-object SHA-256, and inspect-artifact still rejects the file there.

Thanks for putting the fixture together; it made this easy to check.

<!-- gh-comment-id:6304913217 --> @8rulerstar commented on GitHub (Oct 21, 2026): I tried this fixture on an unmerged branch of mine (`feat/unity-assets-v2`, no PR), in case the result is useful. Released 6.7.0 and current `main` behave as you described: `inspect-artifact` rejects the file with `file_format_rejected`. On that branch, `rea inspect-unity-assets neutral-unity-v22.assets --format json --full-output` exits 0 and reports: - `serialized-file` version 22, little-endian, metadata size 85, file size 148, data offset 144, Unity `6000.0.0f1`, target platform 19, type tree disabled - one type: class ID 49 (`TextAsset`), no script type - one object: path ID `"1"`, class ID 49, offset 144, size 4 - no issues; object bodies are located but not decoded It does not produce a per-object SHA-256, and `inspect-artifact` still rejects the file there. Thanks for putting the fixture together; it made this easy to check.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1338
No description provided.