English | 简体中文
Android IL2CPP Unity mod manager with CoreCLR runtime embedding and ImGui overlay UI.
- Native injection via
libmodmanager.soloaded into Unity process - Dobby hook on
eglSwapBuffersand Android input events - CoreCLR embedded at runtime, launching .NET managed code from JNI
- UnityResolve reflection engine traverses IL2CPP/Mono managed types
- ImGui overlay rendered via EGL + OpenGL ES with touch and keyboard IME
- EGL SwapBuffers hook with Dobby — renders ImGui overlay every frame
- Touch & key input via InputConsumer hooks + cimgui Android backend
- IME support (Chinese/Japanese) via custom KeyboardView + InputConnection bridge
- Mod system — scan/load/unload with dependency resolution + auto-enable on restart
- Mod overlay API —
OnBackgroundGUI/OnForegroundGUIfor direct game-screen drawing - Auto-update — OTA version check + download + SHA-256 verification + restart
- Config persistence — STJ source-gen JSON, AOT-compatible
- File logging — dual-write to logcat +
manager.log - GL debug panel — caps toggles, blend/depth func selectors, GL state queries
- FontAwesome 7 icon support via embedded resource
- IImGuiRenderer interface — swap between EGL, Vulkan backends
- CoreCLR args — pass
string[]from Java to managedEntry(int, IntPtr) - CoreCLR bundled in AAR —
copyCoreClrToJniLibscopies runtime .so into jniLibs
| Interface | Method | Description |
|---|---|---|
IModPlugin |
OnLoad() |
Called when mod is loaded |
OnUnload() |
Called when mod is unloaded | |
OnBackgroundGUI(ImDrawListPtr) |
Draw behind ImGui windows | |
OnForegroundGUI(ImDrawListPtr) |
Draw above ImGui windows | |
IModSettings |
OnGui() |
Draw custom settings tab in ModManager |
[NativeHook] supports three modes:
| Attribute | Description |
|---|---|
[NativeHook("lib.so", "symbolName")] |
Symbol export hook — uses dlsym to resolve |
[NativeHook("lib.so", 0x1234)] |
RVA hook — uses GetFuncPtr(base + 0x1234) |
[NativeHook(nameof(ResolverMethod))] |
Resolver pattern — references a static nint ResolverMethod() in the same class; supports "Namespace.Type.Method" cross-class |
Generated partial class provides InstallHooks() / UninstallHooks() with *Original delegates for calling the original function.
| Base Class | Description |
|---|---|
UnmanagedObject |
Wraps nint pointer, provides Obj field for runtime access |
Wrap<T>(nint ptr) |
Returns a typed stub wrapping the given pointer |
| Class | Key Methods | Description |
|---|---|---|
RuntimeObject |
GetField<T>(string) / SetField(string, T) |
Read/write instance fields |
Invoke<T>(string, params object[]) |
Call instance methods | |
GetProperty<T>(string) / SetProperty(string, T) |
Read/write properties | |
RuntimeArray<T> |
new RuntimeArray<T>(nint ptr) |
Strongly-typed array wrapper |
.Length / [index] |
Array length and element access | |
RuntimeManager |
GetDomain() / IsIl2Cpp |
Detect current runtime and get domain |
IAppDomain |
OpenAssembly(string) |
Open a loaded assembly |
IRuntimeAssembly |
GetClass(string ns, string name) |
Get a class by namespace + name |
IRuntimeClass |
GetMethod(string, int) / GetField(string) |
Get method/field for invocation |
IRuntimeMethod |
Invoke(nint thisPtr, params object[]) / InvokeStatic() |
Call a method |
IRuntimeField |
GetValue<T>(nint) / SetValue(nint, T) |
Read/write a field |
| Method | Description |
|---|---|
FindRva(string symbol, byte?[]? pattern) |
Symbol → RVA, fallback to sig scan |
FindSymbolRva(string) |
Search .dynsym + .dynstr by exact name |
FindRva(byte?[] pattern) |
Signature-scan .text section |
ParseHexPattern(string) |
Convert "48 89 ?? ?? ?? ?? ?? ff" to pattern |
Resolve(string, byte?[]?) |
Load + find + return function pointer |
| Attribute | Parameters | Description |
|---|---|---|
[ModSettingLabel("text")] |
string |
Display label for a setting field |
[ModSettingLabelSide(ModInspector.LabelSide)] |
Left / Right |
Label position |
[ModSettingRange(min, max)] |
float, float |
Clamp numeric values to range |
[ModSettingJson(Lines = N)] |
int |
Multi-line JSON editor |
| Method | Description |
|---|---|
ModInspector.Draw(object) |
Auto-generate UI for all [ModSettingLabel] fields |
| Class | Description |
|---|---|
Logger |
Info(string), Warn(string), Error(string) — unified logging |
HookHelper |
Instance — set to your hook implementation; GetFunctionRVA / GetFunctionRVAFallback |
BehaviourManager |
Schedule actions on Unity lifecycle events |
DL |
Cross-platform Open / Symbol / Close / Error / Addr / GetBaseAddress |
| Library | License | Used In |
|---|---|---|
| Dear ImGui | MIT | UI rendering |
| cimgui | MIT | ImGui C API bindings |
| ImGui.NET | MIT | C# ImGui bindings |
| kiero2 | MIT | Graphics API detection (D3D9/11/12/GL/VK) |
| MinHook | BSD-2 | API hooking |
| Corehold | MIT | winmm proxy DLL + CoreCLR hosting |
| Dobby | Apache-2.0 | Android inline hook |
| CoreCLR | MIT | .NET runtime |
| FontAwesome 7 | OFL/SIL | Icon font |
| Platform | Requirements |
|---|---|
| Windows | .NET 10 SDK; Visual Studio with Desktop development with C++ (MSVC + Windows SDK) and C++ CMake tools for Windows (CMake + Ninja on PATH) |
| Android | Android NDK (cmake on PATH) |
# Windows (native + C#, one command)
dotnet build StArray.ModManager.Windows -c Release
# Android
cd Android && ./gradlew :library:assembleReleaseStArray.ModManager.CImGui/ is the shared cimgui source used by both
platforms. The Windows and Android native builds reach it through two git
symlinks (StArray.ModManager.Windows/Native/cimgui,
Android/library/src/main/cpp/libs/cimgui), which on Windows only materialize
as real links with Developer Mode enabled (or an elevated shell) —
otherwise they are checked out as plain text files and the CMake build fails
with missing cimgui references. One-time fix after cloning:
# 1. Enable Windows Developer Mode first, then:
git config core.symlinks true
rm StArray.ModManager.Windows/Native/cimgui Android/library/src/main/cpp/libs/cimgui
git checkout -- StArray.ModManager.Windows/Native/cimgui Android/library/src/main/cpp/libs/cimguiThe native DLL (StArray.ModManager.Windows.Native.dll) uses
kiero2 for graphics API detection and
MinHook for Present hooking.
Per-backend hook files (Native/):
| File | Backend | Init | Render |
|---|---|---|---|
dx12hook.cpp |
D3D12 | Descriptor heaps, command list, RTV per-frame contexts | Resource barriers + OMSetRenderTargets |
dx11hook.cpp |
D3D11 | Device + Context + persistent backbuffer RTV | OMSetRenderTargets + RenderDrawData |
dx9hook.cpp |
D3D9 | SwapChain QI → Device | Viewport/scissor save-restore |
main.cpp |
GL/VK | Win32 init only (C# handles backend) | C# render callback only |
Unified dispatch (main.cpp hkPresent):
- All backends share
DisplaySize,ImGui_ImplWin32_NewFrame, WndProc hook DEBUG_LOGconditional compilation (NDEBUG→ compiled out in Release)- Window handle from
SwapChain::GetDesc().OutputWindow
flowchart LR
CSharp[C# ImGuiRenderer] -->|P/Invoke Init| Native[Native DLL]
Native -->|kiero2 detect| API[D3D9/11/12/GL/VK]
API -->|MinHook| Present[hkPresent]
Present -->|dispatch| D3D11[dx11hook]
Present -->|dispatch| D3D12[dx12hook]
Present -->|dispatch| D3D9[dx9hook]
D3D11 & D3D12 & D3D9 -->|callback| CSharp
CSharp -->|ImGui.NET| cimgui[cimgui.dll]
- Windows x64 (D3D9/11/12) — DLL injection or CoreCLR hosting
- Android arm64-v8a (OpenGL ES) — IL2CPP Unity games (API 26+)
- CoreCLR .NET 10 runtime
git clone --recurse-submodules https://github.com/StArraySharp/StArray.ModManager.gitOn Windows, complete the one-time cimgui symlink setup in
Build before running dotnet build, then:
dotnet build StArray.ModManager.Windows -c ReleaseSee GET_STARTED.md for injection and build instructions.
Runtime assemblies can be downloaded from the runtime-references release.
Most code generated by AI.