# Honkai: Star Rail In-Process Dumper (`honkai-dumper`) Architectural Specification & Deep Dive
This technical specification provides an exhaustive, code-level analysis of **`honkai-dumper`** (by AzenKain / lanylow), an in-process, native Rust dynamic link library (`cdylib`) engineered to extract metadata, field layouts, and compiled method Relative Virtual Addresses (RVAs) from the live process memory of *Honkai: Star Rail* (`StarRail.exe`).
---
## 1. Executive Summary & Design Rationale
### 1.1 The Fundamental Problem of Offline IL2CPP Dumpers
In conventional Unity IL2CPP games, compiled assemblies exist as two distinct artifacts:
1.`GameAssembly.dll`: Native machine code (PE x86_64) resulting from C# $\rightarrow$ C++ $\rightarrow$ Clang compilation.
2.`global-metadata.dat`: Binary metadata file containing string literals, type definitions, method signatures, and field offset tables.
In standard environments, tools like **Il2CppDumper** or **Il2CppInspector** parse `global-metadata.dat` statically on disk. However, modern live-service games protected by commercial packers/DRM implement active anti-reversing countermeasures:
- **Encrypted Metadata**: `global-metadata.dat` is heavily obfuscated and encrypted on disk; static extractors fail immediately without proprietary decryption keys.
- **Stripped API Exports**: `GameAssembly.dll` strips all standard IL2CPP C-API exports (e.g., `il2cpp_domain_get`, `il2cpp_class_get_methods`), rendering standard dynamic import resolution (`GetProcAddress`) ineffective.
### 1.2 The In-Process Dumper Solution
`honkai-dumper` bypasses disk-level metadata encryption entirely by operating **inside the game process space** as an injected native module (`.dll`):
- It delays execution until the game engine has completed unpacking, decrypted metadata, and instantiated the internal runtime structures in memory.
- Instead of relying on exported symbols in `GameAssembly.dll`, it utilizes **signature pattern scanning** inside `UnityPlayer.dll` to locate the hidden global table of IL2CPP API function pointers.
- It walks the live memory graph (`Domain` $\rightarrow$ `Assembly` $\rightarrow$ `Image` $\rightarrow$ `Class` $\rightarrow$ `Method` / `Field`), emitting reconstructed C# header definitions (`dump.cs`) and method RVA address maps (`methods.json`) ready for IDA Pro, Ghidra, or Binary Ninja.
## 3. Initialization Lifecycle & Process Synchronization
### 3.1 Non-Blocking DLL Attachment Pattern
Standard Windows DLL injection executes the payload inside `DllMain` during `DLL_PROCESS_ATTACH`. Executing heavy workloads or blocking synchronization inside `DllMain` violates Windows loader lock contracts and causes deadlocks.
`honkai-dumper` isolates injection lifecycle via `src/base.rs`:
```rust
usector::ctor;
usewinapi::um::consoleapi;
usestd::{error::Error,thread,time::Duration};
usecrate::outputs::{csdumper,methoddumper};
#[ctor]
fnentry(){
thread::spawn(||{
init().unwrap_or_else(|e|{
panic!("honkai-dumper encountered an error: {}",e)
});
});
}
```
1.**`#[ctor]` Injection**: The `ctor` macro generates a C-runtime startup initializer function.
2.**`thread::spawn`**: Immediately offloads execution to an isolated OS thread, returning control to the Windows loader instantly and averting loader lock deadlocks.
3.**Engine Warmup Delay**:
```rust
fn init() -> Result<(), Box<dyn Error>> {
thread::sleep(Duration::from_secs(10));
unsafe { consoleapi::AllocConsole() };
// ...
}
```
A fixed sleep of 10 seconds is imposed. This provides the host game process sufficient wall-clock time to:
- Load and decrypt secondary DLLs (`GameAssembly.dll`).
- Run native unpackers and decrypt internal `global-metadata.dat` structures into RAM.
- Execute IL2CPP runtime initialization routines (`il2cpp_init`) and register class domains.
4. **Console Allocation**: Calls Win32 `AllocConsole()` to attach a dedicated Win32 terminal window to the host process for operator feedback.
---
## 4. The Signature Scanning Engine (`utils.rs` & `api.rs`)
### 4.1 Locating Stripped IL2CPP Function Pointers
Because `GameAssembly.dll` exports are stripped, `honkai-dumper` leverages the structural architecture of the Unity Engine: `UnityPlayer.dll` acts as the engine host and holds a dispatch table of internal IL2CPP functions passed or registered during engine startup.
### 4.2 The x86_64 Machine Code Pattern
In `src/il2cpp/utils.rs`, the tool scans `UnityPlayer.dll` on disk using the following byte pattern:
```
48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? FF D0
```
#### Disassembly & Instruction Breakdown:
```nasm
48 8B 05 xx xx xx xx ; mov rax, [rip + disp32_1] <-- Loads target function table pointer
x86_64 uses relative instruction pointer (RIP) addressing. The displacement stored in the machine instruction is relative to the address of the **subsequent** instruction.
`honkai-dumper` resolves this with mathematical precision:
for i in 0..=data.len().saturating_sub(pattern.len()) {
if match_pattern(data, &pattern, i) {
let rva = file_offset_to_rva(i as u32, pe)?;
let va = base_addr + rva as u64;
let rel_offset = i + 3; // Offset to 4-byte displacement in `mov rax, [rip + disp32]`
let disp = i32::from_le_bytes(data[rel_offset..rel_offset + 4].try_into().unwrap());
let rip_after_mov = va + 7; // Length of `mov rax, [rip + disp32]` is 7 bytes
let qword_addr = rip_after_mov.wrapping_add(disp as u64);
return Some((qword_addr - base_addr) as usize);
}
}
None
}
```
1. **File Offset $\rightarrow$ RVA Conversion**: Iterates PE section headers via `pelite` to map raw file offset $i$ to section Virtual Address.
2. **Displacement Extraction**: Reads 32-bit signed little-endian integer (`i32`) from offset `i + 3`.
3. **Effective Address**: Adds displacement to $RIP_{\text{next}} = VA + 7$.
4. **Base Address Normalization**: Subtracts image base to return the target table RVA relative to `UnityPlayer.dll`.
---
## 5. IL2CPP Function Table Jump Indices (`functions.rs`)
Once the table RVA is resolved, `honkai-dumper` treats the memory address inside the running `UnityPlayer.dll` as an array of raw function pointers:
```rust
let functions = Il2CppFunctions::new(unity_player.handle as usize + qword);
```
### 5.1 Function Pointer Dereferencing Wrapper
To safely call untyped C function pointers, `src/il2cpp/module.rs` defines a zero-cost wrapper `FunctionPtr<T>` utilizing Rust's `Deref` trait and `PhantomData`:
```rust
pub struct FunctionPtr<T> {
pub ptr: *const c_void,
pd: PhantomData<T>
}
impl<T> Deref for FunctionPtr<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*(&self.ptr as *const *const _ as *const T) }
}
}
```
### 5.2 The Index Map
`src/il2cpp/functions.rs` maps array offsets using the `index!` macro:
```rust
macro_rules! index {
($start:expr, $index:expr) => {{
let addr = unsafe { *$start.offset($index) };
if addr.is_null() { None } else { Some(FunctionPtr::new(addr)) }
}};
}
```
The specific indices corresponding to the global HSR Unity release:
| Table Index | Function Signature | Description |
| :---: | :--- | :--- |
| **22** | `il2cpp_assembly_get_image(*const Il2CppAssembly) -> *const Il2CppImage` | Retrieves primary PE image from assembly |
| **31** | `il2cpp_class_get_fields(*const Il2CppClass, *const *const c_void) -> *const FieldInfo` | Iterates member fields of a class |
| **169** | `il2cpp_image_get_class_count(*const Il2CppImage) -> usize` | Total number of classes in image |
| **170** | `il2cpp_image_get_class(*const Il2CppImage, usize) -> *const Il2CppClass` | Retrieves class definition by index |
> [!WARNING] Version Fragility
> Because these table indices represent direct slots inside an internal dispatch table in `UnityPlayer.dll`, **any major Unity version update or custom engine compilation will shift these indices**. If the game client updates its Unity minor/patch version, the array offsets must be re-verified against a disassembly of `UnityPlayer.dll`.
In JIT or native code hooking engines, pointers may point to trampolines, dynamically generated stubs, or external modules. `methoddumper.rs` enforces strict pointer sanity checking:
(pointer > il2cpp.game_assembly.handle as usize) &&
(pointer < il2cpp.game_assembly.handle as usize + il2cpp.game_assembly.size)
}
```
Only method pointers strictly located within the memory boundary of `GameAssembly.dll` ($\text{Base} \le \text{Ptr} < \text{Base} + \text{SizeOfImage}$) are treated as genuine compiled game methods.
In C#, method overloading allows multiple methods to share the same identifier with different parameter signatures. To generate a flat JSON dictionary without key collisions, `methoddumper.rs` implements a collision tracker:
```rust
let method_name = il2cpp.method_get_name(method_info)?;
let description = format!("{}{}::{}", class_namespace, class_name, method_name);
let unique_description = if name_map.contains_key(&description) {
let count = duplicates.entry(description.to_string()).or_insert(0);
*count += 1;
format!("{}_{}", description, count)
} else {
description
};
name_map.insert(unique_description, pointer - il2cpp.game_assembly.handle as usize);
```
### 8.2 JSON Output Schema (`methods.json`)
The resulting file is formatted as a key-value mapping of fully qualified method signatures to hexadecimal RVAs:
| **Execution Vector** | Native DLL injection into running process | Standalone CLI executable analyzing disk files |
| **Metadata Protection** | **Immune**: Reads memory after unpacker decrypts metadata | **Fails**: Blocked by custom encryption on `global-metadata.dat` |
| **Symbol Stripping** | **Immune**: Locates API table via signature scan in `UnityPlayer.dll` | Requires manual IDA script or pattern search if exports are stripped |
| **Anti-Cheat Risk** | **High**: Process injection can trigger game integrity checks/watchdogs | **Zero**: Entirely offline |
| **Build Stability** | Fragile: Dependent on fixed jump table index offsets in `functions.rs` | Robust: Parses standard metadata format specifications |
- Network packet dispatchers often use obfuscated command handlers.
- By searching `dump.cs` for classes implementing network interfaces or containing `CmdId` constants, engineers can pinpoint the exact RVA of packet deserialization routines.
- Cross-referencing `methods.json` RVAs with dynamic breakpoints allows live tracing of packet encryption keys, seed exchanges, and payload masking expressions.
### 10.3 Compliance with Repository Rules (`AGENTS.md`)
- `honkai-dumper` is a one-shot diagnostic utility executed in an isolated thread (`thread::spawn`) on injection, writing results and terminating.
- In contrast to prohibited persistent runtime probes, it does **not** attach to per-frame update loops or main thread heartbeats, adhering to the principle of zero in-game performance degradation.
- No local machine paths, static IPs, or client versions are hardcoded in the codebase. All memory resolution is performed dynamically relative to `GetModuleInformation` bases.