# 2026-10-12 阶段 2(0.1.14)协议补全、事件包分层与动作回包关联审计报告 - 审计日期:2026-10-12 - 目标二进制:`E:\SteamLibrary\steamapps\common\FINAL FANTASY XIV Online\game\ffxiv_dx11.exe` - 客户端版本:`2026.09.15.0000.0000` - 二进制 SHA256:`5BBC501DD5C7F22FD61A11D08C25356041D878DB7CD83203ADAE393E4DFACC44` - 样本文件:`capture-018.jsonl`(SHA256 `407B6933E0886AA567C7A585B2732BA8064DC5B731987EB3744F14F4296AF639`,891 条完整记录) - 映射缓存:`opcodes.json`(SHA256 `6B40585D43CC83C31EC16A8DD9CF2B3008D3084752472BD5E89B4D20C6C14F7E`) - 离线回放报告:`diagnostics/2026-10-12-capture-018-v0114.json` ## 1. 上行构造与发送包装层核实 ### 1.1 原生发送结构体与包装函数(为阶段 3 M4 预备证据) - `ZoneClient.SendPacket`:`0x1D03040`(内部转调 `ZoneUp` `0x1D718C0`)。 - `NetworkModuleProxy` 发送包装层:`0x1CFD090`、`0x1CFD140`(由 `0x4810E0` 调用)、`0x1CFD170`(由 `0x481100` 调用)、`0x1CFD1A0`(由 `0x4810C0` 调用)。 - 原生发送结构体(`rdx`):`+0x00 uint Opcode`、`+0x08 ulong TotalSizeWithHeader`(等于 `payloadLength + 0x10`)、`+0x20 byte[] Payload`。 ### 1.2 `UP_ActionSend`(`913` / `0x391`,载荷 32 字节)與 `UP_ActionSendPos`(`891` / `0x37B`,载荷 24 字节) - 构造调用链:`0x8E21A3` → `0xB36F50`(`UP_ActionSend`,总长 `0x30` = 载荷 `0x20`);`0x8E2255` → `0xB37050`(`UP_ActionSendPos`,总长 `0x28` = 载荷 `0x18`,旧版 `UpPacketStructs.cs` 记为 `0x20` 已过期)。 - 共用头部(`+0x00..+0x0B`): - `+0x00 uint ActionId` - `+0x04 byte CastBuff` - `+0x05 byte ActionKind` - `+0x06 ushort RequestId`(样本 27 条递增为 `179..205`) - `+0x08 ushort Facing` - `+0x0A ushort TargetFacing` - `UP_ActionSend` 尾部:`+0x10 ulong TargetId`、`+0x18 uint Arg`(共 32B)。 - `UP_ActionSendPos` 尾部:`+0x0C float X`、`+0x10 float Y`、`+0x14 float Z`(共 24B,校验有限浮点数)。 ### 1.3 `UP_ClientTrigger`(`391` / `0x187`,载荷 32 字节) - 构造器:`0xB352F0`、`0xB353B0`、`0xB35550`(总长 `0x30` = 载荷 `0x20`)。 - 载荷布局:`+0x00 uint CommandId`、`+0x04..+0x10 uint Arg0..Arg3`、`+0x18 ulong TargetId`。 ### 1.4 P2 变长事件包冲突解决(`UP_EventFinish*` 与 `UP_EventAction*`) - 旧版 `UpPacketStructs.cs` 将 `UP_EventFinish` 与 `UP_EventAction` 写成单一固定长度结构,与在线 `opcodes.json` 的变长后缀表冲突。 - 原生反汇编确认客户端按 `ArgCount`(`r9b`)分成 8 档固定长度 Opcode 发送: - `UP_EventFinish*`(`0xB32C5C..0xB33022`):`UP_EventFinish`(`982`,`cap=2`,16B)、`4`(`607`,24B)、`8`(`516`,40B)、`16`(`856`,72B)、`32`(`283`,136B)、`64`(`787`,264B)、`128`(`779`,520B)、`255`(`658`,1032B,含 4B 对齐尾填充)。 - `UP_EventAction*`(`0xB33050..0xB333DA`):`UP_EventAction`(`140`,`cap=2`,16B)、`4`(`778`,24B)、`8`(`938`,40B)、`16`(`957`,72B)、`32`(`637`,136B)、`64`(`988`,264B)、`128`(`703`,520B)、`255`(`705`,1032B)。 - 共用 8 字节头部:`+0x00 uint EventId`、`+0x04 ushort SceneId`、`+0x06 byte State`、`+0x07 byte ArgCount`(校验 `ArgCount <= capacity`),`+0x08` 起为 `uint Args[capacity]`。 ## 2. 下行 `PacketDispatcher`(`0x18D9500`,跳表 `0x18DD980`)核实 | 名称 | Opcode | 长度 | 跳表入口与消费函数 | 已核实字段与旧版差异 | | --- | --- | --- | --- | --- | | `DOWN_ActorControl` | `607` (`0x25F`) | 24B (`0x18`) | 预处理 `0x18DA1EC`;跳表 `0x18DAABB` → `0x18DAB03` → `0xB3A350` | `+0x00 ushort Category`,`+0x04..+0x10 uint Arg0..Arg3`。当 `Category == 0x22` 时,采集发生在 `0x18DA1EC` 减去会话值 `esi` 之前 | | `DOWN_ActorControlSelf` | `516` (`0x204`) | 40B (`0x28`) | 跳表 `0x18DAB9E` → `0x18DABE6` → `0xB3A350` | `+0x00 ushort Category`,`+0x04..+0x20 uint Arg0..Arg7`(旧版 32B 仅 6 个参数,现为 8 个 `uint`) | | `DOWN_ActorControlTarget` | `856` (`0x358`) | 32B (`0x20`) | 跳表 `0x18DAB4D` → `0x18DAB95` → `0xB3A350` | `+0x00 ushort Category`,`+0x04..+0x10 uint Arg0..Arg3`,`+0x18 ulong TargetId` | | `DOWN_Effect` | `787` (`0x313`) | 124B (`0x7C`) | 预处理 `0x18DA4CC`;跳表 `0x18DA9C6` → `0xB3A0F0` → `0x902C20` | 旧版 120B 已变为 124B。`+0x08 uint RawActionId`(采集时含会话偏移 `esi`),`+0x0C uint Sequence`,`+0x10 float LockTime`(有限数),`+0x18 ushort RequestId`,`+0x1C ushort AnimationId`,`+0x1F byte ActionKind`,`+0x21 byte TargetCount`(`<=1`),`+0x70 ulong TargetId` | | `DOWN_EffectResult` | `305` (`0x131`) | 96B (`0x60`) | 跳表 `0x18DAF52` → `0xB38CA0` → `0x8A6BF0` | `+0x00 byte Count`(`<=1`),`+0x04 uint Sequence`,`+0x0C uint CurrentHp`,`+0x10 uint MaxHp`,`+0x14 ushort Mp`,`+0x16 byte TargetIndex`,`+0x17 byte ClassJob`,`+0x18 byte Shield`,`+0x19 byte StatusCount`(`<=4`),`+0x1C` 起 4×16B 状态项(内部调用 `StatusManager.SetStatus` `0x8A6BF0`) | | `DOWN_StatusEffectList` | `131` (`0x83`) | 384B (`0x180`) | 预处理 `0x18D97FC`;跳表 `0x18DAE4B` → `0xB385F0` → `0x8A78F0` | `+0x00 byte ClassJob`,`+0x01 byte Level`,`+0x04 uint CurrentHp`,`+0x08 uint MaxHp`,`+0x0C ushort Resource`,`+0x0E ushort MaxResource`,`+0x10 byte Shield`,`+0x14` 起 30×12B 状态槽(采集时 `+0x00 ushort RawStatusId` 尚未减去 `si`,`+0x04 float Timer` 校验有限数) | | `DOWN_ActorDelete` | `974` (`0x3CE`) | 8B (`0x08`) | 跳表 `0x18DA75C` → `0xB37C00` | `+0x00 byte ObjectIndex` | | `DOWN_ObjectSpawn` | `238` (`0xEE`) | 64B (`0x40`) | 跳表 `0x18DAD80` → `0xB4ADA0` (`0xB4B057`) | `+0x00 byte ObjectIndex`,`+0x01 byte Kind`,`+0x04 uint BaseId`,`+0x08 uint EntityId`,`+0x1C float Scale`,`+0x34 float X, +0x38 float Y, +0x3C float Z`(旧版坐标偏移 `0x30` 错误) | | `DOWN_NpcSpawn` | `524` (`0x20C`) | 656B (`0x290`) | 跳表 `0x18DA6FA` → `0xB37A10` → `0x8CF750`(`rsi = payload + 0x10`) | `+0x40 uint BaseId`,`+0x54 uint OwnerId`,`+0x5C uint MaxHp`,`+0x60 uint CurrentHp`(旧版 HP 颠倒且 OwnerId 偏移错),`+0x7E byte ObjectIndex`,`+0x81 byte ObjectKind`,`+0x82 byte SubKind`,`+0x86 byte Level`,`+0x87 byte ClassJob`,`+0x200 float X, +0x204 float Y, +0x208 float Z` | ## 3. 动作发包与下行效果关联(`PacketCorrelator.Actions`) - 关联依据:`UP_ActionSend`(32B)与 `UP_ActionSendPos`(24B)在 `+0x06` 写入递增非零 `ushort RequestId`;服务端返回的本地动作确认 `DOWN_Effect`(124B)在 `+0x18` 回传同一 `ushort RequestId`(其他角色/NPC 的 `DOWN_Effect` 该字段为 `0`)。 - 样本验证:`capture-018.jsonl` 中 27 条 `UP_ActionSend`(`RequestId = 179..205`)与 27 条 `DOWN_Effect`(`RequestId = 179..205`)100% 一一配对,往返耗时 `68.5 ms .. 138.3 ms`。 ## 4. `capture-018.jsonl` 回放复核结果 - `Records = 891`(上行 `534`,下行 `357`) - `Complete = 891`,`Known = 891` - `Parsed = 891`(由 `0.1.13` 的 `679` 提升至 `891`,覆盖率 100%) - `SupportedButRejected = 0` - `PingPairs = 8`,`ActionPairs = 27`