| Crates.io | il2cpp_rs |
| lib.rs | il2cpp_rs |
| version | 0.1.3 |
| created_at | 2025-11-08 22:05:18.734869+00 |
| updated_at | 2025-11-11 18:50:13.893766+00 |
| description | A library for interacting with il2cpp on Windows |
| homepage | https://github.com/ElCapor/il2cpp_rs |
| repository | https://github.com/ElCapor/il2cpp_rs |
| max_upload_size | |
| id | 1923329 |
| size | 137,321 |
This repo is currently in development and is not ready for production use.
Windows ONLY
I wrote it in a single day with 15 hours of work.
A lightweight Rust library for discovering and navigating IL2CPP metadata at runtime. It provides a safe-ish Rust façade over the IL2CPP C API, and builds a cache of assemblies, classes, fields, and methods with a modern ownership model for convenient querying and printing.
Note: This repo targets Windows for now and requires using an injected DLL entry for example (
DllMain) to attach to a running IL2CPP process (e.g., a Unity game).
il2cpp::il2cpp_sys) and ergonomic wrappers (il2cpp module)Arc shared handles for nodes (Assembly, Class, Field, Method, Type)Weak back-references (e.g., Field/Method → Class) to avoid cyclesRwLock<Vec<...>> in Classprofile_scope!, profile_call!)src/il2cpp/il2cpp_sys: raw FFI to IL2CPP exports (pointers, C-strings). All low-level Il2Cpp* are represented as *mut u8 handles.src/il2cpp/mod.rs: safe-ish wrappers around the FFI that return Rust types (e.g., String, Vec<...>), and helper functions like:
get_domain, thread_attach/thread_detachdomain_get_assemblies, assembly_get_imageimage_get_class_count, image_get_classclass_get_name/namespace/parentclass_get_fields, field_get_name/offset/typeclass_get_methods, method_get_name/flags/params/return_typesrc/il2cpp/classes: high-level Rust model types used in the cache
Class = Arc<ClassInner>
fields: RwLock<Vec<Field>>methods: RwLock<Vec<Method>>Field = Arc<FieldInner>
class: Weak<ClassInner> backrefMethod = Arc<MethodInner>
class: Weak<ClassInner> backref, return_type: TypeType = Arc<TypeInner> (cacheable handle with address, name, size)src/il2cpp_cache.rs: metadata discovery and hydration into the high-level types
Cache::parse_assemblies(domain)Cache::parse_class(&mut Assembly, image)Cache::parse_fields(&Class) (populates fields)Cache::parse_methods(&Class) (populates methods)Assembly → Vec<Class>Class → RwLock<Vec<Field>>, RwLock<Vec<Method>>Field.class, Method.class → Weak<ClassInner>Class ↔ Field/Method)Arc clones)RwLock)The crate exposes a tiny profiling module for quick ad-hoc timing in dev builds.
profile_scope!("Cache::new");
// code to profile...
let cache = profile_call!("Cache::new", Cache::new(domain));
Printed output example:
Cache::new took 1.23ms
Implementation: see src/prof.rs (ScopeTimer) and macros exported at crate root.
rustup toolchain install stable
cargo build
cargo build
use il2cpp_rs::il2cpp;
use il2cpp_rs::il2cpp_cache::Cache;
fn entry_point() -> Result<(), String> {
il2cpp::init("GameAssembly.dll")?;
let domain = il2cpp::get_domain()?;
il2cpp::thread_attach(domain)?;
// the cache is the structure that contains all the assemblies, classes, fields, and methods
let cache = Cache::new(domain)?;
// Debug printing is available via Debug impls
// println!("{:?}", cache);
Ok(())
}
*mut u8) from IL2CPP. Access patterns assume the underlying engine keeps these pointers valid while attached to the domain.thread_attach).String immediately (already handled by wrappers).Arc/Weak handles are Send/Sync only insofar as the contained data is. The raw pointer addresses are opaque and not dereferenced in safe code.cargo fmt and cargo clippy on changesGNU General Public License v3.0. See LICENSE.MD for details.