分析对象:Torchlight2.exe(静态基址 0x400000)
方案层级:可执行 O1 观测规范(机器指令级调用现场与测试用例),待实机动态采样确认
数据登记:原版游戏分析/reversed_cpp/REGISTRY/o1_sampling_recipes.json

1. 结论速览 ​

为彻底验证反编译提取的硬编码缺省值,全部 35 个候选未采样字段已建立起机器指令级的动态观测方案。需明确区分配方定义与实测采样的状态区别:

  • 配方已建立:明确掌握每个配置键的调用现场断点、返回载体寄存器、无键测试样本以及真假值区分依据。
  • 默认值获 O1 证实:必须附加调试器或通过运行时 Hook 实际启动游戏并采集到有效返回值后,方可将对应字段升档为实测已证实状态。

经系统梳理,在 35 个候选字段中,除已有成熟方案的项外,本次重新标定并构建高置信度观测配方的字段共计 24 项。

2. 采用 Getter 返回值直接观测的动因 ​

“加载完后读对象字段”并不总能回答“缺键时 getter 的默认值是什么”。已经发现四类破坏判别力的情况:

  1. 哨兵值写入字段后立刻被派生值覆盖,例如 MANACOST、LEVEL_REQUIRED。
  2. 条件分支跳过字段写入,例如元素护甲值为 -1 时不写槽位。
  3. 后续流程重新缩放并写回字段,例如精英怪的 XP、ARMOR。
  4. 0、空串等默认值与构造函数初值同形,最终字段无法说明 getter 当时返回了什么。

因此,本轮 24 条配方全部把观测点前移到 CDataGroup__getXxx 的返回瞬间:

text
缺少目标键的 DAT
  → CDataGroup__getXxx(key, hardcoded_default)
  → 在 call 的下一条指令处停住
  → 直接读取返回值

返回约定如下:

getter观测值
getIntEAX
getBoolAL
getFloatx87 ST(0)
getStringEAX 指向的 std::wstring;复制对象或读取其长度/内容

这条路径不经过字段写回、取整、钳位、曲线求值或精英缩放,所以候选默认值不同,返回读数必然不同。

3. 精确观测表 ​

表内地址是静态 VA;运行时存在 ASLR,应使用 Torchlight2 + (VA - 0x400000)。call bytes 同时作为版本/地址漂移保护。

3.1 怪物字段 ​

字段call → returncall bytes返回值预期 / 排除缺键样本
MINHP0x55D0A4 → 0x55D0A9E8 F7 AE 11 00ST(0)1.0 / 0.0MONSTERS/QUESTUNITS/A3_GHOST_CART.DAT
MAXHP0x55D0DB → 0x55D0E0E8 C0 AE 11 00ST(0)1.0 / 0.0同上
MINDAMAGE0x55D8A9 → 0x55D8AEE8 D2 A6 11 00EAX0 / 1同上
MAXDAMAGE0x55D8ED → 0x55D8F2E8 8E A6 11 00EAX0 / 1同上
HEALTH_GRAPH0x55D23B → 0x55D240E8 C0 AC 11 00EAX → wstringlength=0 / length>0MONSTERS/ARMADAX/ARMADAX.DAT
ARMOR_GRAPH0x55D5D0 → 0x55D5D5E8 2B A9 11 00EAX → wstringlength=0 / length>0同上
DAMAGE_GRAPH0x55D947 → 0x55D94CE8 B4 A5 11 00EAX → wstringlength=0 / length>0同上
CHAMPION_SCALE0x522CAA → 0x522CAFE8 81 52 15 00AL1(true) / 0(false)同上
ARMOR_MAGICAL0x55D7DC → 0x55D7E1E8 9F A7 11 00EAX-1 / 0MONSTERS/ARMADAX/ARMADAX.DAT
ARMOR_FIRE0x55D7DC → 0x55D7E1同上EAX-1 / 0MONSTERS/ARMOR_RACK/ARMOR_RACK.DAT
ARMOR_ICE0x55D7DC → 0x55D7E1同上EAX-1 / 0同上
ARMOR_ELECTRIC0x55D7DC → 0x55D7E1同上EAX-1 / 0同上
ARMOR_POISON0x55D7DC → 0x55D7E1同上EAX-1 / 0同上
ARMOR_ALL0x55D7DC → 0x55D7E1同上EAX-1 / 0MONSTERS/ARMADAX/ARMADAX.DAT

六个 ARMOR_* 共享一个调用点不是抄写错误。可执行文件只保存 ARMOR_ 前缀,完整键在循环里动态拼接;采样时必须读取/记录本次传入的运行时键,并只接受与目标字段同名的那次命中。ARMOR_MAGICAL 与 ARMOR_ALL 在 EXE 中没有完整宽字符串字面量,因此不能为它们伪造独立 literal xref。

