#!/usr/bin/env python3 """虎牙证书 nonce(rnd) 生成器 —— 纯本地可算, 无需设备! 2026-08-26 动态hook实锤(scripts/hook_nonce_chain.py, /tmp/opencode/nonce_chain.json): rnd = XXTEA(pack(serviceTime) + pack(nonce_next), key=MD5hex(uid_str + k1)[:16]) 其中: serviceTime = 当前毫秒时间戳 (int(time.time()*1000) & 0x7FFFFFFFFFFF) nonce_next = counter | (serviceTime << 16) (counter 为同 serviceTime 内递增计数, 从0开始) k1 = 设备级常量 32位hex (865a4924a40897ac1fcfe6b4c2cbb0e3, 不随账号变) key = hashlib.md5((uid_str + k1).encode()).hexdigest()[:16].encode() pack(x) = 8字节小端 (cred_packlsEm: append 8B LE) XXTEA 输出 = ceil(len/4)+1 words, 末尾 word 存原始长度 -> 16B输入 => 20B输出 = P1的rnd。 验证: 对设备实采两轮 rnd (80dd1dfa.../0ba8f8b7...) 逐字节复现一致。 k1 跨账号不变(uid=1199666914671 vs 1199666911746 均同值) => 任意账号可本地铸 nonce! """ from __future__ import annotations import hashlib import struct import time # 设备级常量: BusinessCfg::this+0x10 (动态hook自真机, 不随账号变化) # TODO: 待确认其生成源(疑为设备安装时生成并持久化), 当前直接从真机提取 K1_DEFAULT = "865a4924a40897ac1fcfe6b4c2cbb0e3" # ---------------------------------------------------------------- XXTEA def _xxtea_encrypt_words(v: list[int], k: list[int]) -> list[int]: """标准 XXTEA (delta=0x9e3779b9, rounds=6+52/n), in-place 返回。""" n = len(v) if n < 2: return v z = v[n - 1] y = v[0] s = 0 delta = 0x9E3779B9 q = 6 + 52 // n while q > 0: s = (s + delta) & 0xFFFFFFFF e = (s >> 2) & 3 p = 0 while p < n - 1: y = v[p + 1] z = (v[p] + ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((s ^ y) + (k[(p & 3) ^ e] ^ z)))) & 0xFFFFFFFF v[p] = z p += 1 y = v[0] z = (v[n - 1] + ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((s ^ y) + (k[((n - 1) & 3) ^ e] ^ z)))) & 0xFFFFFFFF v[n - 1] = z q -= 1 return v def xxtea_encrypt(data: bytes, key16: bytes) -> bytes: """虎牙专用 XXTEA: 输出 words = ceil(len/4)+1, 末尾 word 存原始长度。 data: 任意长度(实测16B); key16: 16字节。 返回: 20B (16B输入时)。 """ n = len(data) nwords = (n // 4) + 1 v = [0] * nwords for i in range(n // 4): v[i] = struct.unpack(" bytes: """cred_packlsEm: 8字节小端裸值。""" return struct.pack(" bytes: """按设备算法生成 20B nonce(rnd)。 uid: 虎牙uid(纯数字, 如1199666914671) k1: 设备常量32hex service_time_ms: 缺省=当前毫秒时间戳 counter: 同serviceTime内的递增计数(通常0即可) """ st = service_time_ms if service_time_ms is not None else int(time.time() * 1000) st &= 0x7FFFFFFFFFFF nonce_val = (counter & 0xFFFF) | (st << 16) data = pack_u64(st) + pack_u64(nonce_val) key = hashlib.md5((str(uid) + k1).encode()).hexdigest()[:16].encode() return xxtea_encrypt(data, key) # ---------------------------------------------------------------- 自检 if __name__ == "__main__": import json # 用设备实采数据验证 (成对遍历 xxtea_in/xxtea_out) ev = json.load(open("/tmp/opencode/nonce_chain.json")) ins = [e for e in ev if e.get("type") == "xxtea_in"] outs = [e for e in ev if e.get("type") == "xxtea_out"] for idx, e in enumerate(ins): data = bytes.fromhex(e["data"]["v"]) key_str = bytes.fromhex(e["key"]["v"]).decode() uid = key_str[:-32] k1 = key_str[-32:] st = struct.unpack("