Skip to content

Commit a97b259

Browse files
author
StArray
committed
fix(imgui): 修复点击hook
1 parent 6cff8fb commit a97b259

9 files changed

Lines changed: 598 additions & 145 deletions

File tree

‎Android/library/src/main/java/starray/android/modmanager/ModManagerUpdater.java‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ private Path downloadAndExtractSync(VersionInfo version, Path targetDir, AlertDi
263263
}
264264

265265
updateDialog(dialog, "正在解压...", -1);
266-
clearDirectory(targetDir.toFile());
267266
unzip(zipFile, targetDir);
268267

269268
Files.write(targetDir.resolve("version.json"),

‎GET_STARTED.md‎

Lines changed: 206 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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 →
5353
MinHook 钩 `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
6868
unzip library-release.aar -d aar_content
6969
apktool d target.apk -o target_src
7070

71-
# Copy .so → lib/
71+
# 复制 .so → lib/(含 CoreCLR 运行时)
7272
cp 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
7875
java -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+
# 回编译 & 签名
9184
apktool b target_src -o repacked.apk
9285
uber-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", 0x123456)]
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), 0xFFFFFFFF, $"FPS: {ImGui.GetIO().Framerate:F0}");
@@ -180,16 +281,13 @@ public class HelloMod : IModPlugin
180281

181282
### 可设置项的 Mod
182283

183-
实现 `IModSettings` 添加设置页:
184-
185284
```csharp
186285
public 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]` 标记替换方法
209361
3. **注册设置** — 用 `[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
220374
public 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

242398
Mod 部署到 `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.1f, 10f)]
265423
public float HealthMultiplier = 1f;
266424

@@ -269,9 +427,9 @@ public float HealthMultiplier = 1f;
269427
public 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

Comments
 (0)