@@ -49,78 +49,180 @@ GameFolder/
4949### 运行
5050
5151启动游戏 → ` winmm.dll ` 被加载 → Corehold 初始化 CoreCLR →
52- ` Managed.Entry ` 调用 ` NativeApi.Init ` → kiero 检测 D3D9/11/12 →
52+ ` Managed.Entry ` 调用 MinHook 初始化 → kiero 检测 D3D9/11/12 →
5353MinHook 钩 ` Present ` → C# 回调渲染 ImGui → 按 ` INSERT ` 打开菜单
5454
5555## Android (IL2CPP Unity) / Android 注入
5656
57- Download ` library-release.aar ` from [ Releases] ( https://github.com/StArraySharp/StArray.ModManager/releases ) .
5857从 [ Releases] ( https://github.com/StArraySharp/StArray.ModManager/releases ) 下载 ` library-release.aar ` 。
5958
60- ⚠️ ** Add INTERNET permission in ` AndroidManifest.xml ` ** (required for OTA updates & mod networking)
6159⚠️ ** 在 ` AndroidManifest.xml ` 添加网络权限** (OTA 更新和 Mod 联网需要):
6260
6361``` xml
6462<uses-permission android : name =" android.permission.INTERNET" />
6563```
6664
65+ AAR 内直接包含 CoreCLR 运行时 ` .so ` (arm64-v8a),无需额外下载。
66+
6767``` bash
6868unzip library-release.aar -d aar_content
6969apktool d target.apk -o target_src
7070
71- # Copy .so → lib/
71+ # 复制 .so → lib/(含 CoreCLR 运行时)
7272cp aar_content/jni/arm64-v8a/* .so target_src/lib/arm64-v8a/
7373
74- # Copy CoreCLR runtime DLLs → assets/
75- cp aar_content/assets/runtime/* .dll target_src/assets/
76-
77- # Decompile classes.jar → smali
74+ # 反编译 classes.jar → smali
7875java -jar baksmali.jar d aar_content/classes.jar -o target_src/smali/starray/
7976
80- # Add to UnityPlayerActivity.onCreate:
77+ # 在 UnityPlayerActivity.onCreate 添加:
8178# invoke-static {}, Lstarray/android/modmanager/ModManager;->launch()V
8279#
83- # ⚠️ launch() defaults to /sdcard/ModManager/. Paths are hardcoded in
84- # ModManager.smali. Modify smali constants if your layout differs,
85- # or use ModManagerUpdater (configurable paths + OTA auto-update).
86- #
87- # ⚠️ launch() 默认加载 /sdcard/ModManager/。路径硬编码在 ModManager.smali,
88- # 如需自定义请修改 smali 内常量,或使用 ModManagerUpdater。
80+ # launch() 默认加载 /sdcard/ModManager/。路径硬编码在 ModManager.smali,
81+ # 如需自定义请修改 smali 内常量,或使用 ModManagerUpdater。
8982
90- # Repack & sign
83+ # 回编译 & 签名
9184apktool b target_src -o repacked.apk
9285uber-apk-signer --apks repacked.apk
9386```
9487
95- ## 2. Deploy Manager (Android) / 部署管理器
88+ ## 部署管理器
9689
97- Place manager DLL and dependencies in ` /sdcard/ModManager/manager/ ` .
9890将管理器 DLL 及依赖放入 ` /sdcard/ModManager/manager/ ` 。
9991
100- | File | From / 来源 |
92+ | File | 来源 |
10193| ---| ---|
10294| ` StArray.ModManager.dll ` | [ Releases] ( https://github.com/StArraySharp/StArray.ModManager/releases ) → ` modmanager.zip ` |
103- | ` ImGui.NET.dll ` | Same dir as StArray.ModManager build output |
95+ | ` ImGui.NET.dll ` | 同上 |
10496| ` OpenTK.Graphics.dll ` | 同上 |
10597
106- Final layout / 最终目录:
98+ 最终目录:
10799
108100```
109101/sdcard/ModManager/
110102├── manager/
111103│ ├── StArray.ModManager.dll
112104│ ├── ImGui.NET.dll
113105│ ├── OpenTK.Graphics.dll
114- │ └── modmanager_config.json (auto-generated / 自动生成)
106+ │ └── modmanager_config.json (自动生成)
115107└── mods/
116108 └── {YourModName}/
117109 ├── {YourModName}.dll
118110 └── ...
119111```
120112
121- ## 3. Write a Mod / 写 Mod (跨平台)
113+ ## Hook 系统
114+
115+ 提供两套独立 Hook 属性,分别适用于不同场景。
116+
117+ ### [ NativeHook] — 原生 so 函数 Hook
118+
119+ Hook ` .so ` 共享库中的导出函数(如 ` libinput.so ` 、` libunity.so ` ),支持三种模式:
120+
121+ #### 1. 符号导出 Hook
122+
123+ 按 ` .dynsym ` 导出的符号名 hook 原生函数:
124+
125+ ``` csharp
126+ [NativeHook (" libinput.so" , " _ZN7android13InputConsumer14consumeSamplesEPNS_26InputEventFactoryInterfaceERNS0_5BatchEmPjPPNS_10InputEventE" )]
127+ static bool OnConsumeSamples (void * thiz , void * factory , IntPtr batch , ulong count , uint * outSeq , void ** outEvent )
128+ {
129+ return OnConsumeSamplesOriginal (thiz , factory , batch , count , outSeq , outEvent );
130+ }
131+ ```
132+
133+ #### 2. RVA Hook
134+
135+ 已知函数在 so 内的 RVA 偏移时使用(绕过符号混淆):
136+
137+ ``` csharp
138+ [NativeHook (" libunity.so" , 0x 123456 )]
139+ static void OnSomeFunc () { }
140+ ```
141+
142+ #### 3. 解析器方法 Hook
143+
144+ 引用同类内的一个 ` static nint MethodName() ` 方法,由该方法返回目标地址(支持特征码搜索、ELF 符号扫描等自定义解析逻辑):
145+
146+ ``` csharp
147+ [NativeHook (nameof (ResolveTarget ))]
148+ static void OnSomeFunc ()
149+ {
150+ // ...
151+ }
152+
153+ static nint ResolveTarget ()
154+ {
155+ NativeFuncResolver resolver = new (" /system/lib64/libinput.so" );
156+ long rva = resolver .FindRva (" _ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE" );
157+ resolver .Load ();
158+ return resolver .GetFuncPtr (rva );
159+ }
160+ ```
161+
162+ 跨类引用可用 ` "Namespace.Type.Method" ` 格式,` :: ` 自动转换为 ` . ` 。
163+
164+ ### 源码生成
122165
123- ### 引用 DLL / Reference DLLs
166+ ` [NativeHook] ` 通过 Roslyn 源码生成器在编译时自动生成 partial 类,包含:
167+
168+ | 生成成员 | 说明 |
169+ | ---| ---|
170+ | ` InstallHooks() ` | 安装所有 hook |
171+ | ` UninstallHooks() ` | 卸载所有 hook |
172+ | ` MethodNameOriginal ` | 调用原函数的委托 |
173+
174+ 生成的 partial class 保留原始类的访问修饰符(` public ` /` internal ` )。
175+
176+ ### 使用 NativeHook
177+
178+ ``` csharp
179+ public static partial class MyHooks
180+ {
181+ [NativeHook (" libinput.so" , " _ZN7android13InputConsumer14consumeSamplesEPNS_26InputEventFactoryInterfaceERNS0_5BatchEmPjPPNS_10InputEventE" )]
182+ static long OnConsumeSamples (void* thiz, void* factory, IntPtr batch , ulong count , uint* outSeq, void** outEvent)
183+ {
184+ // 调用原函数
185+ var result = OnConsumeSamplesOriginal (thiz , factory , batch , count , outSeq , outEvent );
186+ // 自定义逻辑
187+ return result ;
188+ }
189+ }
190+
191+ // 安装:
192+ MyHooks .InstallHooks ();
193+ // 卸载:
194+ MyHooks .UninstallHooks ();
195+ ```
196+
197+ ### NativeFuncResolver
198+
199+ ELF 文件解析工具,用于查找符号 RVA、特征码扫描、计算最终函数指针:
200+
201+ | 方法 | 说明 |
202+ | ---| ---|
203+ | ` FindRva(string symbol, byte?[]? pattern) ` | 符号名 → RVA,失败则回退特征码 |
204+ | ` FindSymbolRva(string) ` | 在 ` .dynsym ` + ` .dynstr ` 中精确匹配 |
205+ | ` FindRva(byte?[] pattern) ` | 在 ` .text ` 段特征码搜索 |
206+ | ` ParseHexPattern(string) ` | 转换 ` "48 89 ?? ?? ff" ` 至匹配模式 |
207+ | ` Resolve(string, byte?[]?) ` | 加载库 + 查 RVA → 返回函数指针 |
208+ | ` Load() ` | 加载 so 到进程(优先已加载基址) |
209+ | ` GetFuncPtr(long rva) ` | 返回 ` base + rva ` 函数指针 |
210+
211+ 特征码使用 ` ?? ` 表示通配符:
212+
213+ ``` csharp
214+ var resolver = new NativeFuncResolver (" /system/lib64/libinput.so" );
215+ byte ? [] sig = NativeFuncResolver .ParseHexPattern (
216+ " e8 0f 19 fc ?? ?? ?? ?? fc 6f 02 a9 ?? ?? ?? ??"
217+ );
218+ long rva = resolver .FindRva (" symbolName" , sig );
219+ resolver .Load ();
220+ nint funcPtr = resolver .GetFuncPtr (rva );
221+ ```
222+
223+ ## 写 Mod(跨平台)
224+
225+ ### 引用 DLL
124226
125227| DLL | 用途 | 必需 |
126228| ---| ---| ---|
@@ -170,7 +272,6 @@ public class HelloMod : IModPlugin
170272 public void OnLoad () { }
171273 public void OnUnload () { }
172274
173- // Draw HUD overlay on game screen / 在游戏画面上叠加 HUD
174275 public void OnForegroundGUI (ImDrawListPtr drawList )
175276 {
176277 drawList .AddText (new Vector2 (10 , 10 ), 0x FFFFFFFF , $" FPS: {ImGui .GetIO ().Framerate : F0 }" );
@@ -180,16 +281,13 @@ public class HelloMod : IModPlugin
180281
181282### 可设置项的 Mod
182283
183- 实现 ` IModSettings ` 添加设置页:
184-
185284``` csharp
186285public class ConfigurableMod : IModPlugin , IModSettings
187286{
188287 public string Id => " com.example.configurable" ;
189288
190- // ── 声明设置字段,自动显示在 ModManager 检查器 ──
191289 [ModSettingLabel (" Show HUD" )]
192- [ModSettingLabelSide (ModInspector .LabelSide .Right )] // 勾选框,标签在右侧
290+ [ModSettingLabelSide (ModInspector .LabelSide .Right )]
193291 public bool ShowHud = true ;
194292
195293 [ModSettingLabel (" Scale" )]
@@ -200,34 +298,92 @@ public class ConfigurableMod : IModPlugin, IModSettings
200298}
201299```
202300
203- ### 使用 UnmanagedHook / UnmanagedStub
301+ ### [ UnmanagedHook] — IL2CPP / Mono 方法 Hook
302+
303+ Hook Unity 托管方法(IL2CPP 或 Mono 运行时),通过程序集名、类名、方法名定位:
304+
305+ ``` csharp
306+ // 3 参数:Il2Cpp 风格(无命名空间)
307+ [UnmanagedHook (" Assembly-CSharp.dll" , " scrController" , " TogglePauseGame" )]
308+ static bool TogglePauseGame (nint thiz )
309+ {
310+ var ret = TogglePauseGameOriginal (thiz );
311+ Logger .Warn (" MyMod" , " Game paused" );
312+ return ret ;
313+ }
314+
315+ // 4 参数:Mono 风格(带命名空间)
316+ [UnmanagedHook (" Assembly-CSharp.dll" , " GameLogic" , " PlayerController" , " TakeDamage" )]
317+ static void TakeDamage (nint thiz , int amount )
318+ {
319+ TakeDamageOriginal (thiz , Math .Min (amount , 10 )); // 减伤
320+ }
321+ ```
322+
323+ ### 定义 Stub DLL — 源码生成器
324+
325+ 将 Stub 定义在单独的类库项目中,编译时 ` UnmanagedStubGenerator ` 自动生成方法实现,输出可直接分发的 Stub DLL。
326+
327+ ``` csharp
328+ using StArray .ModManager .RuntimeAbstractions ;
329+
330+ // [UnmanagedType(assembly, namespace, class)] 标记目标游戏类
331+ [UnmanagedType (" Assembly-CSharp.dll" , " " , " scrController" )]
332+ public partial class ScrControllerStub
333+ {
334+ // partial 方法 + [UnmanagedMember] → 生成器自动实现运行时反射调用
335+ [UnmanagedMember ]
336+ public partial int CurrentSeqID ();
337+
338+ [UnmanagedMember ]
339+ public partial nint PlayerOne ();
340+
341+ [UnmanagedMember ]
342+ public partial void TogglePauseGame ();
343+ }
344+ ```
345+
346+ 编译后,Stub DLL 可直接被 Mod 项目引用使用:
347+
348+ ``` csharp
349+ // Mod 项目引用 Stub DLL,无需自己写运行时反射
350+ var controller = new ScrControllerStub (ptr );
351+ int seqId = controller .CurrentSeqID ();
352+ controller .TogglePauseGame ();
353+ ```
354+
355+ ### 使用 UnmanagedStub + UnmanagedHook
204356
205357参考 [ LevelDebugger] ( LevelDebugger/ ) ,分三步:
206358
207- 1 . ** 定义 Stub** — 用 ` UnmanagedObject ` 封装游戏类的字段/属性/方法调用
208- 2 . ** 声明 Hook** — 用 ` [UnmanagedHook(程序集, 类, 方法) ] ` 标记替换方法
359+ 1 . ** 定义 Stub** — ` partial class ` + ` [UnmanagedType] ` + ` [UnmanagedMember] ` ,生成器自动产出实现
360+ 2 . ** 声明 Hook** — 用 ` [UnmanagedHook] ` 标记替换方法
2093613 . ** 注册设置** — 用 ` [ModSettingLabel] ` 特性声明可开关项
210362
211363``` csharp
212- // Stub — 封装游戏类字段/方法
213- public sealed class ScrControllerStub (nint ptr ) : UnmanagedObject (ptr )
364+ // Stub DLL 项目 — 声明游戏类接口
365+ [UnmanagedType (" Assembly-CSharp.dll" , " " , " scrController" )]
366+ public partial class scrController
214367{
215- public int CurrentSeqID => Obj .GetField <int >(" currentSeqID" );
216- public nint PlayerOne => Obj .GetField <nint >(" playerOne" );
368+ [UnmanagedMember ] public partial int CurrentSeqID ();
369+ [UnmanagedMember ] public partial nint PlayerOne ();
370+ [UnmanagedMember ] public partial void TogglePauseGame ();
217371}
218372
219- // Hook
373+ // Mod 项目 — 引用 Stub DLL + Hook
220374public class MyMod : IModPlugin
221375{
222376 [UnmanagedHook (" Assembly-CSharp.dll" , " scrController" , " TogglePauseGame" )]
223377 static bool TogglePauseGame (nint thiz )
224378 {
225379 var ret = TogglePauseGameOriginal (thiz );
226- Logger .Warn (" MyMod" , " Game paused" );
380+ var ctrl = new scrController (thiz );
381+ Logger .Warn (" MyMod" , $" Paused at seq {ctrl .CurrentSeqID ()}" );
227382 return ret ;
228383 }
229384}
230385```
386+
231387### Stub / Hook 详细文档
232388
233389参见:
@@ -237,7 +393,7 @@ public class MyMod : IModPlugin
237393- [ ModInspector] ( StArray.ModManager/Inspector/ ) — 自动绘制设置面板
238394- [ UnmanagedObject] ( StArray.ModManager/RuntimeAbstractions/IUnmanagedObject.cs ) — Stub 基类,提供 ` Wrap<T>() ` 安全包装
239395
240- ### package / 发布
396+ ### 发布
241397
242398Mod 部署到 ` mods/{ModId}/ ` 目录,ModManager 自动识别:
243399
@@ -247,20 +403,22 @@ GameFolder/
247403 └── mods/
248404 └── {YourModId}/
249405 ├── {YourModId}.dll
250- └── 依赖.dll # 如有额外依赖
406+ └── 依赖.dll
251407```
252408
253- ### Optional interfaces / 可选接口
409+ Android 同样部署到 ` /sdcard/ModManager/mods/{YourModId}/ ` 。
410+
411+ ### 可选接口
254412
255413``` csharp
256- interface IModSettings { void OnGui (); } // Custom settings tab / 自定义设置页
257- interface IModSettingCustomDraw { void DrawInspector (); } // Custom inspector / 自定义检查器
414+ interface IModSettings { void OnGui (); } // 自定义设置页
415+ interface IModSettingCustomDraw { void DrawInspector (); } // 自定义检查器
258416```
259417
260- ### Field attributes / 设置特性
418+ ### 设置特性
261419
262420``` csharp
263- [ModSettingLabel (" Health Multiplier / 血量倍率 " )]
421+ [ModSettingLabel (" Health Multiplier" )]
264422[ModSettingRange (0 . 1 f , 10 f )]
265423public float HealthMultiplier = 1 f ;
266424
@@ -269,9 +427,9 @@ public float HealthMultiplier = 1f;
269427public string JsonConfig = " {}" ;
270428```
271429
272- ### Overlay drawing / Overlay 绘制
430+ ### Overlay 绘制
273431
274432``` csharp
275- void OnBackgroundGUI (ImDrawListPtr d ) { } // Behind ImGui windows / 窗口下方 (watermark / 水印)
276- void OnForegroundGUI (ImDrawListPtr d ) { } // Above ImGui windows / 窗口上方 ( HUD)
433+ void OnBackgroundGUI (ImDrawListPtr d ) { } // 窗口下方(水印)
434+ void OnForegroundGUI (ImDrawListPtr d ) { } // 窗口上方( HUD)
277435```
0 commit comments