il2cpp-bridge-rs is a Rust library for exploring and interacting with Unity's IL2CPP runtime from native code. It resolves IL2CPP exports at runtime, builds a metadata cache, and exposes ergonomic wrappers for common tasks such as class lookup, method invocation, metadata dumping, and Unity object access.
This crate is aimed at engineers who are already working inside a native environment where Unity and IL2CPP are loaded:
- native plugins and injected libraries
- runtime tooling and diagnostics
- IL2CPP introspection utilities
- Unity wrappers built on top of raw IL2CPP metadata
If you need a general Unity mod loader or a standalone game patching framework, this crate is a building block rather than a complete solution.
Before using the API, keep these constraints in mind:
- A live IL2CPP runtime must already be loaded in the current process.
- You must call
initbefore relying on cache-backed lookups such asapi::cache::csharp(). - Many operations ultimately work with raw pointers and runtime-owned memory. The crate reduces footguns, but it does not make IL2CPP integration fully safe.
- Method calls, field access, and object wrappers only make sense while the target Unity runtime is alive and compatible with the expected metadata.
- Some operations require the current thread to be attached to the IL2CPP VM. Use
api::Threadwhen you are doing work outside the initialization callback.
cargo add il2cpp-bridge-rsThe repo includes a Makefile with common workflows:
make build
make check
make clippy
make docPlatform-specific targets are also available, including make build-ios, make build-macos, make build-linux, make build-android, and make build-windows.
The snippet below shows the intended happy path: initialize the runtime, fetch a cached assembly, resolve a class, inspect a method, and call an instance method if an object is available.
This example is illustrative. It compiles as Rust, but it requires a live Unity IL2CPP runtime to do anything useful.
use il2cpp_bridge_rs::{api, init};
use std::ffi::c_void;
init("GameAssembly", || {
let asm = api::cache::csharp();
let player_class = asm
.class("PlayerController")
.expect("PlayerController should exist after cache hydration");
let damage_method = player_class
.method(("TakeDamage", ["System.Single"]))
.expect("TakeDamage(float) should exist");
println!(
"Resolved {}::{} at RVA 0x{:X}",
player_class.name,
damage_method.name,
damage_method.rva
);
if let Some(player) = player_class.find_objects_of_type(false).into_iter().next() {
let bound_method = player
.method(("TakeDamage", ["System.Single"]))
.expect("instance method should bind automatically");
let damage: f32 = 25.0;
unsafe {
let _: Result<(), _> =
bound_method.call(&[&damage as *const f32 as *mut c_void]);
}
}
});The markdown guides explain workflows and caveats. Generated rustdoc should be treated as the source of truth for exact signatures.
To build rustdoc locally:
cargo doc --no-depsMost users will spend time in these APIs first:
initapi::cacheapi::Threadapi::invoke_method- dump helpers such as
api::dumpandapi::dump_all_to - wrappers such as
api::Applicationandapi::Time - metadata and object wrappers under
structs
The project currently ships build targets for:
- iOS:
aarch64-apple-ios - macOS:
aarch64-apple-darwin - Linux:
x86_64-unknown-linux-gnu - Android:
aarch64-linux-android - Windows:
x86_64-pc-windows-msvc
See Platform and Runtime Notes for symbol resolution details, target image naming guidance, and thread/runtime caveats.
Contribution expectations and local development workflow are documented in CONTRIBUTING.md.
This library is provided strictly for educational and research purposes. It is intended to facilitate learning about IL2CPP internals, reverse engineering concepts, and Rust-based interop with native applications.