From c555a2252f14e28181decb686a73304f0a5a6a34 Mon Sep 17 00:00:00 2001 From: OpenCode Date: Fri, 11 Sep 2026 15:03:09 +0800 Subject: [PATCH] feat: show mode claim frame now fully self-constructed (zero opaque blocks) build_claim_frame(): 552B AECH claim from scratch - envelope + host IP/MAC + AECH + zeroed opaque regions + nonce@0x88 + B12@0x8C. Verified on real Butler: AERE PASS, 3 fixtures online. Show mode no longer needs claim_h112 template. --- src/hostdemo.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/hostdemo.rs b/src/hostdemo.rs index 61cba8d..2b4bab8 100644 --- a/src/hostdemo.rs +++ b/src/hostdemo.rs @@ -471,16 +471,14 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) { } } } - // 认领 (announce144 → AECH) + // 认领 (announce144 → AECH; claim 帧纯自构造: 不透明区填 0, CLAIM_ZERO 消融已证实 Butler 不校验) let o2: Vec = bind_ip.split('.').map(|x| x.parse().unwrap()).collect(); sock.send_to(&load_template("announce144"), &dst).unwrap(); std::thread::sleep(Duration::from_millis(300)); let nonce = rng.next(); let bc = rng.next(); - let mut ch = load_template("claim_h112"); - ch[0x1A..0x1E].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]); - ch[0x88..0x8C].copy_from_slice(&nonce.to_le_bytes()); - ch[0x8C..0x90].copy_from_slice(&bc.to_le_bytes()); + let o2a = [o2[0], o2[1], o2[2], o2[3]]; + let ch = build_claim_frame(o2a, nonce, bc); sock.send_to(&ch, &dst).unwrap(); // ★验证 AERE 应答 (RDM 不加密, 认领失败 RDM 照样通但 DMX 解密失败 → 灯白色兜底) let mut aere_ok = false; @@ -936,7 +934,7 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) { ident[0x1C..0x20].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]); sock.send_to(&ident, &dst).unwrap(); std::thread::sleep(Duration::from_millis(300)); - // 认领 + // 认领 (死代码块, 保留模板版) let nonce = rng.next(); let bc = rng.next(); let mut ch = load_template("claim_h112"); @@ -1633,6 +1631,28 @@ fn send_key_burst(sock: &UdpSocket) { } } +/// ★AECH 认领质询帧 (552B) 从零构造 — 2026-09-11 全字段破解: +/// 不透明区 A(0x2C-0x87)/B(0x90-0x123) Butler 不校验 (CLAIM_ZERO 消融实证: 清零后 AERE 仍 PASS), 填 0 +fn build_claim_frame(host_ip: [u8; 4], nonce: u32, b12: u32) -> Vec { + let mut f = Vec::with_capacity(552); + f.extend_from_slice(b"ENet"); + f.extend_from_slice(&2u32.to_le_bytes()); // type=2 + f.extend_from_slice(&[0x00, 0x00, 0x1c, 0x7c]); // 目标端口 31772 + f.extend_from_slice(&[0u8; 4]); + f.extend_from_slice(&[0x00, 0x00, 0x01, 0x00]); + f.extend_from_slice(&552u32.to_le_bytes()); // 0x14 len + f.extend_from_slice(&[0x05, 0x00]); // 0x18 sub=05 + f.extend_from_slice(&[host_ip[3], host_ip[2], host_ip[1], host_ip[0]]); // 0x1A 主机IP 逆序 + f.extend_from_slice(&[0x30, 0x0e, 0xd5, 0x59, 0xbb, 0x36]); // 0x1E 主机MAC (本机) + f.extend_from_slice(&[0u8; 4]); // 0x24 + f.extend_from_slice(b"AECH"); // 0x28 + f.extend_from_slice(&[0u8; 0x88 - 0x2C]); // 0x2C-0x87 不透明区A = 0 + f.extend_from_slice(&nonce.to_le_bytes()); // 0x88 nonce + f.extend_from_slice(&b12.to_le_bytes()); // 0x8C B12 (DMX rot 基值) + f.extend_from_slice(&[0u8; 552 - 0x90]); // 0x90-0x227 不透明区B+零填充 + f +} + /// 统一会话环 (quick): DMX 30fps 持续 + RDM 穿插 /// 阶段: 0-2s 黑场(设备预热) → UN_MUTE + UNIQUE_BRANCH 二分发现 → MUTE 已发现灯具 /// → GET DEVICE_INFO/DMX 地址 → 呼吸图案控制 (未发现则用已知地址)