# 偏移 0x364 全量反汇编分析 —— 谁在读、谁在写、各是什么 > **对象**:`D:\rxjh2025\client\YGOnline.exe`(capstone 逐指令口径,373 万条指令全扫) > **问题**:实机遍历证实「控件 ID = [控件+0x364]」,那游戏自己在哪里读它、写它?这个偏移还有没有别的含义? > **生成**:2026-09-18 --- ## 一、结论速览 | 结论 | 内容 | |---|---| | **总量** | 全 `.text` 共 **879 处** `[x+0x364]` 访问,分布在约 **300 个函数** —— 是个高频复用偏移 | | **★ ID 写入点找到了** | `0x0075B230`:控件工厂 **`InterUI_Manufacture`**(虚表 `0x010831E0`,RTTI 验证)构造控件时写 `[控件+0x364] = 控件ID`(样本值 `0x38`) | | **★ 游戏自己的 0x3F4 分发原文挖到了** | `0x0075A6F8`:`push 0 / push 1 / push 0x3F4` → `ecx = [[0x183C52C]+0x3EC]+0x39C` → `call [虚表+4]` —— **与你的机器码写法逐条同款** | | **★ 引擎级取控件新链路** | `[0x183C52C]`(UI管理器) → `+0x3EC`(当前活动窗口) → `+0x39C`(当前目标控件),不依赖助手窗口 `+0x414` 链 | | **⚠ 语义随类而变** | 同一个 `+0x364`,在控件上是 ID、在 `Inter*` 窗口上是「当前活动子对象指针」、在数据结构上是数组首 —— **不能见 364 就当 ID** | --- ## 二、扫描口径与形态统计 方法与《全局地址与基址分析报告》一致:capstone `detail=True + skipdata=True` 逐指令,收所有 `disp==0x364` 的内存操作数,区分读/写、标量/变址、位宽。 | 形态 | 次数 | 说明 | |---|---|---| | 读 · 标量 · 32位 | **550** | 大多数是取 ID 或取指针 | | 写 · 标量 · 32位 | **185** | 含构造初始化(ID / -1 / 指针) | | 读 · 变址 `[reg+reg*4+0x364]` | 86 | **数组**用法(另一批类) | | 写 · 标量 · 8位 | 24 | 字节标志 | | 写 · 变址 · 32位 | 13 | 数组写入 | | 读 · 标量 · 8/16位 | 10 + 6 | 字节/字标志 | | 写 · 标量 · 16位 | 5 | — | > **第一课**:879 处里只有一部分是「控件 ID」。判定对象身份必须先看 `[x]` 是不是 `.rdata` 虚表(RTTI 法),不能见 364 就当 ID。 --- ## 三、★ 控件 ID 的写入点:`InterUI_Manufacture` 控件工厂 `0x0075B230` 处的构造序列(上下文直接可读): ``` 0075B272 mov dword ptr [eax], 0x10831E0 ; ← 虚表 = 0x010831E0 0075B27B mov dword ptr [ecx + 0x358], 5 ; 类型/样式字段 0075B288 mov dword ptr [edx + 0x35C], 0x80 0075B295 mov dword ptr [eax + 0x360], 0x9D 0075B2A2 mov dword ptr [ecx + 0x364], 0x38 ; ★ 控件 ID 写入点(样本 0x38) 0075B2AF mov dword ptr [edx + 0x330], 0 0075B2BC mov dword ptr [eax + 0x354], 0 0075B2C9 mov dword ptr [ecx + 0x334], 0 ``` 虚表 `0x010831E0` 走 RTTI 链(`[-4]→COL 0x01185D44 → TD 0x011F7458`)解出类名: ``` .?AVInterUI_Manufacture@@ ← 「UI 制造」,即控件工厂类 ``` **解读**:这个类生产的每个控件实例都把「分配到的控件 ID」放在自己的 `+0x364`。与你实机遍历 74 个控件读出的 `[控件+0x364]=ID` **完全吻合** —— 静态与活体两头闭合。相邻字段族(`+0x330 / +0x334 / +0x354 / +0x358 / +0x35C / +0x360 / +0x364`)就是控件对象头的初始化区。 --- ## 四、★ 游戏自己的 0x3F4 分发原文(最重要的发现) `0x0075A6F8` 起,一个函数的收尾处(`ret 8` 前): ``` 0075A6FF push 0 ; 参数3(lparam?) 0075A701 push 1 ; 参数2 0075A703 push 0x3F4 ; ★ 动作码 —— 和你 push 0x3F4 一模一样 0075A708 mov eax, [0x183C52C] ; ★ UI 管理器单例(.data 全局) 0075A70D mov ecx, [eax + 0x3EC] ; → 当前活动窗口 0075A713 mov edx, [ecx + 0x39C] ; → 窗口的「当前目标控件」 0075A719 mov eax, [0x183C52C] 0075A71E mov ecx, [eax + 0x3EC] 0075A724 mov edx, [edx] ; 控件虚表 0075A726 mov ecx, [ecx + 0x39C] ; ecx = 控件(this) 0075A72C mov eax, [edx + 4] ; 虚表槽1 = 动作分派 0075A72F call eax ; 槽1(0x3F4, 1, 0) 0075A732 mov esp, ebp / pop ebp / ret 8 ``` **这是引擎原生的「点控件」调用**,与你的机器码逐条对应: | 你的机器码 | 游戏引擎(0x75A6FF) | |---|---| | `push 0` ×2 | `push 0` / `push 1` | | `push 0x3F4` | `push 0x3F4` | | `mov ecx, 控件` | `mov ecx, [[[0x183C52C]+0x3EC]+0x39C]` | | `mov edx,[ecx]` | `mov edx,[edx]` | | `call [edx+4]` | `call [edx+4]` | → **你的写法不是"旁门左道",就是引擎自己的手法**。双向证实。 ### 4.1 顺手拿到的新链路(活体验证候选) ``` [0x183C52C] UI 管理器单例(.data,与已知的 0x183C520 相邻) +0x3EC → 当前活动窗口 +0x39C → 当前目标控件 ``` 这条链**不依赖**「`[[NPC基址值]+0x414]` 助手窗口」那条路,是引擎级的「当前窗口 → 当前控件」旁路。值得开 CE 实测:打开助手窗口后看 `[[0x183C52C]+0x3EC]` 是否等于助手窗口句柄、`+0x39C` 是否随鼠标悬停/点击变化。 --- ## 五、`Inter*` 窗口槽1 里的 `+0x364`:父 → 子动作转发 ### 5.1 InterNpc 槽1(`0x00C7FCC0`)—— 0x3EE 转发 ×32 ``` push [ebp+0x10] push [ebp+0xC] push 0x3EE ; 通用动作码 mov edx, [ebp-0x4078] ; 窗口 this mov eax, [edx + 0x364] ; ★ [窗口+0x364] = 当前活动子对象(指针!) mov ecx, [ebp-0x4078] mov edx, [eax] ; 子对象虚表 mov ecx, [ecx + 0x364] mov eax, [edx + 4] ; 子对象虚表槽1 call eax ; 子对象.槽1(0x3EE, ...) ``` → 在 `Inter*` 窗口类上,`+0x364` 不是 ID,是**「当前活动子对象」指针**:窗口收到动作后转手递给活动子对象的槽1。这解释了为什么 `0x3EE/0x3F4` 会"跨类通用"—— 大家都在走同一条转发链。 ### 5.2 同族佐证 | 函数 | 身份 | 对 `+0x364` 的用法 | |---|---|---| | `0x006254F0` | InterAutosell 槽1(11 读) | `mov edx,[ecx+0x364]` 后当**指针**用:`mov eax,[edx+0xB3C]` | | `0x00A59C10` | InterByulho 槽1 | `movzx ecx, byte [eax+0x364]`、`[edx+0x365]` —— 字节标志 | | `0x00652520` | 高引用(12 读 + 0x3F4/0x3EE 各 1) | 混合用法 | | `0x00A3F3D0` | **InterAutoPlay 槽1** | **0 次** —— 助手分派不读 `+0x364`,ID 全靠栈上参数(这正是你 push ID 就能驱动它的原因) | --- ## 六、其余三大用户:不是控件,是别的类 | 函数 | 命中 | `+0x364` 的真实语义(证据) | |---|---|---| | `0x00723070` | 32 读 | 数据块指针族:`[obj+0x364]` → 解引用访问 `+0x53C` 数组 | | `0x00646080` | 20 读 | 同上:`mov eax,[edx+0x364]` → `cmp [eax+edx*4+0x53C], 0` | | `0x00BA6F70` | 30 读 + 3 写 | `malloc(0x37C)` 出来的结构体,`+0x364` 是其**数组首**(`[eax+edx+0x364]` 变址写) | | `0x0072F4F0` | 1 写 | 写 `0xBAADF00D` —— **CRT 堆调试标记**,与控件无关 | | `0x0064F7C0` / `0x00BA4A60` / `0x00EFFC30` | 各 1 写 | 写 `0xFFFFFFFF` —— 字段初始化为 -1 | | `0x00CBA960` / `0x00CBAC00` | 各 1 写 | 写 `0xC8FFFFFF`(-56)—— 数据字段 | > **第二课**:`+0x364` 在这 exe 里至少被 4 个类族复用:①控件 ID(`InterUI_Manufacture` 系)②`Inter*` 窗口的活动子对象指针 ③数据结构数组首 ④杂项初始化。**遍历控件时务必先用 `[ptr]` 是否落在 `.rdata` 虚表区 + RTTI 名来验明正身**,否则会把数据块当控件。 --- ## 七、push 0x3F4 / 0x3EE 全景(77 个函数) | 函数 | 0x3F4 | 0x3EE | +0x364 | 备注 | |---|---|---|---|---| | `0x00A05B30` | 16 | **78** | 0 | 0x3EE 最大户(通用动作转发器) | | `0x00A3CBC0` | 29 | 0 | 0 | | | `0x009F7F50` | 19 | 0 | 0 | | | `0x00CDABE0` | 16 | 0 | 0 | | | `0x007E63B0` | 15 | 0 | 0 | | | `0x009FC290` | 14 | 0 | **1** | ★ 联合命中 | | `0x0075CF30` | 7 | 0 | **1** | ★ 联合命中 | | `0x00C7FCC0` | 0 | 32 | **2** | ★ InterNpc 槽1(§5.1) | | `0x0068B2B0` | 4 | 0 | 0 | InterInGameTotalShop 槽1 自身 | | `0x00A3F700` | 2 | 0 | 0 | 小助手下一层分发 | **同时命中 `+0x364` 与分派特征**的 13 个函数(见脚本输出 `_s364b.txt`):`0x652520 / 0x6254F0 / 0x741710 / 0x75A310 / 0x9FC290 / 0x75CF30 / 0xC7FCC0 / 0xAFACE0 / 0xC572F0 / 0xCB33F0 / 0xCB70D0 / 0x6FC030 / 0xBED4C0` —— 这一批就是「读到控件/子对象 → 直接分派动作」的引擎枢纽,是后续逆向的最佳切入点。 --- ## 八、给你的三条实操结论 1. **写法已双证**:`push 0x3F4 → mov ecx,控件 → call [edx+4]` 就是引擎原生手法(0x75A6FF 原文对照),不用再怀疑这层。 2. **新链路待活体**:`[[0x183C52C]+0x3EC]` = 当前活动窗口、`[+0x39C]` = 当前目标控件。若成立,找控件不再依赖 `+0x414`,而且**对所有窗口通用**(不止助手)。 3. **+0x364 别盲用**:判对象四步——取 `[ptr]` → 是否 `.rdata` → RTTI 解名 → 再决定把 `+0x364` 当 ID 还是当指针。 --- ## 九、脚本与原始输出 | 脚本 | 作用 | 输出 | |---|---|---| | `scan_364.py` | 全 `.text` 扫 `disp==0x364` + 上下文反汇编 + 函数归属 | `_s364.txt` / `_s364_fn.txt` | | `scan_364b.py` | 虚表 RTTI 验证 + 0x3F4 分发原文 + 联合特征 | `_s364b.txt` | | `scan_364c.py` | 形态统计(读写/标量/变址/位宽) | `_s364c.txt` | --- *配套:《助手 0x3F4 分派机制与控件 ID 实现表》·《助手控件实机对照表(74 个)》·《全局地址与基址分析报告》* *分析对象:`D:\rxjh2025\client\YGOnline.exe` · capstone 逐指令口径*