| name | il2cpp |
| description | Unity IL2CPP game reversing — Il2CppDumper metadata recovery, global-metadata.dat decryption, IDA/Ghidra symbol restore via generated scripts, Frida method hooking, IAP/license bypass, and zygisk-il2cpp-dumper for obfuscated metadata. |
| allowed-tools | Bash Read Write |
| metadata | {"subdomain":"mobile","when_to_use":"unity il2cpp libil2cpp.so global-metadata.dat il2cppdumper il2cppinspector mono game reversing anti-cheat license iap inapp purchase ghidra ida symbol restore zygisk","tags":"unity, il2cpp, game, reverse-engineering, il2cppdumper, global-metadata, ghidra, frida, iap, anti-cheat","mitre_attack":"T1635, T1406, T1407"} |
Unity IL2CPP Game Reversing Playbook
Unity IL2CPP compiles C# to C++ then to native libil2cpp.so. The
managed bytecode is stripped — jadx and apktool expose only the
thin Java bootstrap and reveal nothing of game logic. This playbook
recovers readable symbols and hooks runtime methods for license/IAP
bypass and vulnerability assessment.
Prerequisites
Step 1: Identify Unity IL2CPP App
unzip -o base.apk -d /tmp/apk-out/
ls /tmp/apk-out/lib/arm64-v8a/
ls /tmp/apk-out/assets/bin/Data/Managed/Metadata/
file /tmp/apk-out/lib/arm64-v8a/libil2cpp.so
Step 2: Recover Symbols with Il2CppDumper
cp /tmp/apk-out/lib/arm64-v8a/libil2cpp.so /tmp/
cp /tmp/apk-out/assets/bin/Data/Managed/Metadata/global-metadata.dat /tmp/
mono Il2CppDumper.exe /tmp/libil2cpp.so /tmp/global-metadata.dat /tmp/dump-output/
Output files:
| File | Content |
|---|
dump.cs | All C# class/method/field definitions with offsets |
script.json | Machine-readable symbol map (used by IDA/Ghidra scripts) |
il2cpp.h | C-style struct definitions for IL2CPP internals |
stringliteral.json | All managed string literals with addresses |
grep -i "licen\|premium\|iap\|purchase\|unlock\|cheat\|anti\|integrity" /tmp/dump-output/dump.cs | head -30
grep -A2 "IsPremium\|CheckLicense\|VerifyReceipt\|IsSubscribed" /tmp/dump-output/dump.cs
Step 3: Apply Symbols in Ghidra / IDA
Ghidra (via MCP ghidra server — batch mode)
# 1. Import libil2cpp.so into Ghidra project
# 2. Run auto-analysis (aarch64)
# 3. Execute the Il2CppDumper Ghidra script:
# Script: ghidra_with_struct.py (from Il2CppDumper/tools/)
# Input: script.json + il2cpp.h
# 4. All methods now have their managed C# names
"$GHIDRA_HOME/support/analyzeHeadless" /tmp/ghidra-project IL2CPP \
-import /tmp/libil2cpp.so \
-postScript ghidra_with_struct.py /tmp/dump-output/script.json \
-processor AARCH64:LE:64:v8A \
-noanalysis
IDA (host-side)
After symbol restore, navigate to IsPremiumUser, CheckLicense,
VerifyIAP, IsCheatDetected, etc. by name.
Step 4: Frida Runtime Hooking
Hook via RVA from dump.cs
var il2cpp_base = Module.findBaseAddress("libil2cpp.so");
var RVA = 0x1A4F80;
var isPremium = il2cpp_base.add(RVA);
Interceptor.attach(isPremium, {
onEnter: function(args) {
console.log("[+] IsPremiumUser called");
},
onLeave: function(retval) {
console.log("[+] Original return:", retval.toInt32());
retval.replace(ptr(1));
console.log("[+] Replaced with: 1");
}
});
frida -U -f com.unity.targetgame -l hook-il2cpp.js --no-pause
Static libil2cpp.so patch (persistent, no Frida needed)
python3 - <<'EOF'
import struct
RVA = 0x1A4F80
LOAD_OFFSET = 0x0
with open("/tmp/libil2cpp.so", "r+b") as f:
file_offset = RVA - LOAD_OFFSET
f.seek(file_offset)
f.write(b"\x20\x00\x80\x52\xC0\x03\x5F\xD6")
print(f"[+] Patched at file offset 0x{file_offset:X}")
EOF
apktool b /tmp/apk-smali/ -o /tmp/patched.apk
zip -u /tmp/patched.apk lib/arm64-v8a/libil2cpp.so
uber-apk-signer.jar --allowResign -a /tmp/patched.apk -o /tmp/
adb install /tmp/patched-aligned-signed.apk
Step 5: Encrypted / Obfuscated global-metadata.dat
Some apps (particularly heavily monetized games) encrypt or obfuscate
global-metadata.dat to frustrate IL2CPP reversing.
Detect obfuscation
xxd /tmp/global-metadata.dat | head -2
Common obfuscation patterns
| Pattern | Detection | Counter |
|---|
| XOR with static key | First 4 bytes XOR'd from AF 1B B1 FA | Brute short key or key in libil2cpp.so strings |
| Custom header / prepended garbage | File larger than expected; magic at offset N | Scan for \xAF\x1B\xB1\xFA pattern in file |
| RC4/AES at init | libil2cpp.so contains crypto init before metadata load | Frida hook on il2cpp_codegen_initialize_method |
r2 -qc 'iz~metadata\|iz~global' /tmp/libil2cpp.so | head -20
strings /tmp/libil2cpp.so | grep -iE "meta|key|init" | head -20
zygisk-il2cpp-dumper (runtime dump, bypasses all static obfuscation)
cat /data/adb/modules/zygisk_il2cpp_dumper/config.json
adb shell am start -n com.unity.targetgame/.MainActivity
adb pull /data/local/tmp/il2cpp_dump/
ls il2cpp_dump/
Feed the runtime-dumped files to Il2CppDumper per Step 2.
Step 6: Il2CppInspector (Alternative — Richer Output)
mono Il2CppInspector.exe \
--select-outputs Frida \
--output /tmp/frida-hooks.js \
/tmp/libil2cpp.so /tmp/global-metadata.dat
Evidence
kg_add_node(
kind="finding",
label="Unity IL2CPP client-side IAP bypass",
props={
"key": f"il2cpp-iap-bypass::{package_id}",
"severity": "high",
"cvss": 8.1,
"package": package_id,
"hooked_method": "IsPremiumUser / VerifyReceipt",
"rva": "0x<from-dump.cs>",
"bypass_proof": "Frida hook returns true; premium features unlocked",
},
)
kg_add_node(
kind="finding",
label="Unity IL2CPP anti-cheat bypass",
props={
"key": f"il2cpp-anticheat-bypass::{package_id}",
"severity": "medium",
"method": "IsCheatDetected",
"details": "Client-only check; server-authoritative validation absent",
},
)
ZFP
dump.cs excerpt showing IsPremiumUser with RVA comment.
- Screenshot/screen-recording of the patched/hooked app with
premium features unlocked or anti-cheat bypassed.
- Frida console output showing hook fired + return value replaced.
OPSEC Notes
- Il2CppDumper runs entirely offline on extracted APK files. No
network activity required for analysis.
- Static patching changes the APK signature; Play Integrity / SafetyNet
will flag it. Use Frida hooks on a rooted device for non-persistent
testing.
- zygisk-il2cpp-dumper requires Zygisk (Magisk Delta or native Zygisk).
It runs in the app process at startup and can be detected by some
anti-cheat engines (EAC, BattlEye mobile). Use only in scope.
- Dumped
dump.cs may contain plaintext user-data class names that
reveal the developer's internal naming conventions — treat as
sensitive during an engagement.
Severity Table
| Bug | Severity |
|---|
| Client-side IAP bypass (server trusts client result) | High 8.1 |
| License check entirely client-side | High 7.5 |
| Anti-cheat only client-side (game balance impact) | Medium 5.5 |
| Encrypted metadata recovered via runtime dump | Informational (enables further bugs) |
Hardcoded API key / secret in dump.cs string literals | Critical 9.0 |
References