enet-validator/docs/enet_protocol.md

773 lines
44 KiB
Markdown
Raw Normal View History

# e:net 协议复原文档(e:cue SymphoLight 5.5 ↔ Butler S2)
> 基于实测抓包复原(2026-09-08 初版,Wireshark/tshark 抓取于"以太网 3")。
> 抓包环境:SymphoLight 5.5 (192.168.123.113) ↔ Butler S2 固件 2.0.3322 (192.168.123.1)。
> 样本:discovery.pcap(启动/发现阶段 56 帧)、dmx40s.pcap(工程驱动阶段 ~5900 帧)。
>
> **2026-09-09 重大更新**:基于真机冷启动全程抓包 `cold2.pcap`(168.7s / 19471 帧,
> SymphoLight 冷启动 → 发现 → 注册 → AECH 认领 → RDM 全程)与虚拟 Butler 验证抓包
> `fix1.pcap`。补全设备上线协议链(§3.6/§3.7),RDM 层完全解码(§5 重写),
> 新增虚拟 Butler 实现与验证(§11)。
>
> **2026-09-10 更新**:稳态双网段对比(ss123/ss58)+ P1 修复验证(fix2/fix3)。
> DMX 帧完全解码(§4.1 重写:seq/保护字节 `(CH-1)^K`/节点序号/端口布局);
> 42B 帧重定性为事件计数遥测(§4.3,推翻"逐帧 ACK");发现收敛与稳态轮询
> 对齐(§11.3);新增 §11.5 DMX 解码里程碑、§12 缺口清单与攻关路线图。
>
> **尺寸口径约定**:本文所有帧长如无特别说明均为 **UDP 负载字节数**。
> 以太网整帧长 = UDP 负载 + 42(Eth14 + IP20 + UDP8)。历史记录中
> "89B 注册帧"=47B 负载、"594B AECH"=552B 负载、"259B 发现应答"=217B 负载、
> "101B 脉冲"=59B 负载、"84B ACK"=42B 负载(§4.3 遥测帧),均为同一帧的两种口径。
---
## 1. 总览
| 项目 | 值 |
|------|-----|
| 传输层 | UDP(无 TCP) |
| 主数据端口 | **31772** (0x7C1C),双向,主机与设备均使用该端口收发 |
| 密钥/许可端口 | **1947**,主机→广播,周期性,静态负载 |
| 帧签名 | 负载前 4 字节 = ASCII `"ENet"`(`45 4E 65 74`) |
| 字节序 | 混合:长度/计数器字段为 **小端 (LE)**,端口字段为 **大端 (BE)** |
| 加密 | **DMX 通道数据经 zPprv0TXRRW 逐帧混淆**(§4.1,已完全破解可双向复现):rot=(B12^r)&511,B12=AECH 实例随机(会话),r=Random.Next()(每帧每端口,帧内 0x22/0x26)。发现帧含部分随机材料 |
| Wireshark 识别 | 无 dissector,全部显示为 `data` |
---
## 2. e:net 通用帧头(所有 31772 报文)
UDP 负载布局(偏移为负载内字节偏移):
```
偏移 长度 示例(主机DMX帧) 含义
0x00 4 45 4E 65 74 魔数 "ENet"(另见 §3.4 的 "EUCE" 第二魔数)
0x04 4 04 00 00 00 消息类型 (LE uint32):01=发现/宣告, 02=主机标识心跳, 04=会话数据
0x08 4 00 00 1C 7C 端口 31772 (BE uint16 前置 0000)
0x0C 6 00 00 00 00 00 00 保留 (实测恒 0)
0x12 2 01 00 协议版本/标志 (实测恒 0x0001)
0x14 4 3C 04 00 00 负载总长 (LE uint32) = UDP负载字节数
0x18 2 01 00 固定字段 (实测所有 ENet 会话帧恒 0x0001)
0x1A 2 97 01 DMX 帧序号 (LE uint16,仅 DMX 数据帧;0x197→0x198→…)
0x1C ... 类型专属字段
```
已实测的长度字段对应关系:
| 帧总长(以太网) | UDP负载长 | 0x14 处 LE 值 |
|---------------|-----------|---------------|
| 222 / 259 / 540 / 686 / 1126 | 180/217/498/644/1084 | 0xB4/0xD9/0x1F2/0x284/0x43C ✓ |
| 84 / 171(Butler→主机) | 42/129 | 0x2A/0x81 ✓ |
---
## 3. 消息类型 01 —— 设备发现 / 宣告
### 3.1 主机发现请求(222 B,主机→255.255.255.255 广播)
- 负载 180 B。0x18 处 `B4 00 00 00`(=180)。
- 负载中含:主机 IP (LE `71 7B A8 C0`=192.168.123.113)、主机 MAC、
机器名+GUID ASCII(如 `tonycaoDESKTOP-PED2LVD-f7f4a043-1a`)、尾部 `09 00`。
### 3.2 Butler 发现应答(259 B,Butler→主机单播)
- 负载 217 B。含设备名 ASCII `"Butler S2"`、Butler IP (LE `7B A8 C0 00` 区域)、
Butler MAC (`00 16 1C F1 2E 39`)、固件信息区
(`23 01 00 00 00 00 24 00 00 00 24 00 00 00 1C 00 ...` 版本/参数块)、
DMX 时序参数区(`F0 0A`=BRK 100µs? `F0 0A`、MAB 等)。
### 3.3 主机宣告帧(540 B / 686 B,主机→广播,周期 ~30s)
- type=01,686 B 版负载 644 B(0x284 ✓)。含:
- 主机 IP、广播地址、主机 MAC
- 16 B 随机/密钥材料:`43 A0 F4 F7 EC 17 56 41 81 2B 90 9B DC 99 F2 FF`
- **UTF-16LE** 主机名+GUID:`tonycaoDESKTOP-PED2LVD-f7f4a043-17ec-4156-812b-909bdc99f2ff`
- `D0 07` = 2000(LE,疑似辅助端口)
- 时间戳 `EA 07 09 07 17 14 12 F4` = 2026-09-07 17:14:20.244(年/月/日/时/分/秒/毫秒)
- 尾部 `5A 00 FF FF`
### 3.4 主机标识心跳(36 B 负载 / 78 B 帧,type=02 sub=03,主机→广播,~6-30s 周期)
```
0x14 24000000 负载长 36
0x18 0300 子类型 3
0x1A 717BA8C0 主机 IP 192.168.123.113 (LE)
0x1E 300ED559BB36 主机 MAC
```
★2026-09-09 实测(cold2.pcap):认领后每 ~6s 一发,**设备从不应答**。
(旧虚拟 Butler 实现误回 42B 状态帧,已列入修正项。)
### 3.5 "EUCE" 帧(582 B,非 ENet 魔数,主机→广播,~30s 周期)
- 魔数 `45 55 43 45` = ASCII **"EUCE"**(与 "ENet" 并存的第二种 e:cue 载荷签名)。
- `EUCE 0000 0000 0000 0000 0500 0000 …`(**0x0C 处 LE `05`**=类型/版本 5),其余大量零。
- 40s 窗口观测 6 帧(帧号 668/1576/2530/3421/4309/5222,间隔 ~908 帧 ≈ 30s),
与 type=02 心跳交替出现,属主机的周期性身份/发现通告族。
### 3.6 设备注册(type=02 sub=01 → sub=02)—— 认领前的关键步骤 ★2026-09-09 补全
冷启动实测(cold2.pcap,t=38.087):
```
主机 → Butler: 102B type=02 sub=01 主机身份宣告
0x1A-0x1D 主机 IP (LE)
0x1E-0x23 主机 MAC
Butler → 主机: 47B type=02 sub=02 注册应答 (10ms 内)
0x1A-0x1D 设备 IP (LE) 例: 017ba8c0 = 192.168.123.1
0x1E-0x23 设备 MAC 例: 00161cf12e39
0x24 00
0x25-0x28 主机 IP (LE) 例: 717ba8c0 = 192.168.123.113
0x29-0x2E 主机 MAC 例: 300ed559bb36
```
47B 注册应答完整样本:
`454e6574 02000000 00001c7c 00000000 0000 0100 2f000000 0200 017ba8c0 00161cf12e39 00717ba8c0 300ed559bb36`
广播的 sub=01 宣告(255.255.255.255)同样触发设备回 sub=02 注册帧。
### 3.7 AECH 认领(type=02 sub=05 → sub=06)—— 加密会话建立 ★★2026-09-10 完全破解
注册完成后主机立即发起认领(cold2.pcap,t=38.105,**连续 3 轮**,设备 **1ms** 内应答):
```
主机 → Butler: 552B type=02 sub=05 AECH 质询 (含 "AECH"=41454348 @0x28)
0x1A-0x1D 主机 IP (LE)
0x1E-0x23 主机 MAC
0x28-0x2B "AECH"
0x2C-0x87 92B 纯随机填充 (设备完全忽略!)
0x88-0x8B ★ 4B 质询 nonce (LE) — 唯一影响应答的输入!
0x8C-0x8F ★ 4B 主机实例随机 B12 (LE, 会话内不变, 会话间不同)
— 兼任 DMX 混淆基值: rot=(B12^r)&511 (§4.1)
0x90-0x123 148B 纯随机填充 (设备完全忽略!)
Butler → 主机: 552B type=02 sub=06 AERE 应答 (含 "AERE"=41455245 @0x28)
0x1A-0x1D / 0x41-0x44 设备 IP (LE, 两处)
0x1E-0x23 / 0x49-0x4E 设备 MAC (两处)
0x25-0x27 主机 IP 前3字节 (LE)
0x2C-0x2E 主机 MAC 后3字节
0xC0-0xC3 ★ 应答 = G(nonce) — 算法见下
其余 设备身份串 + RDM 灯具摘要 + 259 式信息块 (全部静态)
```
**★应答算法(源自 SymphoLight `Ecue.Devices.dll` 反编译,类 `Ecue.Devices.ENet.Protocol.AgiosElias`)**:
```
G(x) = x ^ TABLE[(x%32 + 1 + (x*13)%30) % 32] (u32 环绕乘法)
TABLE = 32×u32 常量表, 内嵌于 DLL 静态数组 (0A8F2B10..., 128B):
1008de00 e3b77ff6 31183e1c 90fb53c8 ff10ba5c 030bc4b6 e22935c8 10e8ede1
55f409a0 3eb0b9a3 f7333308 edf07810 8abf4078 60ec0545 1f4645c8 54094148
04c0f500 fabe50bb f3f3e54c 6c2a02cb a2546e60 4f6ae10e 51dfe8d8 2eedb0ec
74006928 48fade70 e5346e9c 1c3fe0bc f6700700 6b7f1ba0 a1143cf4 3be8426c
主机验证 (AgiosElias.i4GrvIBQ6xF): AERE blob[152] (= 帧 0xC0) == G(主机 nonce)
```
**无密钥、无 dongle 参与**——纯混淆级算法。黑盒探测(aechprobe/aechprobe2 子命令)实证:
仅 0x88-0x8B 影响应答(全雪崩),确定性;离线 G 对 cold2+fix1 共 10/10 样本命中;
虚拟 Butler 已实现本地计算(`aech::agios_g`),随机 nonce 测试 5/5 PASS。
**终极实测(2026-09-10 14:29)**:SymphoLight 重启后 .7 在无桥/无中继条件下完成
3 轮 AECH 本地应答 → 89B 注册 → DMX 流恢复 → RDM 灯具重新注册,全流程独立。
**认领不再依赖真机中继**(中继/桥代码保留作对比,桥已退役)。
**实测过滤行为**:真机 .1 只应答**源 IP=192.168.123.113** 的 AECH(任意源端口均可),
其他源 IP 的质询直接丢弃(这是真机固件行为;虚拟设备无需此过滤)。
**★密钥来源与可变性盘点(2026-09-11,hostdemo 实战确认)**:
| 密钥/敏感量 | 来源 | 可变性 | 实测 |
|---|---|---|---|
| nonce(0x88-0x8B) | 主机每次质询随机生成 | ✅ 完全自由 | 每轮不同,均 PASS |
| B12(0x8C-0x8F,DMX 混淆基值) | 主机认领时随机生成,设备经 AECH 登记为会话基值 | ✅ 完全自由 | 每次认领不同,DMX 均被接受 |
| G 表(32×u32) | 内嵌 Ecue.Devices.dll 静态数组 | ❌ 全产品固定常量 | 10/10 样本命中 |
| 1947 令牌(40B ASCII `BlZRQphA…`) | SymphoLight 安装许可证绑定 | ⚠️ 安装级常量(跨天稳定,换 license 才变) | 从 SL 抓包捕获,随重放广播;**是否必需未做消融** |
| AECH 帧内 92B+148B 随机填充 | 任意(黑盒实证设备完全忽略) | ✅ 完全自由 | 模板陈旧值亦可 |
结论:**核心密钥(nonce/B12)完全自主可控、逐会话随机,无外部密钥依赖**;
外部依赖仅两项——1947 许可令牌(安装级,可从任意一次 SL 会话重抓)与
RDM 帧变长结构(当前以重放 SL 原帧绕过,见 §11.7)。
认领通过后:主机开始 DMX 会话(§4),心跳 sub=03(36B,每 ~6s,**设备不应答**)。
---
## 4. 消息类型 04 —— 会话数据(连接后的主通道)
type=04 的帧在 0x20 处有 2 字节**子类型/方向标记**:
- `01 01` = 主机→设备 **DMX 通道数据**(形态 A)
- `02 00` = 事件/状态帧(形态 B,双向:主机发事件,Butler 周期回 42B 计数遥测 §4.3)
### 4.1 形态 A:DMX 通道数据帧(1126 B,主机→Butler,~30 Hz)★2026-09-10 完全解码(含混淆算法)
```
负载偏移 长度 示例 含义
0x00 4 454E6574 "ENet"
0x04 4 04000000 type=4
0x08 4 00001C7C 端口 31772
0x0C 6 000000000000 保留
0x12 2 0100 版本/标志
0x14 4 3C040000 负载长 1084
0x18 2 0100 wnor8bDK5nr u16 = 1 (DMX 数据消息)
0x1A 4 4CB20000 ★ 帧序号 aOKr8qAKOAa LE u32,逐帧 +1(高 2B 恒 0)
0x1E 2 0000 OcpmOpr u16 = 0
0x20 2 0101 cbY0uIrZR2S3QxGbNbNC × 2 (每端口 1B, 均=1)
0x22 4 6E100C2D ★ r1 = Random.Next() LE u32 (每帧每端口随机, MSB=0)
0x26 4 80681200 ★ r2 = Random.Next() LE u32 (端口2)
0x2A 2 0000 端口状态字节 × 2 (universe 状态表查得, 通常 0)
0x2C 2 0400 端口1 ID = 4 (LE, 两设备同值)
0x2E 2 0200/0300 ★ 端口2 ID = 节点序号 LE: 真机 .1=2, 虚拟 .7=3
0x30 2 0000 端口索引0 (LE)
0x32 2 0100 端口索引1 (LE)
0x34 2 0002 端口1 通道数 = 512 (LE! 00 02, 旧文档"BE"系误读)
0x36 2 0002 端口2 通道数 = 512 (LE)
0x38 2 0100 固定 (01 00)
0x3A 2 0000 固定
0x3C 512 ★ 端口1 DMX 通道 1-512 — zPprv0TXRRW 混淆 (见下, 非明文!)
0x23C 512 ★ 端口2 DMX 通道 513-1024 — 同式混淆 (用 r2)
```
**★通道数据混淆(2026-09-10 反编译+实证完全破解;旧文档"明文/保护字节/滚动值"系误判)**:
构建链(反编译 `iey7k4rDmrsMD1TvRywm::ScheduleUpdate` → `ENet2MessageCreator::QJiBkgLcunE`
→ `vpFbA8r8ibe5ncT1Lum7::CopyToBuffer`,60B 头结构逐字段与线值吻合):
```
r = Random.Next() # 每帧每端口独立, u31 (MSB 恒 0), 写入 0x22/0x26
rot = (B12 ^ r) & 511 # B12 = AECH 质询 0x8C-0x8F 实例随机 (AgiosElias.B12rvMuR8pT, 会话不变)
out[i] = src[(i+rot) % 512] ^ cnt[i] # src = 真实通道值
cnt[i] = (512-rot-i) & 255 (i < 512-rot)
= (rot-(i-(512-rot))) & 255 (i >= 512-rot)
```
接收方解密:`src[(i+rot)%512] = wire[i] ^ cnt[i]`(rot 由帧内 r 与会话 B12 重建)。
**实证**:cold2(B12=0x1e67079f)3893 帧 + fix1(B12=0x70959ecf)1781 帧全部解密为
跨帧稳定的全零场景(测试期间主机发的是 blackout),加解密回环 5674/5674 逐字节一致
(`aech::agios_dmx_encrypt`/`agios_dmx_decrypt`,`dmxdec` 子命令)。
**"保护字节 (CH-1)^K" 系涌现巧合**:src=0 时 wire[0x3C]=(512-rot)&255,代数化简得
K = ~B12 & 0xFF(cold2: ~0x9f=0x60 ✓、fix1: ~0xcf=0x30 ✓、ss123: K=0x70 → B12l=0x8f);
0.5% 失配 = rot%256==0 的边界帧。宿主侧另有 `kWarv47o79k`(新算法,
`out[i]=src[i]^((512-i)&255)^key[(512-i)&3]`, key=r^B12,CP7rDoD7VL4() 标志选择)——
Butler S2 走旧算法。
**端口映射**:工程 universe 号(U6/U2、U1/U5)**不上线**,主机按端口序拼包,Butler 解密后按
port1/port2 各 512B 直接映射到 RS485 输出。实测帧率 29.8/s(中位间隔 33ms),稳态恒定。
### 4.2 形态 B:事件/状态帧(主机→Butler,1126 B)★2026-09-10 完全解码
> 速率随主机活动模式变化:纯 DMX 播放时 A:B ≈ 1:2(dmx40s: 1207A+2336B/40s);
> RDM 激活稳态时 B ≈ 24.7/s、A 恒定 29.8/s(ss123)。旧版"A·B·B 固定节奏"
> 仅为其中一种模式。
>
> **帧构成 = [事件头 0x00-0x3B] + [标准 E1.20 RDM 消息] + [通道图案填充至 1084B]**。
> ss123 实证:RDM 激活稳态下 2963/2963 事件帧 100% 为 RDM 查询
> (cmd24×1886 + cmd18×645 + cmd19×352 + flag=01 内嵌副本×80),无非 RDM 事件帧。
```
负载偏移 长度 示例 含义
0x00-0x17 24 … 通用头 ("ENet"/type=4/端口/负载长 1084)
0x18 4 01000000 固定
0x1C 4 00000000 固定
0x20 2 0200 子类型 = 事件
0x22 4 65890000 ★ 事件序号 u32 LE,严格 +1(2962/2962 无例外)
0x26 4 00000000 固定
0x2A 4 CC000000 常量 0xCC
0x2E 2 0100 事件类标记(DMX-A 帧此位为节点序号 2/3)
0x30 1 00/01 ★ flag:00=独立查询帧(设备应答);
01=DMX 内嵌副本(设备不应答),此时 0x32=01
0x31 3 000000 固定
0x34 4 25000000 ★ RDM 消息长度 LE u32 = E1.20 msgLen+1(含 SubSC)
0x38 4 01000000 疑似 RDM 总线/端口号(LE,本样本恒 1)
0x3C … ★ 标准 E1.20 消息(免 0xCC 起始字节):
3C:SubSC=01 3D:msgLen 3E-43:destUID 44-49:srcUID
4A:TN 4B:port 4C:msgCount 4D-4E:subDevice 4F:CC
50-51:PID(BE) 52:PDL 53+:PD 末尾:checksum(BE)
… … ★ 通道图案填充:从 0x3C+RDM长 到帧尾,约 1011-1031B
```
**填充区性质(2026-09-10 实证)**:外观为 DMX 通道图案(渐变波形),但 24B
签名在本网段与跨网段 DMX-A 流中均只命中**周期性效果的自相似片段**
(时间差中位 -50s、偏移散乱)→ 与任何实时 universe 数据无系统对应,
判定为**主机缓冲复用/填充,无协议语义**。Butler 只需解析 RDM 消息即可
(虚拟 Butler 实现即如此)。
**E1.20 字段实证对齐**(逐偏移标注):
- 0x4F = Command Class:cmd24/18 → 0x10(DISCOVERY)、cmd19 → 0x20(GET)、cmd=1a → 0x30(SET)
- cmd24 → PID=0001 UNIQUE_BRANCH(PD=分支 lo/hi 12B);cmd18 → PID=0002 UN_MUTE
(PDL=0);cmd19 → PID=0201 SENSOR_VALUE(PD=传感器序号)
- RDM 长度字段实证:cmd24→37、cmd18→25、cmd19→26(= 24 头 + PDL + 1)
- flag=01 内嵌副本:srcUID 末字节 fe(独立帧为 ff)、0x4B(port)=01
### 4.3 Butler 42B 事件计数遥测帧(Butler→主机,~每 115ms 一帧)
```
0x14 2A000000 负载长 42
0x18 0200 子类型
0x1A 4275746C 设备名前缀 "Butl"
0x1E 0100
0x20 4 xxxxxxxx ★ Butler 本地"已收事件B帧"累计计数(LE, 严格递增)
0x24 6 010000000000
```
★2026-09-10 更正(ss123 实证):0x20-0x23 **不是** "被确认事件帧的序号",
而是 Butler 自己累计收到的事件B帧数(120s 内 +2959 ≈ 实收事件 2963;
相邻帧差 +1~+10,1039/1039 严格递增)。该帧以 ~8.7/s(中位间隔 35ms,
有事件才发)周期上报,与具体查询**无逐帧配对关系**——旧结论"ACK seq =
事件 seq - 1"是"计数器≈主机事件序号"的自然巧合,叠加分析脚本
PowerShell 运算符优先级 bug(`$a.seq -eq $q.seq - 1` 解析为
`($a.seq -eq $q.seq) - 1`)导致的假相关。收到非 RDM 事件B帧**不应**即时
回 42B。
### 4.4 Butler 状态帧(171 B 等,Butler→主机,周期性)
与 42B 遥测同构:`ENet` + type=04 + `0200` + `"Butl"` + `0100` + 事件计数器(0x317…) +
`0000` + 状态块(首字节 `57`=87…,尾部补零至 129 B 负载)。
观测到的状态帧长度分布:171/177-247 B(约 60 余种长度,随状态内容伸缩)。
★2026-09-09 补注(cold2.pcap):上述"状态帧"实际多为 RDM 应答帧(69-129B 负载);
认领后的稳态会话中,真机**几乎不发送任何自发状态帧**。旧虚拟 Butler 的
"每 3.5s 突发 10 帧 84B"为错误指纹。
★2026-09-10 再补(ss123):稳态下真机**确有** 42B 帧,但性质是 §4.3 的
事件计数遥测(~8.7/s,有事件才发),既非状态帧也非逐帧 ACK。cold2 的
168s 仅 1 帧是因为冷启动窗口内事件量小。
### 4.5 流量节奏(实测 40 s 窗口)
| 流量 | 包数 | 说明 |
|------|------|------|
| 主机→Butler 1126 B | 3543 | 形态A 1207 + 形态B 2336(该窗口 A:B≈1:2;稳态 RDM 窗口 A 29.8/s 恒定、B ~24.7/s) |
| Butler→主机 84 B(负载42B) | 1250 | 事件计数遥测(§4.3,非逐帧 ACK) |
| Butler→主机 状态帧 | ~700 | 各长度状态/心跳 |
| 主机→广播 1947 | 42 | 密钥报文,~1 Hz 成对 |
| 主机→广播 686 B | 13 | type=01 宣告,~3 s 间隔 |
| 主机→广播 78 B | 7 | type=02 标识心跳,~30 s 间隔 |
| 主机→广播 582 B | 6 | "EUCE" 帧,~30 s 间隔 |
## 5. RDM over e:net ★2026-09-09 完全解码(推翻旧版"私有变体"结论)
> 触发条件:SymphoLight RDM 窗口 → Butler 端口 Enable RDM → Discover;
> 认领通过后主机持续进行后台 RDM 发现/轮询(无需人工触发)。
> 旧版"e:cue 私有变体、字段序与 E1.20 不一致"结论系解析偏移错误——
> **e:net 封装的 RDM 负载就是标准 E1.20 报文**(含标准 checksum,真机 1396/1396 验证通过)。
RDM 流量全部复用 type=04 会话帧,无独立消息类型。
主机→设备的 RDM 查询帧完整布局见 **§4.2**(事件头 + 免 0xCC 起始的 E1.20 消息
@0x3C + 通道图案填充)。
### 5.1 e:net 封装(RDM 应答,Butler → 主机)
```
0x00 4 "ENet"
0x04 4 04000000 type=4
0x08 4 00001C7C 端口
0x0C 6 00…00 保留
0x12 2 0100
0x14 4 负载总长 (LE)
0x18 2 0200 子类型
0x1A 4 "Butl" (4275746C)
0x1E 2 0000 / 0100 标志
0x20 4 序号 (LE):参数应答 = 回显查询 0x22-0x23;DISC 脉冲 = 设备本地自增
0x24 4 00000000
0x28 2 ★ RDM 数据区长度 (LE) = msgLen + 2
0x2A ... ★ RDM 数据区
- 标准 E1.20 报文: CC 01 <msgLen@0x2C> ... <checksum 2B BE>
- DISC 脉冲: AA <9-bit 编码 16B> (无 CC01, 见 §5.4)
```
### 5.2 标准 E1.20 字段布局(0x2A 起)
```
0x2A CC Start Code (SC_RDM)
0x2B 01 Sub Start Code
0x2C msgLen Message Length = CC 起到最后一个 PD 字节的字节数 (不含 checksum)
0x2D 6B Dest UID = 主机 RDM 控制器 UID (实测 45:3a:7f:ff:ff:ff)
0x33 6B Src UID = 灯具 UID (应答时)
0x39 TN Transaction Number — 应答回显查询的 TN
0x3A Response Type (00=ACK)
0x3B Message Count
0x3C 2B Sub-Device (0000)
0x3E CC Command Class (0x11=DISC_RESP, 0x21=GET_RESP, 0x31=SET_RESP)
0x3F 2B PID (BE)
0x41 PDL
0x42 PD (PDL 字节)
末尾 2B Checksum = 从 CC(0x2A) 起 msgLen 字节的 16-bit 和, 大端
```
**checksum 规则(已验证 1396/1396)**:`sum16(CC..最后一个PD字节) == checksum(BE)`。
任何模板 patch(改 UID/TN/bridge-id)后**必须重算**,否则主机按 E1.20 规范丢弃。
(注意 PowerShell 陷阱:`-shl` 保持类型宽度,`[byte] -shl 8` 恒得 0,验证脚本须先转 int。)
### 5.3 主机 → Butler 的 RDM 请求(type=04,"cmd" @0x3D 实为 E1.20 msgLen)
请求帧两种形态:独立短帧(~112B 负载)与 1084B DMX 内嵌帧(0x30 flag=01)。
**实测真机只应答 flag=00 的帧**,flag=01 为 DMX 流内嵌副本,不答。
★2026-09-10 更正(SET 抓包实证):**0x3D 不是 e:net 命令字节,是 E1.20 msgLen**。
旧称 cmd=24/18/19/1a 恰为各类请求的 msgLen:0x24=36(UNIQUE_BRANCH, PDL=12)、
0x18=24(PDL=0 的 DISC/GET)、0x19=25(GET SENSOR_VALUE, PDL=1)、
0x1A=26(SET DMX_START_ADDRESS, PDL=2)。命令语义由 CC@0x4F 决定
(0x10 DISCOVERY / 0x20 GET / 0x30 SET)。下文沿用"cmd=xx"仅作 msgLen 类别简写。
```
0x20 2 子类型 0200
0x22 2 查询序号 (LE) — 应答在 0x20-0x21 回显
0x30 1 flag: 00=独立帧(应答), 01=DMX内嵌副本(不答)
0x3C 1 SubSC = 01
0x3D 1 E1.20 msgLen(非命令字节!)
0x3E 6 目标灯具 UID (发现时为分支下界, 全范围=ffffffffffff)
0x44 6 主机 RDM 控制器 UID (bridge-id, 453a7fffffff)
0x4A 1 TN (Transaction Number)
0x4F 1 Command Class: 0x10=DISCOVERY / 0x20=GET / 0x30=SET
0x50 2 ★ PID (BE)
0x52 1 PDL;0x53+ PD;末尾 checksum (BE)
```
### 5.4 DISC 脉冲(DISC_UNIQUE_BRANCH 响应)—— 编码已破解
脉冲不是标准 RDM 包,是 E1.20 发现响应的 9-bit 线路编码(17B):
```
AA 分隔符前缀
<16B: 8×2B 编码 UID> 每字节 b → byte1=0xAA|(b&0x55), byte2=0x55|(b&0xAA)
(GF(2) 仿射求解, 13 样本全过; 解码: b=(b1&0x55)|(b2&0xAA))
<4B: 2×2B 编码 checksum> checksum=16 位编码后 UID 字节的和, 先高字节后低字节
```
- 脉冲帧 e:net 序号(0x20)= **设备本地自增序号**(非回显查询!),每次 +1..3。
- 实测脉冲时延 2-48ms(中位 29ms);应答率 ~40%(967/2419,含 mute 效果)。
### 5.5 发现/验证语义(实测裁决)
| 主机查询 | 真机应答 | 说明 |
|---|---|---|
| cmd=24 全范围 UNIQUE_BRANCH (UID=ffffffffffff) | DISC 脉冲(59B 负载) | 未静音灯具应答 |
| cmd=18 PID=**0002** (DISC_UN_MUTE) | **70B 参数 ACK**: `CC=11 PID=0002 PDL=2 PD=0000` + 有效 checksum,延迟 17-29ms | ⚠️ **不是脉冲!** 虚拟 Butler 曾错答脉冲导致主机死循环重试(947 次),灯具永不注册 |
| cmd=18 PID=**0003** 广播 MUTE (UID=ffff…) | **无应答**(0/10) | 广播静音不应答;780 条 flag=01 0003 为 DMX 内嵌广播 MUTE |
| cmd=18 PID=0060 (DEVICE_INFO) | 87B,每灯具每轮 ~10 次/3min | 注册后主机每 ~1min 重读 |
| cmd=18 PID=00f0 (DMX_START_ADDRESS) | 70B | 同上节奏 |
| cmd=18 PID=0050 (SUPPORTED_PARAMETERS) | 78/178/182B | — |
| cmd=18 PID=80xx(厂商-specific 40 余个) | 69-72B,100% 应答 | 延迟 12-35ms(中位 29ms) |
**主机 RDM 流程**:后台持续全范围发现(~14-24/s)→ 对新 UID 发 UN_MUTE(0002) 验证
→ 拿到合法 ACK 后读 DEVICE_INFO(0060)/标签/参数注册进拓扑 → 每 ~1min 轮询。
**若 0002 应答非法,主机永远停在 0002 重试,不会进入参数读取阶段。**
### 5.6 SET 写路径(★2026-09-10 set123b.pcap 实证,GUI 改地址触发)
**抓取方法**:SymphoLight RDM 窗口改真机灯具 DMX 地址(NFM680S `010734880900`
111→114→111→112),双网段同抓。
**SET 请求**(主机→Butler,1084B 事件帧,flag=00):
- CC=**0x30**@0x4F、PID=00f0@0x50 (BE)、PDL=2@0x52、PD@0x53 = **DMX 地址 BE u16**
- 0x3D msgLen=26 (=24+2),0x34 RDM 长=27 (=msgLen+1)
- 完整布局与 GET 相同(§4.2/§5.3),仅 CC/PDL/PD 不同
**SET 应答**(Butler→主机,68B 负载):
- CC=**0x31**@0x3E (SET_COMMAND_RESPONSE)、RT=**00 (ACK)**@0x3A、**PDL=0**(无参数数据)
- TN 回显(查询 0x4A → 应答 0x39)、checksum 有效、延迟 18-25ms(RS485 指纹)
**主机行为**:SET 后 **+33ms 立即 GET 同一 PID 验证**(TN 连续 +1),应答中
新地址已生效;此后稳态轮询按新地址继续。三次 SET (114/111/112) 模式完全一致。
**虚拟 Butler 实现(2026-09-10 上线,GUI 改地址实测通过)**:SET (CC=0x30) 一律回
68B ACK(`rdm_setack_68.hex` 模板,TN/UID/seq/PID 回显 + checksum 重算,延迟 15-25ms),
并按 `(UID, PID) → PD` 落存 `set_store`;后续 GET 同 PID 时以存储值覆盖模板 PD 再重算
checksum。**只 ACK 不落存会被主机的 SET→GET 验证识破**(GUI 显示改地址无效)。
该机制对所有 PID 通用(地址/标签等均可写)。
**残余**:厂商 PID 0x0200 族的 SET 写路径未触发(GUI 未暴露对应操作);
IDENTIFY_DEVICE 等其他 SET PID 未专项。
---
## 6. 端口 1947 —— 密钥/许可报文
- 主机→`192.168.123.255` 与 `255.255.255.255` 成对广播,源端口 53685/53687。
- 负载恒定 40 B,ASCII Base64:
`BlZRQphAGxUbi7at8KEfmdZ7TdSB903b+qoC1RIA`
- Base64 解码 = 30 B 高熵二进制(`06 56 51 42 98 40 1B 15 1B 8B B6 AD F0 A1 1F 99 D6 7B 4D D4 81 F7 4D DB FA AA 02 D5 12 00`)。
- 长时间观察内容**不变** → 静态许可/身份令牌,非会话密钥。
- 与 OLA 社区所述 "key message" 一致。DMX 数据并不依赖其加密(见 §4.1 明文结论)。
---
## 7. 会话建立时序(★2026-09-09 以 cold2.pcap 冷启动实测更新)
```
t=0~23s 主机 Butler S2
│ 01 发现请求 180B 广播(~每3-5s) ─▶│
│◀────── 01 发现应答 217B (设备名/MAC/IP/ConnectedTo) │
t=38.087 │ 02 宣告 sub=01 102B ─────────▶│
│◀────── 02 注册 sub=02 47B (10ms) │ ★ 认领前关键步骤
t=38.105 │ 02 AECH sub=05 552B ─────────▶│
│◀────── 02 AERE sub=06 552B (1ms) │ ×3 轮 (38.105/38.111/38.140)
t=40+ │ 04 DMX帧 A(seq) ─────────────▶│
│ 04 事件帧 B ──────────────────▶│
│◀─── 04 计数遥测 42B (~115ms) ────│ … A 29.8/s 恒定 + B 随模式 …
│ 04 RDM 查询(cmd24/18) ────────▶│ 后台持续发现/轮询
│◀─────── 04 RDM 脉冲/参数应答 ────│ 延迟 12-48ms
t=44+ │ 02 心跳 sub=03 36B (每~6s) ───▶│ ★ 设备不应答
```
Butler 若 ~数秒收不到主机数据则自动回落 standalone 模式(播 SD 卡 show)。
---
## 8. ESP32 模拟可行性结论
| 需求 | 结论 |
|------|------|
| 接收 DMX 数据 | ✅ **可行**:明文。监听 UDP 31772,过滤 `454E6574 04000000` + 0x20 处 `0101`,从负载 0x3C 取 1024 B 通道数据 |
| 维持连接 | ⚠️ 需模拟:周期回复 ACK(0x2A 负载、递增序号)+ 状态帧,防止主机判定离线;同时需应答 type=01 发现(259B 应答帧格式见 §3.2) |
| 阻止 standalone 抢发 | 停止收 DMX 后设备(真 Butler)会播 SD show;ESP32 自身无此问题 |
| 1947 端口 | 无需实现(仅主机广播,设备不监听) |
| 加密 | 无需解密——通道数据明文 |
ESP32 最小实现 = **被动接收**:绑 31772 收形态A 帧、取 0x3C 起 1024 B 即得到全通道值;
要长期稳定在线再补 ACK/状态/发现应答三类模拟帧(结构均已在本文档给出)。
---
## 9. 抓包样本索引
| 文件 | 内容 |
|------|------|
| `C:\Users\tonycao\AppData\Local\Temp\opencode\discovery.pcap` | 启动/发现阶段 56 帧 |
| `C:\Users\tonycao\AppData\Local\Temp\opencode\dmx40s.pcap` | 驱动阶段 40s,5903 帧 |
| `dmx_one_hex.txt` | 1126 B DMX 帧完整 hex ×2 |
| `rdm3.pcap` | RDM 会话(Enable RDM + Discover,DUT 停发后抓取) |
| `f686.txt` | 686 B 宣告帧完整 hex |
| `cold2.pcap` ★ | **真机 .1 冷启动全程**(168.7s/19471 帧:发现→注册→AECH→RDM),2026-09-09 |
| `fix1.pcap` ★ | **虚拟 Butler .7 验证**(58 网段,P0 修复后主机读 DEVICE_INFO 全程),2026-09-09 |
| `ss123.pcap` / `ss58.pcap` ★ | **稳态双网段对比**(120s:.1 真机 vs .7 虚拟),2026-09-10 |
| `fix2.pcap` / `fix3.pcap` ★ | **P1 修复后 .7 验证**(cmd24 收敛/稳态轮询/42B 遥测),2026-09-10 |
## 10. 待确认项(低置信度字段)
- ~~0x22 处 8 B 字段~~ → **已破解**(§4.1,2026-09-10):0x22/0x26 = 两端口首通道
保护字节 `(CH-1) XOR K`(K 会话相关,三会话验证 2000/2000);残余开放:
0x23-0x25/0x27-0x29 共 6B 为会话密钥派生滚动值(标准哈希/计数器/时间戳
全部排除),破解需先还原 AECH 密钥协商;K 与 AECH 凭证字节的对应关系未考
- ~~形态 B 事件帧内部布局~~ → **已完全解码**(§4.2,2026-09-10):事件头 +
标准 E1.20 消息 @0x3C + 通道图案填充(缓冲复用,无协议语义);
事件序号 u32@0x22 严格 +1(2962/2962,旧"偶发交换/重号"为抓包顺序现象)。
残余细节:0x38 疑似总线/端口号(样本恒 1,多端口语境未验证);flag=01
内嵌副本的生成规则(srcUID 末字节 fe、port=01)
- Butler 应答帧(217B 负载)内的版本/时序参数块逐字段含义
- "EUCE" 帧内部布局(仅确认魔数与 05 版本字节)
- ~~RDM 数据区内层字段序~~ → 已完全解码,标准 E1.20(§5.2,2026-09-09)
- cmd=19/1a(SET 类,厂商 PID 0x0200 族)的请求/应答布局(§5.6)
- RDM / show 上传 / 配置写回报文:RDM 已覆盖(§5),show 上传与配置写回未触发
---
## 11. 虚拟 Butler 实现与验证(★2026-09-09)
### 11.1 已实现能力(Rust,enet-validator)
- 发现应答(217B,Connected To 动态取请求方主机 IP/MAC,网段自适应)
- 注册应答(sub=02,47B)
- **AECH 认领本地计算(2026-09-10 起)**:虚拟设备收 AECH → 取 nonce@0x88-0x8B(LE) →
本地算 `G(nonce)`(§3.7,无密钥)写入 AERE 0xC0-0xC3 → 直接应答,**无需真机中继/桥**。
(旧中继路径保留:经源 113 socket 转发真机 .1 算 AERE;非 123 网段曾需 Windows 桥。)
- RDM 生成式应答:全新 UID 灯具的 DISC 脉冲(§5.4 编码器)+ pid_map 模板参数应答
- 高速主循环:socket 读超时 2ms(原 80/100ms 时吞吐仅 ~5/s,无法支撑发现收敛)
- DMX 接收:形态 A 帧按 §4.1 布局;0x22/0x26 = 每端口随机 r,通道数据经
zPprv0TXRRW 混淆 → **已实现实时解密**(AECH 认领时取 B12=质询 0x8C-0x8F,
`agios_dmx_decrypt` 还原真实通道值后打日志;旧日志里的 "CH1-16" 递减序列
实为 blackout 全零场景的密文形态)
### 11.2 灯具注册根因与修复(P0)
**症状**:虚拟 Butler 被认领、DMX 流入、主机能发现全新 UID,但 RDM 拓扑里
.7 的总线始终 "no devices";主机只对 .7 反复发 PID=0002(947 次)从不读 DEVICE_INFO。
**根因(cold2.pcap 实证)**:
1. 虚拟 Butler 把 PID=0002(DISC_UN_MUTE)错答成 DISC 脉冲;
真机应答为 70B 参数 ACK(§5.5)。主机拿不到合法 UN_MUTE ACK → 死循环重试。
2. 所有模板应答 patch UID 后未重算 E1.20 checksum(§5.2)→ 主机按规范丢弃。
**修复**:0002 走 pid_map 70B 模板 + TN 回显(查询 0x4A → 应答 0x39)+
全部 RDM 应答 patch 后重算 checksum。
**验证结果(fix1.pcap + GUI)**:主机对 .7 的查询变为 `0002×772 + 0060×771`
(3 个新 UID 各 257 次 DEVICE_INFO,0002:0060 = 1:1 与真机模式一致),
.7 应答 checksum 777/777 有效;SymphoLight RDM 拓扑显示
**Butler S2(.7) → Port 1 (U1) — 3 devices**,灯具注册成功。
### 11.3 稳态对齐修复(P1,2026-09-10 全部落地)
**根因(ss123/ss58 双网段对比实证)**:cmd=24 是标准 DISC_UNIQUE_BRANCH
(分支范围 lo@0x53-0x58 / hi@0x59-0x5E),真机灯具只答 UID 在范围内的查询
(二分收敛);虚拟 Butler 无视范围全答 → 主机二分永不排空 → 发现洪水
51/s(真机仅 16/s)→ 稳态轮询对 .7 永不启动。
**已修复项**:
1. cmd24 分支范围过滤,仅范围内灯具各答一个脉冲(延时入队);
2. RDM 应答延迟队列:脉冲 2-40ms / 参数 15-40ms(LCG),对齐真机 RS485
延迟指纹(中位 29ms);
3. 心跳 sub=03 改静默(真机从不应答);删除 84B 突发遥测;
4. 参数应答按镜像灯具取帧池:FIXTURE_MIRROR 130→125 / 131→80900 /
132→26c9,cmd=19/1a 再按 PD[0](传感器序号)匹配;
5. 42B 改为周期遥测(§4.3):event_ctr 累计事件B帧,~115ms 上报一次,
不再即时 ACK。
**验证(fix2/fix3.pcap)**:cmd24 率 51/s → **8.5/s**(真机 15.7/s,洪水消除);
主机对 .7 启动稳态轮询:0002/0060 每灯具每周期 + **0201 SENSOR_VALUE
23/17/4 次,与镜像灯具传感器数完全一致**;脉冲 4.9/s(真机 6.2/s);
42B 遥测 6.5/s 计数严格递增(真机 8.7/s);RDM 应答 checksum 全有效。
**残余开放项(不影响注册与轮询)**:42B 精确节流规则未明(真机差值 +1~+10
不平滑);mute 状态机语义未明(真机灯具在广播 0003 后反而恢复应答,未实现
mute 亦无碍);cmd=19/1a 中 SET 类厂商 PID(0x0200 族)写路径未专项。
### 11.4 运维要点
- 模板全部 `include_str!` 编译期内嵌,改 samples/*.hex 必须重新编译部署
- 部署:分块 base64 + Invoke-SSHCommand(Set-SFTPItem 不可用);.7 用 `~/run_butler.sh` 启动
- 进程窗口:用 86400s(24h)避免反复到期退出
- ~~桥若退出,.7 认领即卡死~~ → 2026-09-10 起本地计算 G(§3.7),桥/中继不再是必需
- .200(Windows 次 IP)的 Butler 必须与 .7 错开 MAC,避免冲突
- 离线分析:`enet-validator dmxdec <tsv>` — DMX 帧 zPprv0TXRRW 解密 + 加解密回环验证
(需 TSV 含 AECH 质询以取 B12);`dmxroll <tsv>` — 滚动值/保护字节历史分析;
tshark 导出 TSV 经 PowerShell 重定向会落为 UTF-16LE(带 BOM)
### 11.5 DMX 帧解码里程碑(2026-09-10,§4.1)★混淆算法完全破解
- 60B 帧头逐字段反编译对账(`vpFbA8r8ibe5ncT1Lum7` 结构体,Pack=2):
帧序号 u32@0x1A、cbY0u×2@0x20、r1/r2@0x22/0x26、端口ID@0x2C/0x2E、
端口索引@0x30/0x32、通道数 512 LE@0x34/0x36;
- **通道数据非明文**——zPprv0TXRRW 逐帧混淆:rot=(B12^r)&511,r=Random.Next() 每帧每端口,
B12=AECH 实例随机;cold2+fix1 共 5674 帧全部解密为稳定 blackout 场景,
加解密回环 5674/5674 逐字节一致;
- 旧结论 "(CH-1)^K 保护字节/6B 滚动值/会话密钥 K" 全部证伪——K=~B12&0xFF 为
计数器结构的涌现巧合(0.5% 失配=rot%256==0 边界);滚动值=Random.Next() 本身;
- 主机侧复现要素齐备:B12 主机自选、r 随机、加解密双向实现(`dmxdec` 验证);
- 帧率 29.8/s 恒定,A:B 比例随模式变化(推翻"A·B·B 固定节奏 ~40Hz")。
### 11.6 主机侧等效播放实测(2026-09-10,档 4 首个里程碑)★
`dmxsend` 子命令实现**完整主机侧流程**(不依赖 SymphoLight):
1. sub=01 宣告 → 收 89B 注册应答 ✓
2. AECH 认领(nonce/B12 均自选随机)→ .7 本地 G 应答 `AERE[0xC0]==G(nonce)` ✓
3. 30fps 发送 zPprv0TXRRW 加密 DMX(583 帧,高识别度图案 ch1=AA/ch2=55/ch3-10=1..128/
ch101=EF/ch513=77/ch1024=33)→ **.7 实时解密日志精确还原** `CH1-16: aa550102040810204080…` ✓
全链路(认领→注册→加密→解密)闭环自证。对真机 .1 仿冒的唯一额外约束:
AECH 质询须源自 192.168.123.113(真机固件过滤,§3.7)。
### 11.7 hostdemo:真机 .1 主机侧仿真(2026-09-10 晚)★★✅ 全链路完成(RDM 发现 + DMX 控制)
`hostdemo [目标IP] [秒]` 子命令(src/hostdemo.rs)。当晚会话历经多次假设-证伪,
**最终工作配方(HOSTDEMO_MODE=slfull,2026-09-11 00:3x 灯光实测确认)**:
```
1. 发现(type=1 → 217B)
2. 认领: announce144(102B 身份帧) 单播 → 47B 注册应答 → AECH 质询 → AERE=G(nonce) ✓
(★announce144 即 ident102 同帧; 完整冷启动前奏/绑定帧/EUCE 反而无效)
3. verbatim 重放 SL 真实会话帧流(replay_merged.txt, 32540 帧/126.7s 循环):
- DMX 数据帧: 保留 SL 原帧结构, 仅替换 r1/r2 + 密文数据段
(用本会话认领的 B12 重加密呼吸图案 → Butler 解密上灯)
- RDM 请求: SL 原帧(UN_MUTE/UNIQUE_BRANCH/GET, 含 ff/fe 成对与可变偏移结构)
4. 被动解码应答: DISC 脉冲→UID, DEVICE_INFO/START_ADDRESS→DMX 地址
```
**实测结果(灯光肉眼确认呼吸)**:
- ✅ RDM 发现 3/3: `010734670125`(NFS1K2D)@101/2ch、`010734880900`(NFM680S)@123、
`0899500026c9`(HMKJ)@1/32ch(应答 15.5k 帧)
- ✅ DMX 控制: ch101/ch123/ch1 异相呼吸(fade-in 渐入)**灯光确认**
**★会话机制深层发现(本晚核心)**:
- **AECH 握手成功 ≠ 会话数据通道打通**——认领(47B/AERE 全过)后,自构造的
type=4 帧(DMX/RDM)仍被静默丢弃;而 verbatim 重放 SL 的历史会话帧立即全通
(遥测/RDM 应答恢复)→ Butler 在帧级校验某种我们尚未破解的会话一致性
- **~~RDM 请求帧消息起始位置可变~~(09-11 更正:解析错误,实为固定)**:
深夜复核证实所有 RDM 请求帧布局恒定——SubSC/msgLen@0x3C、len16(msgLen+1)@0x34、
flag@0x30(00 独立/01 副本,配对 srcUID 末字节 ff/fe,UN_MUTE 同样成对)、
counter@0x1A、qseq@0x22、TN@0x4A。此前"0x3A vs 0x3C 变长"系手工解析错位
- **★单帧 verbatim 在流外仍被拒**(slbisect 突变实验:SL 原版 GET→NFS 帧单独
发送 → 零应答;同一帧在完整流重放中 → 正常应答)→ **门控在会话流上下文**
(疑似流级防重放指纹/状态机,具体算法未破解)——实用方案即完整流重放
- 1947 端口"密钥报文"实为 **40B ASCII 许可令牌**(`BlZRQphA…`,恒定值,双目标
21 帧 @200ms 每 ~34s 一轮);EUCE 实为全程每 6s 的许可信标
- 真机认领门控行为(当晚早段观察): 灯具 MUTE 态跨会话持久(SL 遗留的 mute
挡住后续 branch 查询);SymphoLight 关闭无释放帧,DMX 流直接消失
**★09-11 凌晨终极突破:RDM 代理开关 = 周期宣告/发现广播(消融矩阵实证)**
| 重放/发送配置 | RDM 发现 |
|---|---|
| 全量流 | ✅ |
| 去令牌 / 去心跳 / 去 EUCE / 去 DMX 数据 | ✅ 各自 3/3 |
| **去宣告+发现广播** | ❌ 0 |
| 仅 RDM / RDM+DMX | ❌ 0 |
→ **Butler 只为持续周期广播宣告(644B)/发现(188B) 的主机转发 RDM**(~2-3s 周期,
SL 天然满足)。SL 关闭后 RDM 哑、重开即通、我们的会话环无广播即哑——全部现象
由此统一解释(含"会话保持"假象)。
**★最终工作配方 v2(hostdemo hybrid 模式,100% 自构造帧)**:
```
认领(announce144 → 47B → AECH) → 循环@30fps:
- 广播: discovery + announce 每 2.5s ← RDM 代理开关
- DMX: 自构造帧, r1/r2 随机, 数据=呼吸图案(认领 B12 加密)
- RDM: 自构造 GET DEVICE_INFO / UNIQUE_BRANCH (窄分支/全范围均可)
- 心跳: 36B sub=03 每 ~1s
```
实测: 3/3 灯具 DEVICE_INFO 持续应答(101/123/1),零 SL 原帧依赖
(DMX 骨架帧的常量字段 0x2E-0x3B 暂取自 SL 模板)。
注: 灯具断开时 Butler 仍代答 discovery/GET(设备表缓存)——分支/GET 应答
不能作为灯具物理在线的证据,须以 DEVICE_LABEL/SENSOR 等实测量确认。
**遗留**:DMX 帧骨架常量字段 0x2E-0x3B 的语义(当前照抄 SL 值);流级门控
与广播门控的关系(广播已证实充分, 是否还需其它条件未知——纯自构造全帧已通,
说明广播+认领即充分)。
**历史过程记录(假设-证伪链,供复盘)**:完整冷启动前奏(45s)/绑定帧×4/EUCE 6s 节奏/
新鲜时间戳宣告/1947 密钥/会话保持 2h 模型——均单因素排除;最终由 verbatim 重放
二分(阶段 A 无认领 vs 阶段 B 先认领)锁定"认领 + 帧级会话一致性"双条件。
---
## 12. 缺口清单与攻关路线图(2026-09-10)
### 档 1 — 核心缺口(阻塞完整复现)
| 缺口 | 位置 | 影响 |
|---|---|---|
| ~~AECH 密钥协商/凭证算法~~ | §3.7 | **已破解(2026-09-10)**:反编译 SymphoLight Ecue.Devices.dll 得 AgiosElias.G —— 无密钥公开算法,本地计算应答,10/10 样本 + 在线 5/5 验证。**同日 SymphoLight 重启无中继终极实测通过**(14:29:35,3 轮本地 G 应答 + 89B 注册 + DMX 恢复 + RDM 灯具重新注册,Windows 桥已退役)|
| ~~会话 K 派生关系~~ | §4.1 | **证伪并消解(2026-09-10)**:K 不是密钥——是 zPprv0TXRRW 计数器结构的涌现巧合,K=~B12&0xFF(cold2/fix1 精确吻合)|
| ~~DMX 帧 6B 滚动值/通道加密~~(0x22-0x29 + 0x3C/0x23C 数据区) | §4.1 | **已破解(2026-09-10)**:r=Random.Next() 每帧每端口;数据 zPprv0TXRRW 混淆,rot=(B12^r)&511;5674 帧解密+回环 100% |
| ~~形态 B 事件帧内部布局~~ | §4.2 | **已完全解码(2026-09-10)**:事件头 + 标准 E1.20 @0x3C + 通道图案填充(无协议语义);残余细节见 §10 |
### 档 2 — 写路径/配置类(从未触发过捕获)
- ~~RDM SET 写路径(DMX_START_ADDRESS)~~ → **已解码(§5.6,2026-09-10)**:
CC=0x30 请求 / CC=0x31 ACK PDL=0 应答 / SET 后 +33ms GET 验证;
残余:厂商 PID 0x0200 族 SET、IDENTIFY_DEVICE 等其他 SET PID
- **show 上传 / SD 卡 / standalone 配置写回**:协议存在性已知,报文从未捕获
- **节点序号(0x2E)分配机制**:仅从 2 个样本推断(.1=2/.7=3),注册顺序还是工程配置未验证
### 档 3 — 辅助帧细节
- **EUCE 帧**(§3.5):仅确认魔数 + 版本字节,内部布局空白
- **217B 发现应答**(§3.2):版本/时序参数块逐字段含义不全
- **1947 许可报文**(§6):静态 token 已记录,Butler 是否/如何校验未知
- 行为指纹级小问题(不影响功能):42B 遥测精确节流规则(差值 +1~+10)、形态 B 序号偶发交换/重号、RDM mute 状态机语义
### 档 4 — 主机侧仿真(协议要素齐备,首个里程碑已实测 ★§11.6)
文档主要覆盖**设备侧**(Butler 视角收/答)。主机侧全部协议要素已解码且
`dmxsend` 已实测闭环(§11.6:认领→注册→加密 DMX→对端精确解密);
剩余工作为产品化(工程解析、场景调度、RDM 主轮询、ESP32 移植)。
§8 的 ESP32 章节也只写了"消费 DMX"的被动模式。
### 建议攻关顺序
1. ~~形态 B 事件帧布局~~ —— **已完成(2026-09-10,§4.2)**;
2. ~~AECH 密钥协商~~ —— **已完成(2026-09-10,§3.7:反编译 AgiosElias,无密钥 G 算法)**;
3. ~~RDM SET 写路径~~ —— **已完成(2026-09-10,§5.6)**;
4. ~~K 派生 + 6B 滚动值 + DMX 加密~~ —— **已完成(2026-09-10,§4.1)**:反编译
ScheduleUpdate 构建链,zPprv0TXRRW 混淆完全破解(rot=(B12^r)&511),
"K" 证实为 ~B12&0xFF 涌现巧合;5674 帧解密+回环 100%,加解密双向实现;
5. 顺带修正:0x3D "cmd 字节"实为 E1.20 msgLen(§5.3 更正)。
**至此档 1 全部关闭。** 剩余开放项均为非阻塞细节(档 2/3):show 上传/SD 写回
(未捕获)、节点序号分配机制、EUCE 内部布局、42B 节流细则、mute 状态机。