enet-validator/docs/enet_protocol_current.md
OpenCode 44ff080ae6 feat: decode remaining frame fields (claim/AERE/announce/discovery-resp/EUCE/events)
- claim 552B: full layout; 240B opaque blocks proven UNCHECKED by Butler
  (CLAIM_ZERO=1 ablation: zeroed blocks -> AERE still PASS) -> claim can be
  built from scratch with zeros
- AERE 552B: Butler IP/MAC/netmask, fixture UID@0x33, slow counter@0xB1/0xB5,
  resp@0xC0; only resp+counter vary across captures
- announce 644B: session GUID, UTF-16BE show-file path, full timestamp
  @0x239 (y/mo/d/h/mi/s/ms; live sample decodes to 22:52:50.992 matching SL
  session), ephemeral port@0x25C, enable flags
- discovery-resp 217B: device name, serial blob, network block, config block
- EUCE 540B: 'EUCE' magic, version 5, username+computername+GUID, host MAC
- event_b: type=4 1084B RDM-uplink frame identified (RDM response @0x3C)
- analyze: new 'xor' subcommand with periodicity analysis
2026-09-11 14:49:39 +08:00

246 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# e:net 协议当前还原状态(截至 2026-09-11,全面逆向后)
本文档汇总基于抓包与当前程序实现的协议还原状态。
**2026-09-11 全面逆向结论:DMX 帧与 RDM 请求帧已 100% 字段破解,可零模板纯自构造。**
---
## 一、UDP/31772 通用信封(全部帧)
| 偏移 | 含义 |
|---|---|
| 0x00-0x03 | `"ENet"` magic |
| 0x04-0x07 | 帧类型 u32 LE(1=发现, 2=注册/宣告, 4=数据/RDM) |
| 0x08-0x09 | 00 00(常量) |
| 0x0A-0x0B | 目标端口 u16 LE = 31772 (0x7c1c) |
| 0x0C-0x11 | 全 0 |
| 0x12-0x13 | 01 00(常量) |
| 0x14-0x15 | 帧总长 u16 LE(如 1084=0x043c, 47=0x002f, 42=0x002a) |
| 0x16-0x17 | 00 00 |
| 0x18-0x19 | 子类型 u16 LE(01=主机→Butler 数据, 02=Butler→主机应答/注册回执) |
---
## 二、DMX 数据帧(1084B,主机→Butler,全破解)
| 偏移 | 含义 | 备注 |
|---|---|---|
| 0x00-0x19 | 通用信封 | type=4, 子类型=01 |
| 0x1A-0x1D | 帧计数器 u32 LE | **每帧 +1,任意起始值**(1031 帧实证);与 r1/r2 无关 |
| 0x1E-0x1F | 00 00 | 常量 |
| 0x20-0x21 | 01 01 | 端口使能 mask(port1+port2) |
| 0x22-0x25 | r1 u32 LE | 随机数,port1 加密种子 |
| 0x26-0x29 | r2 u32 LE | 随机数,port2 加密种子 |
| 0x2A-0x2D | 00 00 00 00 | 常量 |
| 0x2E-0x2F | 01 00 | 输出口对序号(SL 抓包=01;实测 01/02 均被接受) |
> ⚠ **两个 DMX 口数据区独立**:port1 @0x3C、port2 @0x23C。灯具接在哪个口就吃哪个口的数据。
> 实测灯具在 port2 时只发 port1 会导致灯具保持旧值/兜底白色——**show 模式已改为双口同时输出**。
| 0x30-0x31 | 00 00 | 常量 |
| 0x32-0x33 | 01 00 | 常量 |
| 0x34-0x35 | 00 02 | port1 数据长度 = 512(u16 LE) |
| 0x36-0x37 | 00 02 | port2 数据长度 = 512(u16 LE) |
| 0x38-0x3B | 01 00 00 00 | 常量 |
| 0x3C-0x23B | port1 密文 512B | `agios_dmx_encrypt(p1, r1, B12)` |
| 0x23C-0x43B | port2 密文 512B | `agios_dmx_encrypt(p2, r2, B12)` |
**加密**:`rot=(B12^r)&511; out[i]=src[(i+rot)%512]^cnt[i]`(zPprv0TXRRW 序列)
---
## 三、RDM 请求帧(1084B,主机→Butler,全破解)
| 偏移 | 含义 | 备注 |
|---|---|---|
| 0x00-0x19 | 通用信封 | type=4, 子类型=01 |
| 0x1A-0x1F | **全 0** | RDM 帧计数器恒 0(424 帧实证) |
| 0x20 | **02** | RDM 请求标志(DMX 帧为 01) |
| 0x21 | 00 | 常量 |
| 0x22-0x23 | qseq u16 LE | 查询序号,逐请求 +1 |
| 0x24-0x29 | 全 0 | DMX 帧的 r1/r2 槽位,RDM 清零 |
| 0x2A | **CC** | 常量(隐式 RDM StartCode 的外显位置) |
| 0x2B-0x2D | 00 00 00 | 常量 |
| 0x2E-0x2F | 01 00 | 常量 |
| 0x30 | 00 / 01 | 成对发送标志:主帧=00,紧跟的副本=01 |
| 0x31-0x33 | 00 00 00 | 常量 |
| 0x34-0x37 | msgLen+1 u32 LE | 如 PDL=0 时 = 25 (0x19) |
| 0x38-0x3B | 01 00 00 00 | 常量 |
| 0x3C | 01 | RDM SubStartCode |
| 0x3D | msgLen | = 24+PDL(含隐式 0xCC 起始码,不含 checksum) |
| 0x3E-0x43 | 目标 UID | 广播=ff×6 |
| 0x44-0x49 | 源 UID | 主机=45 3a 7f ff ff ff(副本末字节 -1) |
| 0x4A | TN | 事务号 |
| 0x4B | port | 主帧=00,副本=01 |
| 0x4C | msgCount | 00 |
| 0x4D-0x4E | subDevice | 00 00 |
| 0x4F | CC | 命令类(0x10=Discovery, 0x20=GET, 0x30=SET) |
| 0x50-0x51 | PID | BE(0001=UNIQUE_BRANCH, 0002=MUTE, 0003=UN_MUTE, 0060=DEVICE_INFO, 00F0=DMX_START_ADDRESS…) |
| 0x52 | PDL | PD 长度 |
| 0x53.. | PD | 参数数据 |
| 0x53+PDL | checksum u16 BE | `0xCC + Σ(0x3C .. 0x53+PDL)` |
| checksum 之后 | 任意填充 | Butler 不读(实测垃圾/零均可) |
**成对副本规则**(SL 实测):主帧之后紧跟一副本,差异仅 0x30=01、0x4B=01、srcUID 末字节 -1,**checksum 不重算**。
---
## 四、RDM 应答帧(Butler→主机,已可解码)
- 信封子类型=02,0x1A-0x1D = `"Butl"` 标记。
- 0x28-0x29 = RDM 数据区长度 u16 LE;数据区 @0x2A。
- 数据区首字节 0xAA = DISC 脉冲(0xAA/0x55 位交织编码,checksum 在 17B 内);否则为标准 RDM 应答(CC@0, CC字段@0x14, PID@0x15-0x16, PDL@0x17, PD@0x18)。
- DEVICE_INFO PD 布局(19B):protocolVer(2) model(2) category(2) swVer(4) **footprint(2)@+10** personality(2)@+12 **DMX地址(2)@+14** 保留(2) **传感器数@+18**。
---
## 五、注册应答 47B(Butler→主机,线上 89B,全破解)
| 偏移 | 含义 |
|---|---|
| 0x00-0x19 | 通用信封:type=2, 子类型=02, len@0x14=47 (0x2f) |
| 0x1A-0x1D | 设备 IP(LE 逆序字节)= 192.168.123.1 |
| 0x1E-0x23 | 设备 MAC = 00 16 1c f1 2e 39 |
| 0x24 | 00 |
| 0x25-0x28 | 主机 IP(LE 逆序)= 192.168.123.113 |
| 0x29-0x2E | 主机 MAC = 30 0e d5 59 bb 36 |
## 六、42B 事件帧(Butler→主机,结构破解)
type=4, 子类型=02, len=42 (0x2a), `"Butl"`@0x1A:
- 0x1E-0x1F = 01 00;0x20-0x21 = 事件码 u16 LE(已见 0x0318=ack_84 / 0x0062=status_84),0x22-0x23 = 00 00;0x24-0x27 = 01 00 00 00;0x28-0x29 = 00 00。
- 事件码具体语义未解码(不影响功能)。
## 六b、心跳 36B(主机→Butler,全破解)
type=2, 子类型=03, len@0x14=36 (0x24):
- 0x1A-0x1D = 主机 IP(LE 逆序)
- 0x1E-0x23 = 主机 MAC
- 周期 ~1-6s,会话保活。
## 六c、发现请求 180B(部分破解)
type=1, len=180 (0xb4):0x1A 起为主机名 ASCII(如 "tony"),其余字段未解码。
## 六d、发现应答 217B(Butler→主机,结构破解)
type=1, 子类型=0f, len=217 (0xd9):
- 0x1A-0x22 = 设备名 ASCII `"Butler S2"`
- 0x29-0x37 = 设备序列号/GUID 样字节(15B,常量)
- 0x3A-0x3C = 00 15 00(设备类型/能力?,与 AERE 同位置同值)
- 0x40-0x4E = 网络块:`01 | Butler IP(LE) | 掩码(00ffffff=255.255.255.0) | Butler MAC | 00`
- 0x59-0x5C = 1a 00 00 00 (26);0x5D-0x5F = 01 01 fa;0x60-0x63 = 0c 00 02 00
- 0xAC-0xC8 = Butler 配置块(与 AERE 共享,见七b)
- 两份抓包逐字节一致 → 除配置块慢变计数器外全静态。
## 六e、EUCE 广播 540B(主机→广播,结构破解)
magic `"EUCE"`(非 ENet 信封!),0x0C = 05 00 00 00(版本5?):
- 0x11C 起 = ASCII 串:`<用户名><计算机名>-<机器GUID>`(如 `tonycaoDESKTOP-PED2LVD-f7f4a043-17ec-4156-812b-909bdc99f2ff`)
- 0x216-0x21B = 主机 MAC
- 其余全 0。即 e:cue 主机身份广播,暖会话下非必需。
## 六f、1084B RDM 上行帧(Butler→主机,type=4,结构识别)
len=1084, 子类型=01, 0x20=02, 0x2A=CC(与 RDM 请求同布局),0x3C 起为标准 RDM 应答消息
(SubSC=01, msgLen@0x3D, dest=ff×6, src=灯具 UID…)。即 Butler 以 1084 信封转发 RDM 应答的另一形态
(与 69B 小帧 rdm_resp 并存)。
---
## 七、AECH 认领质询帧 552B(主机→Butler,功能全破解)
type=2, 子类型=05, len=552 (0x228):
| 偏移 | 含义 | 备注 |
|---|---|---|
| 0x1A-0x1D | 主机 IP(LE 逆序) | |
| 0x1E-0x23 | 主机 MAC | |
| 0x24-0x27 | 00 00 00 00 | |
| 0x28-0x2B | `"AECH"` | |
| 0x2C-0x87 | **不透明区 A(92B)** | 每轮高熵不同;XOR 无周期 → 真随机或密文 |
| 0x88-0x8B | nonce u32 LE | 主机自选,每轮不同 |
| 0x8C-0x8F | B12 u32 LE | 主机自选,会话内恒定(DMX 混淆 rot 基值) |
| 0x90-0x123 | **不透明区 B(148B)** | 同 A |
| 0x124-0x227 | 全 0 填充 | |
**★消融实证(CLAIM_ZERO=1)**:不透明区 A+B 全清零后认领照样 AERE PASS →
**Butler 只读 nonce/B12/头部,240B 不透明区不校验**(疑为 SL 软件侧证书/许可 blob,与 Butler 无关)。
→ 认领帧可零模板纯自构造(不透明区填 0 即可)。
## 七b、AERE 认领应答 552B(Butler→主机,结构破解)
type=2, 子类型=06, len=552,同会话多轮/跨时段 diff 仅 0xC0-0xC3 + 配置块计数器变化:
| 偏移 | 含义 |
|---|---|
| 0x1A-0x1D | Butler IP(LE 逆序)= 192.168.123.1 |
| 0x1E-0x23 | Butler MAC = 00 16 1c f1 2e 39 |
| 0x24-0x27 | 00 71 7b a8(主机 IP 前 3 字节回显?待跨主机验证) |
| 0x28-0x2B | `"AERE"` |
| 0x2C-0x2E | 59 bb 36 = 主机 MAC 低 3 字节(回显?) |
| 0x2F-0x32 | 7f ff ff fd/ff = 端口/状态位掩码(跨时段微变) |
| 0x33-0x38 | **灯具 UID**(首个应答者;跨时段见 010734880900 / 010734670125) |
| 0x39-0x3C | 00 15 00 0a(与发现应答同位置同值) |
| 0x40-0x4E | 网络块:01 \| Butler IP \| 掩码 \| Butler MAC \| 00 |
| 0x59-0x63 | 1a 00 00 00 01 01 fa 0c 00 02 00 |
| 0xAC-0xC8 | Butler 配置块:`00 <ctr> 01 00 00 00 / 00 <ctr> 00 00 00 <ctr> 00 00 00 1c 00 1c 00 1d 00 1d / 00 1c 00 1c 00 f0 0a f0 0a`(ctr=慢变计数器 0x23→0x24;0x1c=28/0x1d=29 疑固件版本;0x0af0=2800 ×2) |
| 0xC0-0xC3 | **resp = G(nonce)**(认领验证值) |
| 0xC4-0xC8 | 00 f0 0a f0 0a |
| 其余 | 全 0 |
## 七c、announce 644B(主机→广播,结构破解)
type=1, 子类型=15, len=644 (0x284):
- 0x3A-0x3D = 1a 00 00 00 (26)
- 0x40-0x4E = 主机网络块:01 | 主机IP(LE) | 掩码 | 主机MAC | 00
- 0x162-0x175 = 会话 GUID 样随机 20B(announce_live 为全 0)
- 0x176-0x17A = 日期头:年 u16LE(0x07ea=2026) + 月 + 日(部分)
- 0x17D-0x1F7 = **UTF-16BE 字符串:SL show 文件路径**(`C:\Users\<user>\AppData\Local\ecue\SYMPHOLIGHT 5.5\CurrentShow`,announce_live 为全 0)
- 0x211-0x215 / 0x219-0x21C / 0x221-0x225 = 3 组 5B 会话随机
- 0x239-0x241 = **完整时间戳**:年 u16LE, 月, 日, 时, 分, 秒, 毫秒 u16LE(announce_live 解码得 22:52:50.992 与 SL 会话时间吻合 ✓)
- 0x25C-0x25D = 临时端口 u16LE(每会话不同:7200 / 2200)
- 0x268 / 0x274 / 0x27C / 0x280 = 4 个使能 flag(01 00 00 00)
---
## 八、历史误区更正(2026-09-11)
1. ~~"RDM 请求消息起始位置可变 (UN_MUTE@0x3A vs BRANCH@0x3C)"~~ → **错误**。424 帧实证全部 SubSC@0x3C,结构固定。
2. ~~"Butler 帧级校验会话一致性,verbatim 才能过"~~ → **错误**。纯自构造帧全通。之前失败根因:
- `sl_get_exact.hex` 是 DMX 数据帧被误当 RDM 模板;
- 旧 `build_rdm_frame` checksum 求和范围多算 1 字节(含模板残留的旧 checksum 槽位)。
3. ~~"0x2E 是节点序号且必须为 2"~~ → 实测 SL 原帧恒为 01,设 02 也被接受,Butler 不校验此字段。
---
## 九、遗留未解码项(不阻塞功能)
| 项 | 状态 |
|---|---|
| 1947 端口 40B 许可令牌 | 固定 ASCII 常量 `BlZRQphAGxUbi7at8KEfmdZ7TdSB03b+qoC1RIA`,SL 每 ~34s 广播 21 帧双目标;语义未知(常量本身排除与狗绑定);暖会话**非必需**;冷启动必需性未验证 |
| 42B 事件码 0x0318(ack)/0x0062(status) 语义 | 结构已解,语义未解码 |
| bind52 flag ffff/0000 语义 | 结构已解(主机+设备 IP/MAC, 0x64/0x01f4 疑速率参数);flag 疑绑定/解绑;暖会话**非必需** |
| AERE 0x24-0x27 / 0x2C-0x2E 回显字段 | 疑主机 IP/MAC 部分回显,需跨主机抓包证实 |
| AERE/发现应答配置块细节(0x1c/0x1d/0x0af0) | 疑固件版本/端口配置,语义未坐实 |
| claim 不透明区 240B 真实内容 | Butler 不校验(已消融证实);疑 SL 软件侧证书/许可 blob,与协议功能无关 |
| Butler 冷启动是否必须完整前奏(45s 广播+bind+ident+EUCE+1947) | 未验证(最有价值的遗留实验:Butler 断电重启后跑 show) |
| NFM680S 通道数漂移(曾见 2ch/1ch) | 近期稳定 1ch,疑早期解析误判;未复现深究 |
| announce 时间戳/会话 ID 是否参与校验 | 按实测填充新鲜值即可;严格性未消融 |
**已证伪的旧猜测**(曾列入待办,本轮排除):
- ~~RDM 变长起始~~ → 固定 0x3C
- ~~会话级帧校验~~ → 不存在
- ~~0x2E 严格校验~~ → 不校验(1/2 均接受)
- ~~claim 帧 240B 高熵区被 Butler 校验~~ → 清零照样 PASS
---
## 十、纯自构造实现
`src/hostdemo.rs`:
- `build_dmx_frame()` — 零模板 DMX 帧
- `build_rdm_frame2()` — 零模板 RDM 请求帧
- `rdm_pair_copy()` — 成对副本
`HOSTDEMO_MODE=show` 已切换到纯自构造,**运行不再依赖 replay 文件或任何抓包模板**(除 announce144/claim 等静态注册帧,这些也可后续自构造化)。