/reversing-unity-il2cpp
Reverse engineer Unity games and apps built with IL2CPP or Mono, using Il2CppDumper, Il2CppInspector, and dnSpy. Use when an APK or IPA contains global-metadata.dat, libil2cpp.so, UnityFramework, or Assembly-CSharp.dll, when jadx shows only UnityPlayerActivity, or when the
$ npx -y skills add trilwu/secskills --skill reversing-unity-il2cpp --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/reversing-unity-il2cpp
Context preview
The summary Claude sees to decide when to auto-load this skill.
Reverse engineer Unity games and apps built with IL2CPP or Mono, using Il2CppDumper, Il2CppInspector, and dnSpy. Use when an APK or IPA contains global-metadata.dat, libil2cpp.so, UnityFramework, or Assembly-CSharp.dll, when jadx shows only UnityPlayerActivity, or when the
SKILL.md
reversing-unity-il2cpp.SKILL.mdname: reversing-unity-il2cpp
description: Reverse engineer Unity games and apps built with IL2CPP or Mono, using Il2CppDumper, Il2CppInspector, and dnSpy. Use when an APK or IPA contains global-metadata.dat, libil2cpp.so, UnityFramework, or Assembly-CSharp.dll, when jadx shows only UnityPlayerActivity, or when the target is described as a Unity build.
verified: 2026-07-27
Reversing Unity IL2CPP
Unity ships in two very different shapes, and identifying which one you have is the entire first decision. A Mono build hands you decompilable .NET assemblies. An IL2CPP build compiles C# to C++ to native code, and the C# metadata lives in a separate file that you must recombine with the binary before anything is readable.
When to Use
- The APK contains `assets/bin/Data/Managed/Metadata/global-metadata.dat` and
`lib/arm64-v8a/libil2cpp.so`
- The APK contains `assets/bin/Data/Managed/Assembly-CSharp.dll` (Mono build)
- The IPA contains `Frameworks/UnityFramework.framework`
- `jadx` shows only `UnityPlayerActivity` and Unity plumbing
- You need to recover game or app logic, anti-cheat behaviour, or the API layer
When NOT to Use
- **Flutter or React Native** — use `reversing-flutter-apps` or
`reversing-react-native-apps`
- **Ordinary native apps** — use `testing-mobile-applications`
- **Desktop game binaries with no Unity markers** — use `analyzing-binaries`
- **Building cheats or bypassing anti-cheat in live multiplayer services** —
out of scope; that is service abuse, not assessment
Identify the Build
unzip -l target.apk | rg 'global-metadata|libil2cpp|Assembly-CSharp|libmono'
| Present | Build | Difficulty | | --- | --- | --- | | `Assembly-CSharp.dll`, `libmono*.so` | Mono | Easy — decompile the DLL directly | | `global-metadata.dat` + `libil2cpp.so` | IL2CPP | The main path below | | Neither, but `UnityFramework` | iOS IL2CPP | Same as IL2CPP; extract from the decrypted IPA |
**Mono builds are a short job.** Pull the assembly and open it:
unzip -j target.apk 'assets/bin/Data/Managed/Assembly-CSharp.dll' -d ./out
# dnSpy / dnSpyEx / ILSpy / dotPeek — full C# source recovery, and dnSpy can edit
ilspycmd ./out/Assembly-CSharp.dll -o ./decompiled
Everything below is for IL2CPP.
Recombining Metadata with the Binary
IL2CPP splits the information: `libil2cpp.so` holds the compiled code, `global-metadata.dat` holds the C# type system — class names, method names, field names, string literals. Neither is useful alone. The tools' job is to match them and produce symbols.
unzip -j target.apk 'lib/arm64-v8a/libil2cpp.so' \
'assets/bin/Data/Managed/Metadata/global-metadata.dat' -d ./in
# Il2CppDumper: the standard first attempt
Il2CppDumper ./in/libil2cpp.so ./in/global-metadata.dat ./outTypical output and what it is for:
| Artifact | Use | | --- | --- | | `dump.cs` | Every class, method, and field with addresses — **read this first** | | `DummyDll/` | Stub .NET assemblies; open in dnSpy/ILSpy to browse the type system comfortably | | `script.json` | Symbol import for IDA/Ghidra — applies names to `libil2cpp.so` | | `stringliteral.json` | String constants: URLs, keys, messages |
`Il2CppInspector` is the alternative when Il2CppDumper fails; it supports a different range of Unity versions and emits Ghidra and IDA scripts plus a richer C++ header. Try both before concluding a build is unsupported.
**Version mismatch is the usual failure.** The metadata format changes across Unity releases; the tool asks for or infers the version. Read the Unity version first and feed it explicitly:
strings ./in/libil2cpp.so | rg -m3 '20[0-9]{2}\.[0-9]+\.[0-9]+'
strings ./in/global-metadata.dat | head -5**Protected builds** encrypt or scramble `global-metadata.dat` — the header magic will be wrong and the dumpers will refuse. Signs: the file does not start with the expected metadata magic, or entropy is uniformly high. The practical route is dynamic: let the app decrypt the metadata itself, then dump it from memory once loaded.
# Dump the decrypted metadata from a running process
frida -U -f com.target.app -l dump-metadata.js
# Then feed the dumped file to Il2CppDumper as normal
That pattern — let it unpack itself, dump from memory — is the same one used for packed malware, and it is almost always cheaper than defeating the protection statically. See `analyzing-binaries`.
Working the Recovered Code
1. **Read `dump.cs` for the type map.** Search for the classes that matter: anything named `*Manager`, `*Service`, `*Api`, `*Network`, `*Auth`, `*Purchase`, `*Config`. 2. **Load `script.json` into IDA or Ghidra** so `libil2cpp.so` shows C# method names instead of `sub_1A2B3C`. Without this step the disassembly is unreadable; with it, it reads like decompiled C#. 3. **Check `stringliteral.json` for endpoints and keys.** Same reasoning as every other framework — string constants survive everything. 4. **Hook at runtime with Frida** using the addresses from `dump.cs`. Method addresses are relative to the `libil2cpp.so` base, so resolve the module base and add the offset.
// Pattern: resolve base, add the RVA from dump.cs, intercept
const base = Module.findBaseAddress('libil2cpp.so');
Interceptor.attach(base.add(0x1A2B3C), {
onEnter(args) { console.log('called with', args[1]); },
onLeave(ret) { console.log('->', ret); }
});Security-Relevant Findings
For an assessment rather than a curiosity exercise, the recurring issues:
- **Hardcoded credentials and endpoints** in `stringliteral.json` — backend
keys, analytics tokens, cloud storage credentials.
- **Client-authoritative logic.** Currency, entitlements, and validation done
in C# and trusted by the server. Verify by calling the API directly.
- **Weak or absent certificate pinning.** Unity's networking layer is often
configured permissively; check `UnityWebRequest` usage.
- **Local save and config
Read more
name: reversing-unity-il2cpp description: Reverse engineer Unity games and apps built with IL2CPP or Mono, using Il2CppDumper, Il2CppInspector, and dnSpy. Use when an APK or IPA contains global-metadata.dat, libil2cpp.so, UnityFramework, or Assembly-CSharp.dll, when jadx shows only UnityPlayerActivity, or when the target is described as a Unity build. verified: 2026-07-27
Reversing Unity IL2CPP
Unity ships in two very different shapes, and identifying which one you have is the entire first decision. A Mono build hands you decompilable .NET assemblies. An IL2CPP build compiles C# to C++ to native code, and the C# metadata lives in a separate file that you must recombine with the binary before anything is readable.
When to Use
- The APK contains `assets/bin/Data/Managed/Metadata/global-metadata.dat` and
`lib/arm64-v8a/libil2cpp.so`
- The APK contains `assets/bin/Data/Managed/Assembly-CSharp.dll` (Mono build)
- The IPA contains `Frameworks/UnityFramework.framework`
- `jadx` shows only `UnityPlayerActivity` and Unity plumbing
- You need to recover game or app logic, anti-cheat behaviour, or the API layer
When NOT to Use
- **Flutter or React Native** — use `reversing-flutter-apps` or
`reversing-react-native-apps`
- **Ordinary native apps** — use `testing-mobile-applications`
- **Desktop game binaries with no Unity markers** — use `analyzing-binaries`
- **Building cheats or bypassing anti-cheat in live multiplayer services** —
out of scope; that is service abuse, not assessment
Identify the Build
unzip -l target.apk | rg 'global-metadata|libil2cpp|Assembly-CSharp|libmono'
| Present | Build | Difficulty | | --- | --- | --- | | `Assembly-CSharp.dll`, `libmono*.so` | Mono | Easy — decompile the DLL directly | | `global-metadata.dat` + `libil2cpp.so` | IL2CPP | The main path below | | Neither, but `UnityFramework` | iOS IL2CPP | Same as IL2CPP; extract from the decrypted IPA |
**Mono builds are a short job.** Pull the assembly and open it:
unzip -j target.apk 'assets/bin/Data/Managed/Assembly-CSharp.dll' -d ./out # dnSpy / dnSpyEx / ILSpy / dotPeek — full C# source recovery, and dnSpy can edit ilspycmd ./out/Assembly-CSharp.dll -o ./decompiled
Everything below is for IL2CPP.
Recombining Metadata with the Binary
IL2CPP splits the information: `libil2cpp.so` holds the compiled code, `global-metadata.dat` holds the C# type system — class names, method names, field names, string literals. Neither is useful alone. The tools' job is to match them and produce symbols.
unzip -j target.apk 'lib/arm64-v8a/libil2cpp.so' \
'assets/bin/Data/Managed/Metadata/global-metadata.dat' -d ./in
# Il2CppDumper: the standard first attempt
Il2CppDumper ./in/libil2cpp.so ./in/global-metadata.dat ./outTypical output and what it is for:
| Artifact | Use | | --- | --- | | `dump.cs` | Every class, method, and field with addresses — **read this first** | | `DummyDll/` | Stub .NET assemblies; open in dnSpy/ILSpy to browse the type system comfortably | | `script.json` | Symbol import for IDA/Ghidra — applies names to `libil2cpp.so` | | `stringliteral.json` | String constants: URLs, keys, messages |
`Il2CppInspector` is the alternative when Il2CppDumper fails; it supports a different range of Unity versions and emits Ghidra and IDA scripts plus a richer C++ header. Try both before concluding a build is unsupported.
**Version mismatch is the usual failure.** The metadata format changes across Unity releases; the tool asks for or infers the version. Read the Unity version first and feed it explicitly:
strings ./in/libil2cpp.so | rg -m3 '20[0-9]{2}\.[0-9]+\.[0-9]+'
strings ./in/global-metadata.dat | head -5**Protected builds** encrypt or scramble `global-metadata.dat` — the header magic will be wrong and the dumpers will refuse. Signs: the file does not start with the expected metadata magic, or entropy is uniformly high. The practical route is dynamic: let the app decrypt the metadata itself, then dump it from memory once loaded.
# Dump the decrypted metadata from a running process frida -U -f com.target.app -l dump-metadata.js # Then feed the dumped file to Il2CppDumper as normal
That pattern — let it unpack itself, dump from memory — is the same one used for packed malware, and it is almost always cheaper than defeating the protection statically. See `analyzing-binaries`.
Working the Recovered Code
1. **Read `dump.cs` for the type map.** Search for the classes that matter: anything named `*Manager`, `*Service`, `*Api`, `*Network`, `*Auth`, `*Purchase`, `*Config`. 2. **Load `script.json` into IDA or Ghidra** so `libil2cpp.so` shows C# method names instead of `sub_1A2B3C`. Without this step the disassembly is unreadable; with it, it reads like decompiled C#. 3. **Check `stringliteral.json` for endpoints and keys.** Same reasoning as every other framework — string constants survive everything. 4. **Hook at runtime with Frida** using the addresses from `dump.cs`. Method addresses are relative to the `libil2cpp.so` base, so resolve the module base and add the offset.
// Pattern: resolve base, add the RVA from dump.cs, intercept
const base = Module.findBaseAddress('libil2cpp.so');
Interceptor.attach(base.add(0x1A2B3C), {
onEnter(args) { console.log('called with', args[1]); },
onLeave(ret) { console.log('->', ret); }
});Security-Relevant Findings
For an assessment rather than a curiosity exercise, the recurring issues:
- **Hardcoded credentials and endpoints** in `stringliteral.json` — backend
keys, analytics tokens, cloud storage credentials.
- **Client-authoritative logic.** Currency, entitlements, and validation done
in C# and trusted by the server. Verify by calling the API directly.
- **Weak or absent certificate pinning.** Unity's networking layer is often
configured permissively; check `UnityWebRequest` usage.
- **Local save and config
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

