feat(ppclock): 离线激活 keygen(ror2 公式+EFEF 反解 MAC)+ activate --auto;docs 终稿(firmware-analysis/protocol/PROGRESS),132 测试全绿

This commit is contained in:
agent committed 2026-07-24 11:53:43 +00:00
1 parent bed8973551
commit 6615696c2d
8 files changed
+465 -24

No files matched your search

+9 -1
View File
@@ -151,6 +151,8 @@ def build_parser() -> argparse.ArgumentParser:
sp = sub.add_parser("activate", help="激活")
sp.add_argument("code", nargs="?", help="激活码 hex")
sp.add_argument("--show-id", action="store_true", help="仅读取设备 ID")
sp.add_argument("--auto", action="store_true",
help="离线自动激活:EFEF 反解 MAC + 本地 keygen(无需服务器)")
sp = sub.add_parser("wifi", help="WiFi 配置")
sp.add_argument("ssid")
@@ -225,8 +227,14 @@ async def _run_command(args, t):
if c == "activate":
if args.show_id:
return {"device_id": await C.get_device_id(t)}
if args.auto:
mac = None
if args.mac:
# 显示序 MAC → NVDS 字节序(LSB 在前);不确定时优先省略 --mac 走 EFEF 反解
mac = bytes.fromhex(args.mac.replace(":", ""))[::-1]
return await C.activate_auto(t, mac)
if not args.code:
raise ValueError("缺少激活码或 --show-id")
raise ValueError("缺少激活码、--auto 或 --show-id")
await C.activate(t, args.code)
return None
if c == "wifi":
+12
View File
@@ -102,6 +102,18 @@ async def activate(t, code_hex: str):
await t.write_rxtx(P.simple_command("commit"))
async def activate_auto(t, mac: bytes | None = None) -> dict:
"""离线自动激活(firmware-analysis §4.3):MAC→本地 keygen→EF 下发。
mac=None 时经 EFEF 设备 ID 反解(字节序无歧义);返回 {device_id, mac, code}。"""
device_id = None
if mac is None:
device_id = await t.request_device_id()
mac = P.recover_mac_from_device_id(device_id)
code = P.keygen_code(mac)
await activate(t, code.hex())
return {"device_id": device_id, "mac": mac.hex(), "code": code.hex()}
async def get_device_id(t) -> str:
return await t.request_device_id()
+38
View File
@@ -162,3 +162,41 @@ def parse_device_id(data: bytes) -> str:
if all(0x20 <= b < 0x7F for b in data):
return data.decode("ascii")
return data.hex().upper()
def _ror2(v: int) -> int:
"""8 位循环右移 2(证据 0x07FC24C8:(t<<6)+(t>>2) & 0xFF = ror2,非左移)。"""
return ((v << 6) + (v >> 2)) & 0xFF
def keygen_code(mac: bytes) -> bytes:
"""本地激活码:code[i] = ror2(((MAC[i]<<1)&0xFF)^0xEF) % 101,i=0..5。
证据:docs/firmware-analysis.md §4.2(keygen @0x07FC24C8,MAC=BD 地址,NVDS 顺序)。
EF 成败不门禁任何固件命令——激活仅影响屏显激活页。"""
if len(mac) != 6:
raise ValueError("MAC 必须 6 字节")
return bytes(_ror2(((b << 1) & 0xFF) ^ 0xEF) % 101 for b in mac)
def recover_mac_from_device_id(device_id: str) -> bytes:
"""14hex 设备 ID → 6B MAC。
ID = %02X(|MAC4-0x99|,|MAC5-0x99|,|MAC3-0x99|,|MAC2-0x99|,|MAC1-0x99|,|MAC0-0x99|,flags),
flags bit i = MAC[i] ≥ 0x9A(证据 §4.2 生成函数 0x07FCA22C)。"""
if len(device_id) != 14:
raise ValueError("设备 ID 必须 14 个 hex 字符")
try:
vals = [int(device_id[i:i + 2], 16) for i in range(0, 14, 2)]
except ValueError:
raise ValueError("设备 ID 含非 hex 字符") from None
*v, flags = vals
# v 顺序:MAC4,MAC5,MAC3,MAC2,MAC1,MAC0 → 反映射
order = [4, 5, 3, 2, 1, 0]
mac = [0] * 6
for vi, idx in zip(v, order):
if flags & (1 << idx):
mac[idx] = 0x99 + vi
else:
mac[idx] = 0x99 - vi
if not 0 <= mac[idx] <= 0xFF:
raise ValueError("设备 ID 无法反解(掩码与差值矛盾)")
return bytes(mac)