← 返回总索引「小助手开」内存地址定位报告2026-09-16-00-24-33\小助手开_内存地址定位.html

热血江湖(台服) ·「小助手开」汇编内存地址定位报告

目标文件:D:\rxjh2025\client\YGOnline.exe ImageBase = 0x00400000(所有地址均为 VA / 内存虚拟地址)。依据你提供的易语言子程序(特征码 A、特征码 B、call 0xA3F700)在主程序中静态定位。

一、核心地址总表

名称内存地址 (VA)含义 / 确认情况
特征码ANPC对话基址0x00668955命中 1 处(主程序 .text,非助手模块)。紧邻前一条指令 mov ecx,[0x183c52c] 揭示了 NPC 管理器全局 = 0x183c52c
NPC 管理器全局0x183c52c特征码 A 前一条立即解出的全局指针,保存 NPC 对话对象容器
特征码B小助手call基址0x00a3f676命中 1 处,位于包裹函数 sub_a3f3d0 内部
包裹函数 prologue0x00a3f3d0含特征码 B 的"助手命令路由"函数起点(ret 0xc,止于 0xa3f6f5)
包裹函数内 call 0xA3F7000x00a3f6e6E8 15 00 00 00 → 目标 0xA3F700。即游戏本体启用助手的官方调用点(特征码 B +0x70)
助手中央分发器0x00A3F700你硬编码的 call 目标。已确认正确。ecx=this,[ebp+8]=命令字
命令字 0x31 处理函数0x00a406b4跳转表索引 0x0f 指向的"小助手开"实现体。可直接 CALL 它(ecx=对话对象)省去分发
分发跳转表(函数地址)0x00a41588256×4 字节,命令 handler 表
分发索引表(字节)0x00a41698256 字节,命令字-0xb 作下标取 handler 索引

二、调用链(你要找的"内存位置"全貌)

易语言「小助手开」
  └─ ECX = [NPC_base + 0x414]  ← 对话对象(this)
  └─ push 0 / push 0x31 / mov ecx=[ebp-4] / mov eax,0xA3F700 / call eax
      ↓
sub_A3F700 (0xA3F700) [ecx=对话对象, [ebp+8]=命令字 0x31]
  └─ sub ecx,0xb → 查 0xa41698 索引表 → 查 0xa41588 函数表
      ↓ jmp
handler 0xa406b4 ← "小助手开"最终实现(ecx 仍为对话对象)

【游戏本体等价路径】
sub_a3f3d0 (0xa3f3d0, 含特征码B) → 0xa3f6e6 call 0xA3F700 → 同上

三、特征码 A 详解(NPC对话基址)

0x0066894f mov ecx, dword ptr [0x183c52c] ; ← NPC 管理器全局 0x00668955 mov edx, dword ptr [ecx + 0x3ec] ; [mgr+0x3ec] 0x0066895b mov eax, dword ptr [edx + 0x390] ; [..+0x390] 0x00668961 movzx ecx, byte ptr [eax + 0x6c8] ; [..+0x6c8] 对话标志字节 0x00668968 test ecx, ecx

你代码里 ECX = [NPC_base + 0x414] 取到的"对话对象",正是传给 0xA3F700 的 this。游戏在 0xA3F700 一开头就读 [this+0x34c9](0xa3f75c)作开关校验——对话对象打开时该字节非零,校验即通过,所以必须传对话对象而非随便一个指针。NPC 管理器全局实测为 0x183c52c

四、特征码 B 详解(小助手call基址)

0x00a3f676 lea ecx, [edx + eax - 1] ; ← 特征码 B 起点 0x00a3f67a mov edx, dword ptr [ebp - 4] 0x00a3f67d mov eax, dword ptr [edx + 0x34b4] ; [this+0x34b4] = 助手对象 0x00a3f683 mov dword ptr [eax + 0x2c], ecx ; 写 助手对象+0x2c ...(略)... 0x00a3f6e6 call 0xa3f700 ; ← 包裹函数在此调用中央分发器

