- Overview
When a Unity project is built using the Mono backend, the Assembly-CSharp.dll is directly accessible from the APK. If the developer hasn’t obfuscated it, tools like ILSpy can easily decompile the assembly. However, when using the IL2CPP backend, this DLL is absent. Instead, the IL2CppDumper tool can reconstruct equivalent type metadata by processing the IL2CPP binary files. This guide demonstrates the process step-by-step.
- Downloading IL2CppDumper
IL2CppDumper is an open-source tool hosted on GitHub. Download the latest release from:
https://github.com/Perfare/Il2CppDumper/releases
After extraction, the folder contains Il2CppDumper.exe and supporting files.
- Creating a Sample Unity Project
For testing, create a simple Unity project with two scripts:
AppController.cs (renamed from Main.cs):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AppController : MonoBehaviour
{
enum AppState
{
Idle,
Running,
Paused
}
public int score = 0;
protected float speed = 0.0f;
private string playerName = "Default";
public int[] highScores;
protected List<string> achievements;
private AppState currentState;
private Greeter greeter = new Greeter();
private void Awake()
{
Initialize();
}
private void Initialize()
{
score = 1;
speed = 2.0f;
highScores = new int[3] { 4, 5, 6 };
achievements = new List<string>() { "first", "second" };
currentState = AppState.Running;
}
private void Start()
{
greeter.SayHello();
playerName = "Guest";
greeter.Say(playerName);
StartCoroutine(UpdateScoreRoutine());
}
private IEnumerator UpdateScoreRoutine()
{
for (int i = 0; i < 100; ++i)
{
++score;
Debug.Log("Score: " + score);
yield return new WaitForSeconds(1);
}
}
}
Greeter.cs (renamed from Hello.cs):
using UnityEngine;
public class Greeter
{
public void SayHello()
{
Debug.Log("Greetings from IL2CPP");
}
public void Say(string message)
{
Debug.Log(message);
}
}
- Building with IL2CPP
Build the project for Android using the IL2CPP scripting backend. The resulting APK (e.g., test.apk) contains the compiled native binaries.
- Extracting Required Files
Rename the APK to .zip and extract it. Locate the following files:
lib/armeabi-v7a/libil2cpp.so– the IL2CPP native binaryassets/bin/Data/Managed/Metadata/global-metadata.dat– metadata containing type and string information
- Running IL2CppDumper
Create two directories (input and output) next to Il2CppDumper.exe. Copy the two extracted files into the input folder.
Create a batch file (e.g., run_dump.bat) with the following command:
..\Il2CppDumper.exe libil2cpp.so global-metadata.dat ..\output
Execute the batch file. The tool will process the binaries and generate output files in the output directory.
- Analyzing the Output
7.1 dump.cs
This file lists all classes, methods, and fields from the original C# code. For example:
// Namespace:
public class Greeter // TypeDefIndex: 1414
{
// Methods
public void .ctor() { }
public void SayHello() { }
public void Say(string message) { }
}
// Namespace:
public class AppController : MonoBehaviour // TypeDefIndex: 1415
{
// Fields
public int score;
protected float speed;
private string playerName;
public int[] highScores;
protected List<string> achievements;
private AppController.AppState currentState;
private Greeter greeter;
// Methods
public void .ctor() { }
private void Awake() { }
private void Initialize() { }
private void Start() { }
private IEnumerator UpdateScoreRoutine() { }
}
7.2 il2cpp.h
A C++ header file showing the memory layout of the managed structs:
struct Greeter_Fields {
};
struct AppController_Fields : UnityEngine_MonoBehaviour_Fields {
int32_t score;
float speed;
struct System_String_o* playerName;
struct System_Int32_array* highScores;
struct System_Collections_Generic_List_string__o* achievements;
int32_t currentState;
struct Greeter_o* greeter;
};
7.3 script.json
A JSON file containing method signatures and their addresses:
{
"Address": 3787608,
"Name": "Greeter$$.ctor",
"Signature": "void Greeter___ctor (Greeter_o* __this, const MethodInfo* method);"
},
{
"Address": 3787616,
"Name": "Greeter$$SayHello",
"Signature": "void Greeter__SayHello (Greeter_o* __this, const MethodInfo* method);"
},
{
"Address": 3787760,
"Name": "Greeter$$Say",
"Signature": "void Greeter__Say (Greeter_o* __this, System_String_o* message, const MethodInfo* method);"
},
{
"Address": 3787896,
"Name": "AppController$$.ctor",
"Signature": "void AppController___ctor (AppController_o* __this, const MethodInfo* method);"
},
{
"Address": 3788040,
"Name": "AppController$$Awake",
"Signature": "void AppController__Awake (AppController_o* __this, const MethodInfo* method);"
},
{
"Address": 3788044,
"Name": "AppController$$Initialize",
"Signature": "void AppController__Initialize (AppController_o* __this, const MethodInfo* method);"
},
{
"Address": 3788412,
"Name": "AppController$$Start",
"Signature": "void AppController__Start (AppController_o* __this, const MethodInfo* method);"
},
{
"Address": 3788576,
"Name": "AppController$$UpdateScoreRoutine",
"Signature": "System_Collections_IEnumerator_o* AppController__UpdateScoreRoutine (AppController_o* __this, const MethodInfo* method);"
}
7.4 stringliteral.json
Lists all string literals and their addresses in the binary:
{
"value": "Greetings from IL2CPP",
"address": "0x52A0AC"
},
{
"value": "Default",
"address": "0x52A0B0"
},
{
"value": "first",
"address": "0x52A0B4"
},
{
"value": "second",
"address": "0x52A0B8"
},
{
"value": "Guest",
"address": "0x52A0BC"
},
{
"value": "Score: ",
"address": "0x52A0C0"
}
7.5 DummyDll/Assembly-CSharp.dll
Inside the DummyDll folder, IL2CppDumper generates placeholder DLLs. The Assembly-CSharp.dll can be opened with ILSpy to vieew the types and methods (though method bodies are empty).
- Extending Analysis with IDA Pro
The generated dump.cs and dummy DLLs contain only method signatures, not the actual implementation. To examine function logic, use a disassembler like IDA Pro. Load the libil2cpp.so file into IDA (choose the 32‑bit version for ARMv7 binaries).
For example, to view the Awake method, look up its address from dump.cs (e.g., 0x39CD08). In IDA, press G and enter the address. The disassembly shows the native code. Press F5 to generate pseudo‑C code:
signed int __fastcall sub_39CD08(_DWORD *a1)
{
_DWORD *v1; // r4
int v2; // r5
int v3; // r5
int v4; // r5
int v5; // r0
int v6; // r0
signed int result; // r0
v1 = a1;
if ( !byte_52A52B )
{
sub_39688C(&elf_hash_chain[89]);
byte_52A52B = 1;
}
v1[3] = 1;
v1[4] = 0x40000000;
v2 = dword_524724;
sub_38CFC4(dword_524724);
v3 = il2cpp_array_new_specific_0(v2, 3);
sub_773C4(0, v3, dword_5277E8, 0);
v1[6] = v3;
v4 = sub_3CCA3C(dword_524EF4);
v5 = sub_303278(v4, dword_5262D8);
if ( v4 )
{
sub_304144(v4, dword_52A0B4, dword_5262E0);
}
else
{
((void (__fastcall *)(int))loc_3BF764)(v5);
v6 = sub_304144(0, dword_52A0B4, dword_5262E0);
((void (__fastcall *)(int))loc_3BF764)(v6);
}
sub_304144(v4, dword_52A0B8, dword_5262E0);
result = 1;
v1[7] = v4;
v1[8] = 1;
return result;
}
Although the generated C code is often heavily optimized, it reveals the original method’s structure.
- How IL2CppDumper Works
IL2CPP converts C# into C++, which is then compiled into native code for each platform. To support runtime features like garbage collection and reflection, the IL2CPP runtime relies on a metadata file (global-metadata.dat) that stores addresses of class names, method names, property names, and string literals. The IL2CPP runtime loads this file at startup; the loading logic can be found in GlobalMetadata.cpp inside the Unity Editor’s IL2CPP source (Editor/Data/il2cpp/libil2cpp/vm).
The global-metadata.dat file is a binary with a specific structure. Its header starts with the magic bytes AF 1B B1 FA (little‑endian 0xFAB11BAF). IL2CppDumper parses this file together with the ELF binary (libil2cpp.so) to reconstruct the original type metadata. The ELF header begins with 7F 45 4C 46 (0x464c457f in little‑endian), confirming it is an ELF file.
You can examine global-metadata.dat in a hex editor like 010 Editor. Using the UnityMetadata.bt template (available from Sweetscape’s template repository) makes the structure readable. However, string values are only retrievable when combined with libil2cpp.so, as the metadata file stores offsets rather then actual strings.
The main Dump method in IL2CppDumper’s source code takes both the metadata and the IL2CPP binary as input, performs ELF parsing, and finally writes the output files described above.
- Final Remarks
This guide covered the full workflow of extracting type information from Unity IL2CPP builds using IL2CppDumper, along with supplementary techniques for deeper analysis. The same principles apply to iOS builds where the libil2cpp.a or FAT binary is used.