il2cpp_rs 0.1.1

A library for interacting with il2cpp on Windows
![Crates.io Version](https://img.shields.io/crates/v/il2cpp_rs)

# Note


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.
# il2cpp_rs


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).

---

## Features


- IL2CPP API bindings (`il2cpp::il2cpp_sys`) and ergonomic wrappers (`il2cpp` module)
- Discovery of:
  - Assemblies → Classes → Fields → Methods
  - Method signatures: name, flags, parameters, return type
  - Field metadata: name, offset, static-ness, type
- Modern ownership model for metadata graph:
  - `Arc` shared handles for nodes (`Assembly`, `Class`, `Field`, `Method`, `Type`)
  - `Weak` back-references (e.g., `Field`/`Method``Class`) to avoid cycles
  - Thread-safe collections via `RwLock<Vec<...>>` in `Class`
- Minimal profiling utilities to time code paths (`profile_scope!`, `profile_call!`)

---

## Architecture Overview


- `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_detach`
  - `domain_get_assemblies`, `assembly_get_image`
  - `image_get_class_count`, `image_get_class`
  - `class_get_name`/`namespace`/`parent`
  - `class_get_fields`, `field_get_name`/`offset`/`type`
  - `class_get_methods`, `method_get_name`/`flags`/`params`/`return_type`
- `src/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>` backref
  - `Method = Arc<MethodInner>`
    - `class: Weak<ClassInner>` backref, `return_type: Type`
  - `Type = 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`)

---

## Ownership Model


- Strong edges (Arc):
  - `Assembly``Vec<Class>`
  - `Class``RwLock<Vec<Field>>`, `RwLock<Vec<Method>>`
- Weak edges (Weak):
  - `Field.class`, `Method.class``Weak<ClassInner>`
- Benefits:
  - Avoid cycles (`Class ↔ Field/Method`)
  - Safe cloning of handles (cheap `Arc` clones)
  - Thread-safe reads and targeted writes (`RwLock`)

---

## Profiling


The crate exposes a tiny profiling module for quick ad-hoc timing in dev builds.

- Scope-based timing:

```rust
profile_scope!("Cache::new");
// code to profile...
```

- Single expression timing with result preserved:

```rust
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.

---

## Building


- Install and build:

```bash
rustup toolchain install stable
cargo build
```

- Standard dev build:

```bash
cargo build
```



---

## Running / Example


Example usage of the library in a dll at [https://github.com/ElCapor/il2cpp_rs-example-dll](https://github.com/ElCapor/il2cpp_rs-example-dll)
---

## Example: Discover and print classes


```rust
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(())
}
```

---

## Safety Notes


- The FFI layer manipulates raw pointers (`*mut u8`) from IL2CPP. Access patterns assume the underlying engine keeps these pointers valid while attached to the domain.
- Do not send handles across threads unless you’ve attached those threads to the IL2CPP domain (`thread_attach`).
- Avoid storing borrowed C-string pointers; convert to Rust `String` immediately (already handled by wrappers).
- All `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.

---

## Contributing


- Run `cargo fmt` and `cargo clippy` on changes
- Keep FFI wrappers minimal and well-commented
- Preserve the Arc/Weak ownership model and avoid re-introducing cycles

---

## License

GNU General Public License v3.0. See [LICENSE.MD](LICENSE.MD) for details.