451 lines
24 KiB
Markdown
451 lines
24 KiB
Markdown
# 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.
|
|
|
|
```
|
|
+----------------------------------------------------------------------------------------------------+
|
|
| Game Process Address Space |
|
|
+----------------------------------------------------------------------------------------------------+
|
|
| |
|
|
| [ UnityPlayer.dll ] [ GameAssembly.dll ] |
|
|
| +-------------------------------------+ +-------------------------------+ |
|
|
| | Pattern: 48 8B 05 .. 48 8D 0D .. | | Native Machine Code Functions | |
|
|
| | | | | (Compiled from C# Methods) | |
|
|
| | v | +---------------+---------------+ |
|
|
| | RIP-relative calculation | ^ |
|
|
| | | | | |
|
|
| | v | | Method Pointers |
|
|
| | Internal Function Pointer Table | | (VAs / RVAs) |
|
|
| | [0] = null | | |
|
|
| | [22] = il2cpp_assembly_get_image | | |
|
|
| | [35] = il2cpp_class_get_methods | | |
|
|
| | [63] = il2cpp_domain_get | | |
|
|
| +-------------------+-----------------+ | |
|
|
| | | |
|
|
+----------------------|----------------------------------------------------------|------------------+
|
|
| Resolves Function Pointers |
|
|
v |
|
|
+---------------------------------------------------------------------------------+------------------+
|
|
| honkai-dumper.dll (Injected Module) |
|
|
+----------------------------------------------------------------------------------------------------+
|
|
| 1. entry() [#[ctor]] -> Spawns worker thread |
|
|
| 2. thread::sleep(10s) -> Awaits runtime initialization & metadata decryption |
|
|
| 3. AllocConsole() -> Interactive logging |
|
|
| 4. Traverser: Domain -> Assemblies -> Images -> Classes -> Fields & Methods |
|
|
| 5. Serialization: |
|
|
| * dump.cs : Reconstructed C# class headers with field offsets and method RVAs |
|
|
| * methods.json : JSON dictionary mapping "Namespace.Class::Method" -> "0xRVA" |
|
|
+----------------------------------------------------------------------------------------------------+
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Codebase Architecture & File Organization
|
|
|
|
The project is implemented in pure Rust (2021 Edition) configured as a Windows C dynamic library (`crate-type = ["cdylib"]`).
|
|
|
|
```
|
|
honkai-dumper/
|
|
├── Cargo.toml # cdylib target configuration, winapi, pelite, serde_json, ctor
|
|
├── src/
|
|
│ ├── lib.rs # Module root (base, il2cpp, outputs)
|
|
│ ├── base.rs # DLL entry point, thread creation, 10s synchronization delay
|
|
│ ├── il2cpp/
|
|
│ │ ├── mod.rs # Re-exports submodules
|
|
│ │ ├── types.rs # Raw C-ABI memory representations (MethodInfo, Il2CppClass, etc.)
|
|
│ │ ├── constants.rs # ECMA-335 CLI metadata attribute masks and flags
|
|
│ │ ├── module.rs # Win32 module loader & generic FunctionPtr<T> wrapper
|
|
│ │ ├── utils.rs # PE scanner & RIP-relative displacement pattern matcher
|
|
│ │ ├── functions.rs # Il2CppFunctions table index mapping (array offsets)
|
|
│ │ └── api.rs # High-level safe wrapper over runtime API pointers
|
|
│ └── outputs/
|
|
│ ├── mod.rs # Output format definitions
|
|
│ ├── methoddumper.rs # Method RVA JSON exporter (methods.json)
|
|
│ └── csdumper.rs # C# AST reconstruction exporter (dump.cs)
|
|
```
|
|
|
|
### 2.1 Dependencies & Their Technical Roles
|
|
|
|
| Crate | Version | Architectural Role |
|
|
| :--- | :--- | :--- |
|
|
| `ctor` | `0.2.8` | Emits `#[ctor]` constructor function into `.CRT$XCU` section for execution on DLL load. |
|
|
| `pelite` | `0.10.0` | High-performance, zero-allocation PE64 file format parser for section headers and RVA conversion. |
|
|
| `winapi` | `0.3.9` | Windows API bindings for `libloaderapi`, `consoleapi`, `psapi`, and `processthreadsapi`. |
|
|
| `serde_json` | `1.0.116` | Serialization engine for generating formatted `methods.json`. |
|
|
| `thiserror` | `1.0.59` | Type-safe error enumeration derivation. |
|
|
|
|
---
|
|
|
|
## 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
|
|
use ctor::ctor;
|
|
use winapi::um::consoleapi;
|
|
use std::{error::Error, thread, time::Duration};
|
|
use crate::outputs::{csdumper, methoddumper};
|
|
|
|
#[ctor]
|
|
fn entry() {
|
|
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
|
|
48 8D 0D yy yy yy yy ; lea rcx, [rip + disp32_2] <-- Loads context argument into RCX
|
|
FF D0 ; call rax <-- Indirect call to IL2CPP initialization
|
|
```
|
|
|
|
### 4.3 RIP-Relative Displacement Calculation
|
|
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:
|
|
|
|
$$\text{RVA}_{\text{target}} = \text{RVA}_{\text{instruction}} + \text{Len}_{\text{instruction}} + \text{disp32}$$
|
|
|
|
In `utils.rs`:
|
|
```rust
|
|
pub fn find_qword_addr(data: &[u8], base_addr: u64, pe: &PeFile) -> Option<usize> {
|
|
let pattern = pattern_to_bytes("48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? FF D0");
|
|
|
|
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 |
|
|
| **33** | `il2cpp_class_get_interfaces(*const Il2CppClass, *const *const c_void) -> *const Il2CppClass` | Iterates implemented interfaces |
|
|
| **35** | `il2cpp_class_get_methods(*const Il2CppClass, *const *const c_void) -> *const MethodInfo` | Iterates class methods |
|
|
| **37** | `il2cpp_class_get_name(*const Il2CppClass) -> *const c_char` | Class name string |
|
|
| **39** | `il2cpp_class_get_namespace(*const Il2CppClass) -> *const c_char` | Class namespace string |
|
|
| **40** | `il2cpp_class_get_parent(*const Il2CppClass) -> *const Il2CppClass` | Base inheritance class |
|
|
| **43** | `il2cpp_class_is_valuetype(*const Il2CppClass) -> bool` | Checks if type is struct / ValueType |
|
|
| **45** | `il2cpp_class_get_flags(*const Il2CppClass) -> i32` | CLI TypeDef attribute flags |
|
|
| **49** | `il2cpp_class_from_type(*const Il2CppType) -> *const Il2CppClass` | Resolves class definition from type ref |
|
|
| **53** | `il2cpp_class_is_enum(*const Il2CppClass) -> bool` | Checks if type is Enum |
|
|
| **63** | `il2cpp_domain_get() -> *const Il2CppDomain` | Returns root AppDomain pointer |
|
|
| **65** | `il2cpp_domain_get_assemblies(*const Il2CppDomain, *const usize) -> *const *const Il2CppAssembly` | Returns array of loaded assemblies |
|
|
| **72** | `il2cpp_field_get_flags(*const FieldInfo) -> i32` | CLI Field attribute flags |
|
|
| **73** | `il2cpp_field_get_name(*const FieldInfo) -> *const c_char` | Field identifier name |
|
|
| **75** | `il2cpp_field_get_offset(*const FieldInfo) -> usize` | Memory offset within instance/static buffer |
|
|
| **76** | `il2cpp_field_get_type(*const FieldInfo) -> *const Il2CppType` | Field type definition |
|
|
| **116** | `il2cpp_method_get_return_type(*const MethodInfo) -> *const Il2CppType` | Method return type |
|
|
| **117** | `il2cpp_method_get_name(*const MethodInfo) -> *const c_char` | Method identifier name |
|
|
| **123** | `il2cpp_method_get_param_count(*const MethodInfo) -> u32` | Parameter count |
|
|
| **124** | `il2cpp_method_get_param(*const MethodInfo, u32) -> *const Il2CppType` | Parameter type at index |
|
|
| **161** | `il2cpp_type_get_name(*const Il2CppType) -> *const c_char` | Full CLR type name |
|
|
| **162** | `il2cpp_type_is_byref(*const Il2CppType) -> bool` | Checks `ref` / pointer indirection |
|
|
| **163** | `il2cpp_type_get_attrs(*const Il2CppType) -> u32` | Param/Type attribute flags (`[In]`, `[Out]`) |
|
|
| **168** | `il2cpp_image_get_name(*const Il2CppImage) -> *const c_char` | Assembly module name (e.g. `Assembly-CSharp.dll`) |
|
|
| **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`.
|
|
|
|
---
|
|
|
|
## 6. Runtime Metamodel & Memory Layout (`types.rs` & `api.rs`)
|
|
|
|
### 6.1 Native `MethodInfo` Structure Representation
|
|
In IL2CPP runtime architecture, every compiled managed method is represented by a C struct. In `src/il2cpp/types.rs`, `MethodInfo` is declared as:
|
|
|
|
```rust
|
|
#[repr(C)]
|
|
pub struct MethodInfo {
|
|
pub klass: *const Il2CppClass,
|
|
pub method_pointer: *const c_void,
|
|
_pad: [u8; 0x20],
|
|
pub flags: u16
|
|
}
|
|
```
|
|
|
|
#### Memory Layout Diagram:
|
|
```
|
|
Offset Field Type Description
|
|
+0x00 klass *const Il2CppClass Pointer to declaring Il2CppClass
|
|
+0x08 method_pointer *const c_void Absolute Virtual Address (VA) of native compiled code
|
|
+0x10 _pad [u8; 0x20] Internal runtime fields (invoker_method, name, etc.)
|
|
+0x30 flags u16 CLI Method Attributes (Public, Private, Static, etc.)
|
|
```
|
|
|
|
### 6.2 Address Boundary Verification
|
|
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:
|
|
|
|
```rust
|
|
fn verify_pointer(il2cpp: &Il2CppApi, pointer: usize) -> bool {
|
|
(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.
|
|
|
|
---
|
|
|
|
## 7. C# Header Reconstruction Engine (`csdumper.rs`)
|
|
|
|
`csdumper.rs` decompiles raw binary runtime structures into valid C# source definitions, decorating them with exact memory offsets and RVAs.
|
|
|
|
### 7.1 ECMA-335 Bitmask Decompilation
|
|
The engine decodes CLI attribute bitfields from `constants.rs`:
|
|
|
|
#### 1. Method Visibility & Modifiers
|
|
```rust
|
|
let flags = unsafe { (*method).flags } as i32;
|
|
let access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
|
|
|
|
let access_str = match access {
|
|
METHOD_ATTRIBUTE_PRIVATE => "private ",
|
|
METHOD_ATTRIBUTE_PUBLIC => "public ",
|
|
METHOD_ATTRIBUTE_FAMILY => "protected ",
|
|
METHOD_ATTRIBUTE_ASSEM | METHOD_ATTRIBUTE_FAM_AND_ASSEM => "internal ",
|
|
METHOD_ATTRIBUTE_FAM_OR_ASSEM => "protected internal ",
|
|
_ => ""
|
|
};
|
|
```
|
|
|
|
#### 2. Virtual Table & Polymorphism Layout
|
|
IL2CPP preserves method slot layout flags (`METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK`):
|
|
- `METHOD_ATTRIBUTE_ABSTRACT`: Emits `abstract`.
|
|
- `METHOD_ATTRIBUTE_FINAL` with `METHOD_ATTRIBUTE_REUSE_SLOT`: Emits `sealed override`.
|
|
- `METHOD_ATTRIBUTE_VIRTUAL` with `METHOD_ATTRIBUTE_NEW_SLOT`: Emits `virtual`.
|
|
- `METHOD_ATTRIBUTE_VIRTUAL` with `METHOD_ATTRIBUTE_REUSE_SLOT`: Emits `override`.
|
|
|
|
#### 3. ValueType Field Offset Normalization
|
|
In IL2CPP's object model:
|
|
- Reference types (`class`) carry a 16-byte (`0x10`) header (`Il2CppObject` containing `Il2CppClass*` and `MonitorData*`).
|
|
- Value types (`struct`) on the heap are boxed with the 16-byte header, but unboxed instance structs lack this header.
|
|
`csdumper.rs` handles this normalization:
|
|
```rust
|
|
if is_valuetype && !is_static && field_offset > 0 {
|
|
field_offset -= 0x10;
|
|
}
|
|
```
|
|
|
|
### 7.2 Type Name Normalization
|
|
To produce clean, idiomatic C# source, `api.rs` maps raw CLR type names to C# language aliases:
|
|
```rust
|
|
const PRIMITIVE_TYPES: &[(&str, &str)] = &[
|
|
("System.Void", "void"),
|
|
("System.Boolean", "bool"),
|
|
("System.Char", "char"),
|
|
("System.SByte", "sbyte"),
|
|
("System.Byte", "byte"),
|
|
("System.Int16", "short"),
|
|
("System.UInt16", "ushort"),
|
|
("System.Int32", "int"),
|
|
("System.UInt32", "uint"),
|
|
("System.Int64", "long"),
|
|
("System.UInt64", "ulong"),
|
|
("System.Single", "float"),
|
|
("System.Double", "double"),
|
|
("System.String", "string"),
|
|
("System.IntPtr", "IntPtr"),
|
|
("System.UIntPtr", "UIntPtr"),
|
|
("System.Object", "object"),
|
|
("&", "")
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Method RVA Map Generation (`methoddumper.rs`)
|
|
|
|
### 8.1 Method Signature Flattening & Collision Resolution
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"RPG.Client.AutoScrollRect::.ctor": "0x1febdf0",
|
|
"RPG.Client.AutoScrollRect::MNOGNALMGPL": "0x1feb700",
|
|
"RPG.Client.AutoScrollRect::POFLDHKMLAF": "0x1feb7a0",
|
|
"RPG.Client.AutoScrollRect::SetItemSelectCallback": "0x1feb7f0",
|
|
"RPG.Client.AutoScrollRect::SnapTo": "0x1feb9c0"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Comparative Technical Assessment: In-Process vs. Static Dumpers
|
|
|
|
| Dimension | `honkai-dumper` (In-Process) | `Il2CppDumper` (Static Disk) |
|
|
| :--- | :--- | :--- |
|
|
| **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 |
|
|
| **Output Formats** | `dump.cs` (Headers) and `methods.json` (RVAs) | `dump.cs`, `script.py` (IDA), `il2cpp.h`, `stringliteral.json` |
|
|
|
|
---
|
|
|
|
## 10. Integration & Cross-Application in Server & Protocol Engineering
|
|
|
|
### 10.1 Labeling Disassembly in IDA Pro / Ghidra
|
|
When reverse engineering stripped Star Rail binaries:
|
|
1. Open `GameAssembly.dll` in IDA Pro / Ghidra / Binary Ninja.
|
|
2. In IDA Pro, run an import script:
|
|
```python
|
|
import json
|
|
import ida_name, ida_bytes
|
|
|
|
with open("methods.json", "r") as f:
|
|
methods = json.load(f)
|
|
|
|
base = ida_nalt.get_imagebase()
|
|
for name, rva_str in methods.items():
|
|
rva = int(rva_str, 16)
|
|
ea = base + rva
|
|
# Sanitize name for IDA symbol constraints
|
|
clean_name = name.replace("::", "__").replace(".", "_")
|
|
ida_name.set_name(ea, clean_name, ida_name.SN_NOWARN)
|
|
```
|
|
3. Instantly transforms thousands of anonymous subroutines (`sub_181FEB700`) into identifiable game logic (`RPG_Client_AutoScrollRect__MNOGNALMGPL`).
|
|
|
|
### 10.2 Protocol & Packet Handler Discovery
|
|
In private server engineering (`LunarCore`, `C# NativeAOT Reference Server`, `GaC-hkrpg-go`):
|
|
- 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.
|