热血江湖助手 · 分析资料站← 报告总目录助手主页
分区:13_内挂配置逆向源文件:内挂配置_保存机制_20261018.md篇幅:6.9 KB收录:2026-10-19 17:10

被封印的内挂丸 —— 配置保存机制实证报告

日期:2026-10-18 23:2x<br>样本:D:\rxjh2025\client\*.aut 共 110 个(全部 11812 字节)<br>目标程序:YGOnline.exe 15,072,256 B / i386 / ImageBase 0x400000


一、结论先给

"开启的功能"不是存在 ini / 文本 / 注册表里,而是存进一个 11812 字节的定长内存结构体,<br>退出面板或改设置时整个结构体原样 dump 到 client\<账号名>.aut。

保存动作本身走的是 C 运行库标准 I/O:

fopen("<exe目录>\client\<账号名>.aut", "r+b")   // 或 "wb"
    fwrite(&g_AutoConfig, 1, 11812, fp)          // 整块,不是逐项
fclose(fp)                                        // 内部 fflush -> SetEndOfFile 截断到 11812

底下落到 Win32:

CreateFileA  (GENERIC_READ|GENERIC_WRITE, OPEN_ALWAYS)
WriteFile    (整块 11812 字节)
SetEndOfFile (把文件精确截到 11812)
CloseHandle

证据链

#证据出处
1.rdata 里明文存在 fopen 模式串 r+bVA 0x00CE2A58
2.rdata 里明文存在路径格式串 %s\client\%s.autVA 0x00CE3918
3代入后正好得到 D:\rxjh2025\client\<账号>.aut,与实际落盘路径完全一致实证
4exe 导入 CreateFileA 36 处调用 / WriteFile 24 处 / SetFilePointer 20 处 / SetEndOfFile 全程序仅 1 处IAT 扫描
5那唯一一处 SetEndOfFile 位于 0x00C4823F,上下文是 CRT 的 _flush 实现(0x1000 分块写、errno 0xD/0x16),证明是 fflush 在收尾截断反汇编
6110 个 .aut 文件大小完全相同(11812),加上 425 个 .scs/.mhslot 也都是定长(403 / 292)全量 stat
7CRT 静态链接,所以导入表里看不到 fopen/fwrite,只能看到它底下的 kernel32导入表
第 6 条是最硬的:任何文本配置都不可能在 110 个不同账号下保持同一字节长度。
唯一解释就是「sizeof(struct) 固定 + 整块 WriteFile」。

二、文件结构

┌──────────────────────────────────────────────┐
│ 0x0000  4 B     文件头 = 6A 00 00 00 (魔数 0x6A) │
├──────────────────────────────────────────────┤
│ 0x0004  2952 B  配置槽 0(预设 1)             │
│ 0x0B8C  2952 B  配置槽 1(预设 2)             │
│ 0x1714  2952 B  配置槽 2(预设 3)             │
│ 0x229C  2952 B  配置槽 3(预设 4)             │
└──────────────────────────────────────────────┘
合计 = 4 + 2952 × 4 = 11812 ✔

槽内布局(槽基址 = 4 + 2952 × N)

槽内偏移长度内容
+0x0002固定 01 01
+0x00646C 42 01 01
+0x00C ~ +0x016112 个 float + 1 字节(≈0.66 / 0.24)
+0x024 ~ +0x0679×89 项数组(float + 4B 尾)
+0x074 ~ +0x09635★ 功能开关位图:每字节 1 个开关,0=关 / 1=开
+0x0981数量/档位(实测取值 0 / 3 / 5 / 50)
+0x100 ~ +0x45C~12×7878(0x4E) 字节定长项的列表
+0x4A8 ~ +0x550—第二组设置(+0x4B0=A、+0x4D0=A、+0x4D4/4D8=0/1、+0x4DC/4E4 float)
+0x56C ~ +0x5A629×2★ 29 项字节参数表(步长 2)
+0x654 ~ +0x84C~6×78又一个 78 字节列表
+0xB6C ~ +0xB84—尾部零散字段

开关位图的实证

110 个样本按「活动槽」统计 +0x074 ~ +0x096:

两个样本实测:

chang1.aut   开关: 0 0 1 1 0 1 1 1 1 1 1 0 0 1 0 0 1 0 ... 1 1 1 0 0
             已开启: [2,3,5,6,7,8,9,10,13,16,30,31,32]   档位=3
             参数: 15 99 25 2 99 99 99 99 99 29 2 99 99 99 30 2 5 99 40 ...

baoma001.aut 开关: 0 1 1 1 0 1 1 1 0 0 1 1 0 0 0 0 0 0 ... 1 1 0 0 0
             已开启: [1,2,3,5,6,7,10,11,30,31]            档位=0
             参数: 全 0

「参数表全 0 但开关照样有 10 个开着」—— 只有开关位图和参数表分开存储才会出现这种组合。


三、现成工具

D:/AI空间/rxjh/tools/_aut_cfg.py

from _aut_cfg import AutCfg

c = AutCfg(r"D:/rxjh2025/client/chang1.aut")
print(c.active_slot)      # 当前生效槽(0..3)
print(c.switch_on())      # 已开启的开关序号列表
print(c.params())         # 29 项参数

c.set_switch(3, 1)        # 打开 3 号开关
c.set_param(0, 20)        # 参数表第 0 项 = 20
c.set_count(5)            # 档位 = 5
c.save()                  # 原地写回,自动生成 .bak

命令行直接看:

python D:/AI空间/rxjh/tools/_aut_cfg.py D:/rxjh2025/client/chang1.aut

注意:游戏运行中改文件会被它内存里的旧结构体覆盖回去。要改必须先退游戏,<br>或者在游戏内改完再退(退出时会再写一次)。


四、还差一步:开关序号 → 功能名

35 个位里目前能确定"是开关",但哪一位对应面板上哪个勾选框,只能靠一次对照实验。

已架好零风险监控(不注入、不 hook、不改游戏代码):

D:/AI空间/rxjh/tools/_watch_save.py   (正在后台跑)
日志:D:/AI空间/rxjh/_save_snap/_watch.log
快照:D:/AI空间/rxjh/_save_snap/

它每 0.25 秒比对 client\ 下所有 .aut/.mhslot/.scs 的大小与 mtime,<br>一有写入立刻存快照并算出「改了哪几个字节、字节区间在哪」。

标定步骤:

  1. 打开内挂面板,记下当前开着哪些功能(或截图)
  2. 只改动 一个 勾选框
  3. 让面板保存(关面板 / 点保存 / 退出游戏都行)
  4. 监控会立刻报出改动的字节偏移 → 那个偏移减去 4 + 2952×活动槽 就是开关序号

做 3~5 次就能把最常用的几个对上号。剩下的按位序推断。


五、另外两种存档(附带结论)

扩展名大小性质
.mhslot292 B = 4 头 + 12 × 24快捷键/宏槽,12 个 24 字节项
.scs403 B前 16 字节是 BIG5 角色名 + 其余定长字段

两者与 .aut 同一套机制:定长结构体 + 整块 WriteFile。<br>命名规则也印在 .rdata 里:%s~%d.scs / %s%d.scs / %s-%d.scs 三种槽位形式。

本页由 Markdown 报告自动转排;如需原始 md,可在原会话工作区取用。