================================================================================ 15. 背包 / 装备物品对象 —— 「负重」字段探测(2026-09-26) ================================================================================ 目标:在背包物品对象里找出每个物品的「负重 / 重量」字段。 ------------------------------------------------------------------------------ 15.0 结论速览(先看这段) ------------------------------------------------------------------------------ ★ 结论 1:物品对象体里【没有】负重字段。 消耗品(药品/箭/丹丸)在 +0xD38~0xEB0 整片为 0。 热血江湖的消耗品本来就不占负重。 ★ 结论 2:只有【装备】填了 +0xD38~0xEB0 这一段, 这里是「装备专属属性区」。该区已完整测绘,见 15.3。 ★ 结论 3:【最像重量的字段】= 物品对象 +0x0D7C 与 +0x0D80 二者成对且值完全相同,只有装备有值,同一件装备在两格上值一致 (初學者之青銅護手 格1/格2 都是 31)→ 典型的"物品定义数据"。 取值:樂服53 / 青銅護手31 / 樂師0 / 皮短靴29。★ 高度疑似但未最终证实。 ★ 结论 4:+0x0D60 / +0x0D68 = 售价 / 收购价,已精确证实(= ×0.3)。 这条推翻了 0925 报告里"疑似重量/上限"的猜测。 ★ 结论 5:0924 那份「当前负重 = YGOnline.exe+1F3F894 +0x6C (Float)」 「负重上限 = ...+0x1F3F898 +0x70」在当前构建【已失效】—— 该指针指向未初始化堆内存(各字段读出 -1.77e38 级别垃圾值)。 ------------------------------------------------------------------------------ 15.1 复现环境 ------------------------------------------------------------------------------ 游戏 YGOnline.exe (PID 23396),CE 桥已 attach。 CE 桥本次踩坑:attach_process 之后立刻读静态基址会返回 0(缓存未清), 必须先读一次别的地址"预热",或隔一拍再读。脚本里每次运行都先 attach。 背包结构(沿用 0924/0925 实测): 背包管理器 [0x03BEF5D0] → 容器 装备容器 [0x03BEF5CC] 物品对象 = [容器 + 0x53C + 4×格号] ------------------------------------------------------------------------------ 15.2 本次枚举到的实际数据(活体) ------------------------------------------------------------------------------ 装备栏 0x03BEF5CC(5 件): 格0 0x6C2450C0 id=811300001 初學者之質樸樂服 格1 0x6C2460C8 id=1500002 初學者之青銅護手 格2 0x6C2470D0 id=1500002 初學者之青銅護手 ← 与格1 同一物品 格3 0x6C2480D8 id=800200010 樂師 格4 0x6C2490E0 id=1800003 初學者之皮短靴 背包 0x03BEF5D0(29 件):墨魂之箱子 x2 / 被封印的內掛丸 / 藥繕的金創藥 x91 雪原蔘 x103 / 藥繕的萬年雪蔘 x113 / 鐵箭 / 木箭 / 青銅箭 / 土靈符(活動) 黃龍統纏銅幣 x50 / 禮物湛藍神草 / 禮物霸王丹 x6 / 禮物太極丸 地.凝神珠(五千萬) / 地.凝神珠(一億) / 獅子吼(10)(活動) / 戀人之魂 東嶺之魂 x2 名称读取修正(推翻旧资料): 旧资料写「物品名称 = +0x64」,实测读出来不是物品名。 实测物品名在 **+0x100 附近**(首件物品 0x454FCA10:+0x100 = "墨魂之箱子")。 稳妥做法:从 +0x40 起扫 Big5 串,取【第一个】长度≥2 的串。 ★ 注意别取"最长串"—— 那会取到 tooltip 描述 (如「身體將會被內掛丸的神秘力量所支配」)。 ------------------------------------------------------------------------------ 15.3 物品对象完整字段表(本次新测绘,覆盖 0x00..0xEB0) ------------------------------------------------------------------------------ +0x000 vtable +0x00C 实例唯一序号(全局递增 9053..9081;装备另占 9230..9234) +0x028 ★ 随【背包格号】周期变化(周期6:24/57/90/123/156/189) 与物品无关,是槽位派生值,勿误当重量 +0x02C 随【背包格号】周期变化(周期5:267/302/337/372/407) +0x054 物品 ID +0x05C 唯一码 hash(大随机数,每实例不同) +0x0D4C 数量 +0x0D54 装备分类(实测 1=衣 2=護手 3=武器 4=靴 20=箭) +0x0D60 售价 ★ 已证实 +0x0D68 收购价 ★ 已证实 = +0xD60 × 0.3 +0x0D74 仅樂師有值=90 +0x0D78 仅樂師有值=74 +0x0D7C ★ 装备专属,樂服53 / 護手31 / 樂師0 / 靴29 ← 重量最强候选 +0x0D80 ★ 与 +0xD7C 完全相同 +0x0DC0 [10,3,3,45,5,4,4,0,2,3,2,...] 装备主导字段 +0x0DCC 指针/ID 型(133085/132729/132099/1536/1756...) +0x0E00 [0,0,0,32,0,0,0,0,38,40,40,45,45,45,...] +0x0E80 [78,78,78,0,78,79,79,...] +0x0E84 [46,46,46,0,46, 1, 2,...] ← 木箭=1 青銅箭=2,疑似箭矢等级 +0x0E8C [29,23,23,0,23,99,78,...] 消耗品在 +0xD38~0xEB0 的其余字段一律为 0 —— 这一段是装备独占的。 ★ +0x0D60 是售价的铁证(全部精确 ×0.3): 627000 → 188100 10000 → 3000 4800 → 1440 600 → 180 5100 → 1530 200000 → 60000 2000000→ 600000 ------------------------------------------------------------------------------ 15.4 排除掉的几条死路(避免重复踩) ------------------------------------------------------------------------------ ✗ 路线 A —— 静态「物品定义表」 0925 报告称 0x2074xxxx 是静态物品定义区(獸項鍊 在此)。 本次复核:獸項鍊(C3 7E B6 B5 C1 E5) 现在全内存只有 2 处命中, 都在 0x2BA9A2FC / 0x2BAA0A28(堆区),0x2074xxxx 已经搜不到。 → 0925「A 类静态定义区」的判定不成立,该结论应作废。 补充:0x2BA9A2FC 的条目里,"盛獸項鍊"后面紧跟 `28 B1 F6 AC 68 AF 75 29` = Big5「(梅柳真)」—— 是角色名, 说明这仍是一块动态构建的串池,不是定长定义表。 ✗ 路线 B —— UI 文本反查 搜「負重」「載重」「重量」的 2 字节模式各命中 1000 处(撞 max_results 上限),纯噪声;3 字节模式(重量+空格)只有零星命中,上下文全是乱码, 不是有效 UI 串。 搜「重量上限」「背負重量」= 0 处。 ✗ 路线 C —— 国服辅助 D:\BB 交叉验证 rxbb.dll / Data.dll / Data1.dll / bg.dll / Addr.dll / Config.ini 六个文件全部 grep「重量|负重|載重|體重|质量」→ 0 命中。 → 热血江湖客户端根本没有"负重"这套 UI 概念。 ✗ 路线 D —— 玩家结构体里的「当前负重」 0924 资料:当前负重 = [YGOnline.exe+1F3F894] +0x6C (Float) 负重上限 = [YGOnline.exe+1F3F898] +0x70 (Float) 按模块基址 0x400000 换算 → [0x0233F894]。 实测该指针 = 0x44468000,但 +0x3C..+0x78 全部读出 -1.77e38 级别的垃圾浮点 → 指向未初始化堆内存,基址已失效。 (本次两次把绝对地址算错:先误用 0x01F3F894 得 0,后误算成 0x0233F896 量级;最终 0x400000+0x1F3F894 = 0x0233F894 是对的。) ------------------------------------------------------------------------------ 15.5 本次交付脚本 ------------------------------------------------------------------------------ bag_weight_dump.py 背包物品对象全字段 dump(0x00..0x600 差异分析) 输出 _bagdump.out bag_weight2.py 补扫 0x600..0x1100 —— 这次才扫到 +0xD4C/+0xD60 区段 bag_weight3.py 装备+背包合并字段差异(含名称读取修正) bag_weight_final.py 装备专属区 0xD30..0xEB0 完整对照表 ★ 主要产出 输出 _wgf.out bag_w_search.py 獸項鍊落点复核(证明 0x2074 定义表结论作废) bag_w_def.py 獸項鍊新落点结构勘察 bag_weight_ui.py UI 文本反查 + 装备栏 dump ------------------------------------------------------------------------------ 15.6 下一步(按性价比排序) ------------------------------------------------------------------------------ 1. ★ 证实 +0x0D7C 是不是重量。 做法:在游戏里换一件已知重量的装备(或脱下再穿上), 对比 角色 → 装備对应槽位对象 的 +0x0D7C 是否随之改变。 若改变 → 它是"实例态重量";若不变 → 它是"定义态重量"。 2. 修好玩家全局结构体基址(0x23F3894 指向垃圾)。 有了它就能直接读「当前负重 / 负重上限」, 再用"总负重 − Σ已知装备重量"反推出单个物品重量。 入手点:搜 8 字节「当前金钱」或活体反推(XIGNCODE 已清 DR, 不能用 find_what_accesses,得走静态代码分析)。 3. 若确认没有负重系统:把 +0xD60/+0xD68 写死为「售价/收购价」, 并补测 装备对象的 +0x0DC0/+0x0E00 含义(疑似攻防/部位等级)。 ================================================================================ ================================================================================ 16. 「指定物品负重值」—— 静态反查(2026-09-26 晚) ================================================================================ 用户诉求转为【写入/设定】负重值。游戏未开、CE 未附加,改用**纯静态分析** (直接解析 YGOnline.exe,不依赖 CE 活体),结果比活体更硬。 ------------------------------------------------------------------------------ 16.1 方法:PE 静态代码扫描(新技能,可复用) ------------------------------------------------------------------------------ 不查 CE,直接读文件解析 PE: e_lfanew@0x3C → NumberOfSections@+6、SizeOfOptionalHeader@+20 **节表偏移 = e_lfanew + 24 + SizeOfOptionalHeader**(不是 +24+96,那个是 DataDirectory,踩过一次,节表会全乱) 本次 5 节:.text VA=0x1000 VS=0xCAAEE1 / .rdata / .data / .fptable / .rsrc 扫描编码:x86 的 `[reg + disp32]` 在 .text 里的固定写法 `8B 86`=mov eax,[esi+d] `8B 87`=eax,[edi+d] `8B 8D`=ecx,[ebp+d] `8B 95`=edx,[ebp+d] `8B 96`=edx,[esi+d] `8B 83`=eax,[ebx+d] `8B 90`=edx,[eax+d] `8B 98`=ebx,[eax+d] 加 0F B7 / 66 8B 覆盖 word ------------------------------------------------------------------------------ 16.2 ★ +0xD7C 的真相:只是一份「镜像」,不是负重 ------------------------------------------------------------------------------ 全 .text 扫 +0xD7C,**只命中 1 条**: 0x00D599BE 8B 90 7C 0D 00 00 mov edx,[eax+0xD7C] 0x00D599C5 89 91 7C 0D 00 00 mov [ecx+0xD7C],edx ← 立刻写回 上下文(0x00D59986..0x00D599FF)是一段**四字段拷贝函数**: 8B 82 30 03 00 00 mov eax,[edx+0x330] 8B 90 7C 0D 00 00 mov edx,[eax+0xD7C] 89 91 7C 0D 00 00 mov [ecx+0xD7C],edx ... 8B 81 78 0D 00 00 mov eax,[ecx+0xD78] 89 82 78 0D 00 00 mov [edx+0xD78],eax 8B 8A 74 0D 00 00 mov ecx,[edx+0xD74] 89 88 74 0D 00 00 mov [eax+0xD74],ecx → +0xD74 / +0xD78 / +0xD7C / +0xD80 是**同一组**,只会被"整组复制"。 → 结论:物品对象里的这四个值是**从模板镜像过来的静态属性副本**, **游戏逻辑从不读取它做运算**。改它,UI 不会有任何反应。 (0926 第 15 节把 +0xD7C 当"重量最强候选"—— 此判断作废。) ------------------------------------------------------------------------------ 16.3 ★ 旧版辅助里藏着完整「人物结构体」,但当前构建大半失效 ------------------------------------------------------------------------------ 来源:D:\AI空间\rxjh\旧版本更新.代码\自定义类型.e.txt(2023 版辅助) .成员 愤怒值 +88 .成员 心/体/气/魂 +B0/+B4/+B8/+BC .成员 攻击力 +C8 .成员 防御力 +CC 命中 +D0 回避 +D4 .成员 金钱 +E4 .成员 最大负重 +EC .成员 负重 +EE .成员 气功点数 +F0 基址 = 常量「当前血量」= 0x03970A70(绝对地址 → 0x400000+0x3970A70) 静态验证: +0xEE(负重) → **.text 零命中** ✗ 当前构建已不是字段 +0xEC(最大负重) → 17 处命中(0x0049E6C2 / 0x004DC1E1 / 0x004FA8D1 等) ★ 该字段仍然有效 样例 0x0049E6C2: mov eax,[ebx+0xEC] / and eax,0xE7 / or eax,ecx / mov [ebx+0xEC],ecx(读+写) 样例 0x004DC1E1: mov eax,[esi+0xEC] / lea eax,[eax+eax*4] / add [eax],2 → 像是 5 步长数组 0x03970A70(人物基址)→ **零命中** ✗ 旧基址彻底失效 +0xE4(金钱) → 零命中(可能走了别的编码) ------------------------------------------------------------------------------ 16.4 找到新锚点:0x0188C60C(玩家全局指针) ------------------------------------------------------------------------------ 静态扫绝对寻址引用(A1 / 8B 15 / 8B 0D / 8B 3D / 8B 1D / 8B 05 / ...): 0x0188C60C → 10+ 处,0x0058B993 / 0x006A75A2 / 0x006B8D28 ... 与任务根 0x0188C61C 仅差 0x10(同一静态块内) 偏移使用统计([reg+disp] 中 0x300~0x400 区间的 disp 频次): **+0x330 x1588** ← 压倒性高频 = 玩家结构体里的【物品容器管理器】 +0x334 x574 +0x338 x394 +0x33C x331 +0x34C x358 +0x354 x223 +0x350 x190 +0x31C x76 +0x318 x86 → 0x0188C60C 就是玩家全局结构体指针;+0x330 = 背包/装备/仓库管理器。 (0926 上下文 0x00D599BE 里的 `mov eax,[0x0188C60C]` + `mov ecx,[eax+0x330]` 正好印证:玩家 → +0x330 → 容器 → 物品对象,整条链打通。) ------------------------------------------------------------------------------ 16.5 交付:bag_weight_set.py(物品负重设定器) ------------------------------------------------------------------------------ python bag_weight_set.py 只读快照(枚举背包+装备) python bag_weight_set.py --name 青銅護手 --weight 999 python bag_weight_set.py --slot 3 --weight 999 python bag_weight_set.py --equip 1 --weight 0 python bag_weight_set.py --off 0xD7C --slot 3 --weight 888 (指定偏移) python bag_weight_set.py --undo 还原(自动备份) * 写 0xD74/0xD78/0xD7C/0xD80 一组(静态证实是同组字段) * 写入前备份原值到 bag_weight_backup.json,--undo 还原 * 容器地址:背包 0x03BEF5D0 / 装备 0x03BEF5DC,槽位指针 +4*slot+0x53C ⚠ 由于 16.2 已证明 +0xD7C 只是镜像,写入后**大概率看不到任何变化**—— 若确实无变化,说明热血江湖的负重由服务端计算,客户端内存写不动; 此时唯一可行的"设定负重"是直接改玩家结构体里的负重字段(待定位)。 ------------------------------------------------------------------------------ 16.6 本轮踩坑(已入 skill) ------------------------------------------------------------------------------ 1. **512 次连续 aob_scan 把 CE 桥管道打死**(OSError: [Errno22] Invalid argument '\.\pipe\ce_mcp_bridge')。桥崩后 `_wbpy.cmd()` 直接抛,**不再重试**。 → aob_scan 单次最多 ~60 条,且必须分批 sleep;大批扫描改走静态分析。 2. CE 关了 / 游戏关了 时管道同样是这个错,别误判成"桥坏了"。 3. PE 节表偏移 = 节表紧跟 OptionalHeader,漏加 SizeOfOptionalHeader 会让 整个节表解析错位(表现为节名乱码 + 命中全 0)。 4. 静态扫 disp32 时不要只扫 `?? ??` 通配前缀:2 字节通配 ≈ 1/65536 误报率, 800 万字节的 .text 直接灌满 200 命中上限,全成噪音。必须用精确 2 字节前缀。 ------------------------------------------------------------------------------ 16.7 下一步 ------------------------------------------------------------------------------ 1. 开游戏 + 挂 CE 跑 bridge,先跑 `bag_weight_set.py` 拿只读快照。 2. 在人物面板/背包里记录当前负重数字,再**穿脱一件装备**,对玩家结构体 0x0188C60C 指向的对象做**差分扫描**(哪个 dword 变了 = 负重)。 3. 若确认负重字段,直接写它 —— 这才是"指定负重"的正路。 4. 顺带:0x0188C60C 是比 0x03BEF5D0 更稳的入口(+0x330 → 容器 → 物品)。