对应关系:特征码 B 在 0xa3f676,其内部 call 0xa3f7000xa3f6e6(偏移 +0x70),call 的目标正是 0xA3F700(偏移 +0x8A)。你用特征码 B 定位助手 call、再硬编码 0xA3F700 的写法完全成立。差异仅在于:包裹函数用"助手管理器(this+0x34b4)"作为 ecx,而你用"对话对象"作为 ecx——两者都能过 0xA3F700 入口的 this+0x34c9 校验。

五、0xA3F700 中央分发器入口反汇编

0xa3f700 push ebp 0xa3f701 mov ebp, esp 0xa3f714 mov [ebp-0xf8], ecx ; 保存 this(对话对象) 0xa3f71a mov eax, dword ptr [ebp + 8] ; eax = 第一个参数 = 命令字 0xa3f723 sub ecx, 0xb ; 命令字 - 0xb 0xa3f732 cmp [ebp-0x114], 0x99 0xa3f73c ja 0xa41204 ; 越界→默认处理 0xa3f748 movzx eax, byte ptr [edx + 0xa41698] ; 索引表 0xa3f74f jmp dword ptr [eax*4 + 0xa41588] ; 跳到对应 handler

命令字范围约 0x0b–0xa4。实测部分命令→handler(节选):

命令字索引handler说明
0x280x080xa3fac0
0x290x090xa3faf2
0x2a0x0a0xa3fb24
0x2b0x0b0xa3fb56
0x2c0x0c0xa3fb88
0x2d0x0d0xa3fbec
0x2f0x0e0xa3fc1e
0x310x0f0xa406b4★ 小助手开(启用)
0x320x100xa40ca9
0x330x110xa40d4e
0x340x120xa40da5
0x350x130xa40d28
0x370x150xa3fc50
0x390x160xa3fc82
0x3b0x170xa40424

六、对你易语言子程序的逐行确认

你的代码定位结论
ECX = 读整数([#NPC基址值] + 0x414)NPC 管理器全局实测 0x183c52c;+0x414 得到对话对象,作为 0xA3F700 的 this正确
特征码 A(NPC对话基址)命中 0x00668955,链路 [mgr+0x3ec]→[+0x390]→[+0x6c8] 与你的 +0x414 取对话对象是同一对象的两种访问方式。正确
特征码 B(小助手call基址)命中 0xa3f676,其所在函数 0xa3f3d0 在 0xa3f6e6call 0xA3F700正确
mov eax,0xA3F700 ; call eax0xA3F700 = 助手中央分发器,命令字取自 [ebp+8]=0x31。正确
push 0 ; push 0x310x31 = 启用助手命令 → handler 0xa406b4正确
mov ecx,[ebp-4](对话对象给 ecx)满足 0xA3F700 入口 this+0x34c9 校验(对话打开时非零)。关键前提:调用时必须有 NPC 对话窗口打开,否则 0x34c9 为 0 会走 0xcb55c0 分支

七、可优化点(给你参考)

1. 直达写法:可绕过分发器直接 CALL 0xa406b4(ecx=对话对象,无需 push 0 / push 0x31),更短更稳。但注意 0xa406b4 是 jmp 进来的 handler(自带 ret),直接 call 后需确认其栈平衡(stdcall,无额外参数,ret 即可)。
2. 特征码 B 动态定位更稳健:扫到 0xa3f676 后,从 0xa3f6e6 读 E8 的 rel32 反推 call 目标,这样即使 exe 重打包导致 0xA3F700 漂移也能自适应。
3. 关闭助手:若需要"小助手关",对应命令字应在 0x2x–0x3x 区间内另有其 handler(0xa406b4 是"开"),可用同样方法在 0xa41588 表里逆向查。需要的话我帮你把"开/关/各分页"全部列出来。