Compare commits
2 Commits
09372cb77f
...
249cd05906
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
249cd05906 | ||
|
|
2fb74a2f67 |
|
|
@ -93,9 +93,10 @@ fn build_rdm_frame(dest: &[u8; 6], tn: u8, qseq: u16, cc: u8, pid: u16, pd: &[u8
|
||||||
f[0x51] = (pid & 0xFF) as u8;
|
f[0x51] = (pid & 0xFF) as u8;
|
||||||
f[0x52] = pd.len() as u8;
|
f[0x52] = pd.len() as u8;
|
||||||
f[0x53..0x53 + pd.len()].copy_from_slice(pd);
|
f[0x53..0x53 + pd.len()].copy_from_slice(pd);
|
||||||
// checksum: 含隐式 0xCC 起始字节, 从 CC 起 msgLen 字节 (不含自身), BE
|
// checksum: 含隐式 0xCC 起始字节, Σ(0x3C..0x53+PDL) (不含 checksum 槽位自身!), BE
|
||||||
|
// ★历史 bug: 曾写成 0x3C..0x3C+msg_len 多算 1 字节(含模板残留旧 checksum) → Butler 不应答
|
||||||
let mut sum: u16 = 0xCC;
|
let mut sum: u16 = 0xCC;
|
||||||
for i in 0x3C..0x3C + msg_len { sum = sum.wrapping_add(f[i] as u16); }
|
for i in 0x3C..0x53 + pd.len() { sum = sum.wrapping_add(f[i] as u16); }
|
||||||
let cs = 0x53 + pd.len();
|
let cs = 0x53 + pd.len();
|
||||||
f[cs] = (sum >> 8) as u8;
|
f[cs] = (sum >> 8) as u8;
|
||||||
f[cs + 1] = (sum & 0xFF) as u8;
|
f[cs + 1] = (sum & 0xFF) as u8;
|
||||||
|
|
@ -1107,6 +1108,14 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) {
|
||||||
if src.ip().to_string() == target && n == 47 { reg = true; break; }
|
if src.ip().to_string() == target && n == 47 { reg = true; break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// CLAIM_BINDFF=1: 认领前先发单个 bind52(ffff), 测试能否解除 Butler 拒答态 (bind 中毒恢复实验)
|
||||||
|
if std::env::var("CLAIM_BINDFF").unwrap_or_default() == "1" {
|
||||||
|
let mut b = build_bind52([0xff, 0xff]);
|
||||||
|
b[0x1A..0x1E].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]);
|
||||||
|
sock.send_to(&b, &dst).unwrap();
|
||||||
|
std::thread::sleep(Duration::from_millis(320));
|
||||||
|
println!("CLAIM_BINDFF: 已发 bind52(ffff)");
|
||||||
|
}
|
||||||
let nonce = rng.next();
|
let nonce = rng.next();
|
||||||
let b12c = rng.next();
|
let b12c = rng.next();
|
||||||
let mut ch = load_template("claim_h112");
|
let mut ch = load_template("claim_h112");
|
||||||
|
|
@ -1201,7 +1210,9 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) {
|
||||||
if mode != "skip2" && mode != "noclaim" {
|
if mode != "skip2" && mode != "noclaim" {
|
||||||
// 冷启动广播前奏 (cold2=38s, SymphoLight 实测 2.5min 成功; 10s 短前奏被拒!)
|
// 冷启动广播前奏 (cold2=38s, SymphoLight 实测 2.5min 成功; 10s 短前奏被拒!)
|
||||||
// ★门控假说: Butler 须收到 ~13+ 轮 discovery/announce 才武装认领应答
|
// ★门控假说: Butler 须收到 ~13+ 轮 discovery/announce 才武装认领应答
|
||||||
let pre_secs: u64 = std::env::var("HOSTDEMO_PRE").ok().and_then(|s| s.parse().ok()).unwrap_or(45);
|
// ★2026-09-11 消融证伪: 广播前奏不仅非必需而且有毒 (PRE=45 认领FAIL / PRE=0 PASS)
|
||||||
|
// → 默认 PRE=0; 复现 SL 行为用 HOSTDEMO_PRE=45
|
||||||
|
let pre_secs: u64 = std::env::var("HOSTDEMO_PRE").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
|
||||||
// ★宣告帧用 announce_live 模板 + 新鲜时间戳/随机会话 ID (陈旧宣告 = 僵尸主机, 认领被无视)
|
// ★宣告帧用 announce_live 模板 + 新鲜时间戳/随机会话 ID (陈旧宣告 = 僵尸主机, 认领被无视)
|
||||||
let euce = load_template("euce"); // 540B, 无主机相关字段
|
let euce = load_template("euce"); // 540B, 无主机相关字段
|
||||||
let pre = Instant::now();
|
let pre = Instant::now();
|
||||||
|
|
@ -1236,21 +1247,21 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) {
|
||||||
println!(" [dbg] 前奏期收帧 len={} src={}", n, src);
|
println!(" [dbg] 前奏期收帧 len={} src={}", n, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut ident = IDENT102.to_vec();
|
// ★bind52×4 实测有毒 (2026-09-11 消融: skip2 认领PASS / 仅加bind×4 即 FAIL) —
|
||||||
ident[0x1C..0x20].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
// ffff→0000→ffff→0000 以 0000(unbind) 收尾, Butler 进入未绑定态后无视 AECH。
|
||||||
// ★sub=07 绑定帧 ×4: ffff→0000→ffff→0000 (间隔 ~0.32s, sym_startup @98.8-99.5 实测)
|
// 默认不再发送; 复现实验用 HOSTDEMO_BIND=1 开启。
|
||||||
// 1.8s 后发 ident
|
if std::env::var("HOSTDEMO_BIND").unwrap_or_default() == "1" {
|
||||||
for flag in [[0xff_u8, 0xff_u8], [0x00, 0x00], [0xff, 0xff], [0x00, 0x00]] {
|
for flag in [[0xff_u8, 0xff_u8], [0x00, 0x00], [0xff, 0xff], [0x00, 0x00]] {
|
||||||
let mut b = build_bind52(flag);
|
let mut b = build_bind52(flag);
|
||||||
b[0x1A..0x1E].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
b[0x1A..0x1E].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
||||||
sock.send_to(&b, &dst).unwrap();
|
sock.send_to(&b, &dst).unwrap();
|
||||||
std::thread::sleep(Duration::from_millis(320));
|
std::thread::sleep(Duration::from_millis(320));
|
||||||
|
}
|
||||||
|
std::thread::sleep(Duration::from_millis(1800));
|
||||||
}
|
}
|
||||||
std::thread::sleep(Duration::from_millis(1800));
|
// IDENT102 常量帧 0x1A 起已是本机 IP+MAC — 不要再补丁 (旧代码 patch 0x1C 会踩坏 MAC 高2字节!)
|
||||||
let mut ident = IDENT102.to_vec();
|
sock.send_to(&IDENT102, &dst).unwrap();
|
||||||
ident[0x1C..0x20].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
println!("[2] 冷启动前奏完成 ({}s 广播 + 身份帧; bind 已默认跳过=有毒)", pre_secs);
|
||||||
sock.send_to(&ident, &dst).unwrap();
|
|
||||||
println!("[2] 冷启动前奏完成 ({}s 广播 + 绑定帧×4 + 身份帧)", pre_secs);
|
|
||||||
}
|
}
|
||||||
let mut reg_ok = false;
|
let mut reg_ok = false;
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
|
|
@ -1269,26 +1280,40 @@ pub fn run(target: &str, bind_ip: &str, secs: u64) {
|
||||||
}
|
}
|
||||||
for round in 0..3 {
|
for round in 0..3 {
|
||||||
if mode == "noclaim" { break; }
|
if mode == "noclaim" { break; }
|
||||||
|
// ★每轮先发身份帧(announce144)等 47B 注册应答, 再发认领 — skip2 实测: 无身份帧直接 claim 被无视
|
||||||
|
let o2a = [o[0], o[1], o[2], o[3]];
|
||||||
|
sock.send_to(&load_template("announce144"), &dst).unwrap();
|
||||||
|
let wr = Instant::now();
|
||||||
|
let mut reg47 = false;
|
||||||
|
while wr.elapsed() < Duration::from_millis(400) {
|
||||||
|
if let Ok((n, _)) = sock.recv_from(&mut buf) {
|
||||||
|
if n == 47 { reg47 = true; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
let nonce = rng.next();
|
let nonce = rng.next();
|
||||||
let mut ch = load_template("claim_h112");
|
let ch = build_claim_frame(o2a, nonce, b12);
|
||||||
ch[0x1A..0x1E].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
// 排空缓存 (前奏自广播回收), 避免 AERE 到达前读循环耗尽
|
||||||
ch[0x88..0x8C].copy_from_slice(&nonce.to_le_bytes());
|
while sock.recv_from(&mut buf).is_ok() {}
|
||||||
ch[0x8C..0x90].copy_from_slice(&b12.to_le_bytes());
|
|
||||||
sock.send_to(&ch, &dst).unwrap();
|
sock.send_to(&ch, &dst).unwrap();
|
||||||
for _ in 0..10 {
|
let w = Instant::now();
|
||||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
let mut rx_n = 0u32;
|
||||||
|
let mut rx_lens: Vec<usize> = Vec::new();
|
||||||
|
while w.elapsed() < Duration::from_millis(700) {
|
||||||
|
if let Ok((n, _)) = sock.recv_from(&mut buf) {
|
||||||
|
rx_n += 1;
|
||||||
|
if rx_lens.len() < 8 { rx_lens.push(n); }
|
||||||
let b = &buf[..n];
|
let b = &buf[..n];
|
||||||
println!(" [dbg] AECH等待中收帧 len={} src={} magic={:?}", n, src, &b[0..4.min(n)]);
|
|
||||||
if n > 0xC4 && b.windows(4).any(|w| w == b"AERE") {
|
if n > 0xC4 && b.windows(4).any(|w| w == b"AERE") {
|
||||||
let resp = g32(b, 0xC0);
|
let resp = g32(b, 0xC0);
|
||||||
let want = agios_g(nonce);
|
let want = agios_g(nonce);
|
||||||
println!("[3] AECH 第{}轮: resp=0x{:08x} G=0x{:08x} {}", round + 1, resp, want,
|
println!("[3] AECH 第{}轮: reg47={} resp=0x{:08x} G=0x{:08x} {}", round + 1, reg47, resp, want,
|
||||||
if resp == want { "★PASS" } else { "FAIL" });
|
if resp == want { "★PASS" } else { "FAIL" });
|
||||||
if resp == want { claim_ok = true; }
|
if resp == want { claim_ok = true; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !claim_ok { println!("[3] AECH 第{}轮: reg47={} 无AERE (等700ms内收{}帧 {:?})", round + 1, reg47, rx_n, rx_lens); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !claim_ok { println!("[3] 认领失败 (真机只应答源 123.113 的 AECH), 终止"); return; }
|
if !claim_ok { println!("[3] 认领失败 (真机只应答源 123.113 的 AECH), 终止"); return; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user