3.2 物品字段 ​

字段call → returncall bytes返回值预期 / 排除缺键样本
MINDAMAGE0x58F106 → 0x58F10BE8 75 8E 0E 00EAX0 / 1ITEMS/BASE.DAT
MAXDAMAGE0x58F14B → 0x58F150E8 30 8E 0E 00EAX0 / 1同上
RARITY_DMG_MOD0x58F686 → 0x58F68BE8 F5 88 0E 00EAX100 / 0同上
SPEED_DMG_MOD0x58F6D5 → 0x58F6DAE8 A6 88 0E 00EAX100 / 0ITEMS/2HAXE/BASE_2HAXE.DAT
SPEED0x58F45C → 0x58F461E8 1F 8B 0E 00EAX100 / 0同上
RARITY_AMR_MOD0x58F4AB → 0x58F4B0E8 D0 8A 0E 00EAX100 / 0同上
SPECIAL_AMR_MOD0x58F4FA → 0x58F4FFE8 81 8A 0E 00EAX100 / 0同上

四个百分比修正值的 100 是乘法单位元。不要用最终伤害/护甲反推:100% 与“某段乘法没有执行”可能同形;必须直接读 getter 的 EAX。

3.3 技能字段 ​

字段call → returncall bytes返回值预期 / 排除缺键样本
TARGET_TYPE0x6DC6F0 → 0x6DC6F5E8 0B B8 F9 FFEAX → wstringlength=0 / length>0SKILLS/ALCHEMIST/EMBERBEAM/EMBERBEAM.DAT
TARGET_UNITTYPE0x6DC88C → 0x6DC891E8 6F B6 F9 FFEAX → wstringlength=0 / length>0同上
REQUIREMENT_LEFT0x6DCFFA → 0x6DCFFFE8 01 AF F9 FFEAX → wstringlength=0 / length>0同上

这三个默认值都是运行时构造的空 std::wstring,其存储位于 BSS,不能靠 EXE 文件里的静态内容证明为空。正确的 O1 观测是在 getter 返回点复制字符串对象或读取其运行期长度。

4. 安全执行步骤 ​

以 MINHP 为例,返回点的 RVA 是 0x15D0A9:

text
bu Torchlight2+15D0A9
g

命中后再单独查看 ST(0)。整数/布尔字段分别查看 EAX/AL。字符串建议由现有 runtime-mod 的 detour 包装器复制返回的 std::wstring;若使用调试器,也必须先让断点停住,再单独检查 EAX 所指对象。

安全约束:

  • 调试器附加时使用 -pd,避免调试器退出连带终止游戏。
  • 断点命令链只负责停住;不要把 dd、du 等内存读取与自动 gc 塞进命令链。
  • 每次观测先确认加载的是表中缺键样本,并按 BASEFILE 继承链复核目标键确实缺席。
  • 元素护甲的共享点必须过滤动态键名,否则一次循环中的其他元素会污染结果。
  • 采到值后保存:游戏版本/EXE 哈希、模块基址、静态 VA、运行时地址、样本路径、键是否经继承后缺席、原始返回值。

5. 门禁与可复现性 ​

tools/o1_recipe_gate.py 现在会执行以下检查:

  1. 每个 O2_unsampled 行必须有 o1_recipe、observe、discriminates,缺一即失败。
  2. 目录型配方必须同时给出 call、return、返回载体、call bytes 和样本路径。
  3. call bytes 必须与发行 EXE 逐字节相同,且 return == call + 指令长度。
  4. 字面量键的 call 必须是该键实际的 getter 现场。
  5. 动态 ARMOR_* 键必须显式声明过滤键名,并验证共享调用点机器码。
  6. 已证伪配方不再是 O2_unsampled 的豁免符;四条证伪记录都必须附替代方案。

执行:

powershell
$env:PYTHONIOENCODING='utf-8'
python tools/o1_recipe_gate.py
python -m unittest tests.truth.test_o1_recipe_gate

6. 对逆向与重制实现的约束 ​

在未完成实机 O1 动态采样前,相关字段仍保留静态推导属性,不可仅凭观测方案就绪而提前标记为动态已验证。逆向工程与数据重制中应继续使用反编译分析确定的缺省值,并保留可审计标记;一旦动态采样读数与静态预期产生分歧,应以动态实测数据为准修正默认值表与单元测试。

本规范的意义在于为后续动态验证建立明确的机器级断点约束与自动化校验门禁:任何新增的未采样字段均须配备完整且经过二进制比对的调用方案,确保每项默认值的逆向结论均可复现、可追溯。