feat(ppclock): 离线激活 keygen(ror2 公式+EFEF 反解 MAC)+ activate --auto;docs 终稿(firmware-analysis/protocol/PROGRESS),132 测试全绿
This commit is contained in:
1 parent
bed8973551
commit
6615696c2d
8 files changed
+465
-24
No files matched your search
+9
-1
@@ -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":
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
Reference in new issue
Block a user