A Zygisk-free, root-free il2cpp runtime dumper that runs inside the target
process itself. load it via system.loadlibrary() and it automatically finds
libil2cpp.so, resolves all Unity il2cpp APIs, and writes a dump.cs to the
app's own files directory.
Extracts a C#‑like pseudo‑source of every managed type (classes, structs, enums, interfaces) along with field offsets and method addresses, directly from the live IL2CPP runtime memory.
- Special Advantages
1.1 Encrypted Metadata Support
1.2 Container / Virtual Machine Native Support
1.3 Crash-Safe Operation
1.4 Zero Impact on App Behaviour
1.5 Multi-Strategy Enumeration - Limitations
- Security Perspective
3.1 What This Library Can Access
3.2 What This Library Does NOT Do
3.3 Policy Compliance - Output Locations & Fallback Order
- Customising the Output Path
- Usage Guide
6.1 Prerequisites
6.2 Build Instructions
6.3 Implementation Instructions - Acknowledgments
The library does not decrypt anything. It waits until IL2CPP itself has loaded the decrypted metadata into memory, then dumps the live state. Works with any encryption scheme — no key extraction, no file parsing.
🔥 Special Feature Works inside any container or virtual machine. Dumps official apps/games without tampering their integrity.
- Implement the library inside the container
- Clone and run the target app inside the container
- The
dump.csis written directly to a shared folder, accessible from the host
All IL2CPP calls are wrapped with a signal handler (SIGSEGV/SIGBUS) on an alternate stack. If a function pointer is wrong, the crash is caught, logged, and the dump thread exits cleanly — the host app never crashes.
- ❎ No method hooking
- ❎ No bytecode modification
- 🧵 The dump thread runs detached at default
SCHED_OTHERpriority - ❄️ All polling loops use
sleep(1)— no CPU busy‑wait, no battery drain - 😑 The library loads as a passive observer, the game’s FPS, network, and UI are unaffected
The dumper automatically tries three enumeration methods, failing gracefully to the next if one is unavailable:
- Image‑based –
il2cpp_image_get_class(fast, well‑structured) - Callback‑based –
il2cpp_class_for_each(newer IL2CPP builds) - Reflection‑based –
Assembly.Load+GetTypes()(slow but guaranteed to work even on stripped/obfuscated builds)
This guarantees a successful dump on Unity versions from 2017.4 to the latest 6000.x releases.
- Generic type argument display: Generic classes appear as
List\1notList. Full instantiation details requireIl2CppGenericInst` parsing, which is planned but not yet implemented. - XOR/custom‑encrypted runtime symbols: If
methodPointervalues are obfuscated in memory (not just the metadata file), the RVA/VA comments will show incorrect addresses. - Unity versions before 2017.1: Very old IL2CPP ABIs may have different struct layouts; in such cases the library produces a best‑effort partial dump.
- Deobfuscation: The dumper does not rename symbols, decrypt strings, or restore control flow. It outputs raw metadata exactly as the engine sees it.
The library operates entirely within the existing security boundary of the target process. It:
- ✅ Reads
/proc/self/mapsand/proc/self/cmdline— accessible to every process by design - ✅ Reads the process’s own mapped memory — it is inside the process
- ✅ Writes to the app’s own files directory — storage the process already owns
- ✅ Makes JNI calls using the app’s own
JavaVM— standard Android API
No system call is made that the app itself could not make.
- ❌ Does not escalate privileges
- ❌ Does not communicate over the network
- ❌ Does not access other apps’ data
- ❌ Does not modify any memory (no hooks, no patches, no code injection)
- ❌ Does not persist after the process exits
- ❌ Does not request additional Android permissions
Because the library loads as part of the app’s own process under the app’s UID, it operates entirely within Android’s standard application sandbox. No security policy is violated. The library is functionally equivalent to an app examining its own runtime state — a normal and permitted operation.
The library tries the following directories in order. If one fails (e.g., permission denied, mount namespace issue), it moves to the next. The final file is always dump.cs.
| Step | Strategy | Example path |
|---|---|---|
| 1 | JNI Context.getFilesDir() |
/data/data/<pkg>/files/il2cpp_dump/ |
| 2 | Hardcoded /data/data/<pkg>/files/ |
/data/data/<pkg>/files/il2cpp_dump/ |
| 3 | /sdcard/il2cpp_dump/ (package unknown) |
/sdcard/il2cpp_dump/ |
| 4* | Scoped external storage (Android 10+) | /sdcard/Android/data/<pkg>/files/il2cpp_dump/ |
| 5* | Full emulated path | /storage/emulated/0/Android/data/<pkg>/files/il2cpp_dump/ |
| 6* | Final /sdcard fallback |
/sdcard/il2cpp_dump/ |
*Steps 4‑6 are only attempted if the primary fopen() fails, using the package name extracted from the original path. They are secondary fallbacks inside the dump function itself.
You can hardcode a custom directory by editing jni_entry.c.
For example, to always write to:
/storage/emulated/0/Android/data/com.example.vm/rootfs/storage/emulated/0/il2cpp_dump/<package>/dump.cs
Replace the output_dir construction block in the dump_thread() function with:
char output_dir[PATH_MAX] = {0};
if (pkg[0] && strcmp(pkg, "unknown") != 0) {
snprintf(output_dir, sizeof(output_dir),
"/storage/emulated/0/Android/data/com.example.vm/rootfs"
"/storage/emulated/0/il2cpp_dump/%s", pkg);
mkdir_p(output_dir);
LOGI("Output dir (custom): %s", output_dir);
} else {
snprintf(output_dir, sizeof(output_dir),
"/storage/emulated/0/Android/data/com.example.vm/rootfs"
"/storage/emulated/0/il2cpp_dump/unknown");
mkdir_p(output_dir);
LOGW("Output dir (custom, unknown pkg): %s", output_dir);
}Rebuild the library and the dump will appear exactly at that hardcoded path.
- Android NDK (for native compilation)
- Termux app (for on-device building)
💡 Tip: If you encounter issues with NDK builds or Termux setup, you can use the GitHub Actions workflow to auto-compile the source — no manual setup needed, handles dependencies, and builds for all architectures.
# Clone the repository
git clone https://github.com/muhammadrizwan87/il2cppdumper.git
cd il2cppdumper
# Set NDK path
export NDK_HOME=/path/to/your/ndk
# export NDK_HOME=/data/data/com.termux/files/home/android-sdk/ndk/24.0.8215888
# Build for all architectures
$NDK_HOME/ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=./jni/Application.mk
# Output will be in libs/
ls libs/
# armeabi-v7a/ arm64-v8a/ x86/ x86_64/The native library must be loaded as early as possible — ideally in the app’s Application class static initialiser or its Smali equivalent.
1. Smali code to load the library:
.method static constructor <clinit>()V
.registers 1
const-string v0, "il2cppdumper"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method2. Add the native libraries:
lib/armeabi-v7a/libil2cppdumper.so
lib/arm64-v8a/libil2cppdumper.so
lib/x86/libil2cppdumper.so
lib/x86_64/libil2cppdumper.so
1. Add to your Application class:
public class MyApp extends Application {
static {
System.loadLibrary("il2cppdumper"); // note: correct spelling from Android.mk
}
@Override
public void onCreate() {
super.onCreate();
// Dump starts automatically when library loads
}
}2. Update build.gradle:
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}Built on the foundation of Zygisk-Il2CppDumper by Perfare.