feat(huya): 证书算法完全闭环 - 差分法证明标准AES-128-ECB并复现铸证
- NativeFunction直调差分: FIPS-197 KAT逐位一致, UdbAESUtil无魔改 - 证书 = b64([0x0C][key_idx][AES128-ECB(key16, zeropad(P1_187B))]) - P1结构: [010400][5008][u16+20B随机][u16+40B指纹hex][u16+114B hyCred] - 密钥: rodata默认三条(AESkeyMgr id=3/4/5) + 服务端下发会话钥(key_idx区分) - tools/cert_forge.py 重铸证书与活体密文 FULL MATCH
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""UdbAESUtil 参考实现与设备实测对拍。
|
||||
|
||||
2026-08-25 Frida 直调差分结论(scripts/hook_aes_diff.py + evidence/aes_diff.json):
|
||||
- UdbAESUtil::Cipher == 标准 AES-128 加密 (FIPS-197 KAT 逐位一致)
|
||||
- UdbAESUtil::KeyExpansion == 标准展开,轮密钥按「列序/转置」存储
|
||||
- C1(uchar* key) 只读 key 前 16 字节
|
||||
即: 不存在自定义 AES;任何"解密对不上"都是密钥来源/调用方的问题。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
MASTER_KEY_STR = b"HuyaUdb1928374650qwertyuiop"
|
||||
|
||||
_SBOX = bytes.fromhex(
|
||||
'637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0'
|
||||
'b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b275'
|
||||
'09832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cf'
|
||||
'd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2'
|
||||
'cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdb'
|
||||
'e0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08'
|
||||
'ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9e'
|
||||
'e1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16')
|
||||
_INV = bytes.fromhex(
|
||||
'52096ad53036a538bf40a39e81f3d7fb733317225e060830ca22c1e0941053ff'
|
||||
'5013071554c6935c9b6b478e49af25ebd6d13ca8d1b972e183a15b8e35997231'
|
||||
'd18def6e281286be5532e377b905b09393c428c0aab8858fddec191bdbaff54c'
|
||||
'bb617a475e8db4f89d389128723abbd6142cbbb8da24014361a9cf83d0d90c0b'
|
||||
'93ae82fdf3a50165c19fd02fa1ca67094c40b5f9bbc9ecd454f75bc46caa5e58'
|
||||
'f4fc6d7893fa59957c04c0a3a8ef7a3493163b536fceaac2d940e70fa65048e9'
|
||||
'4bccd5d83760a68309a2ded466b75c8976ee06753c70336a124b2042b8db9a8f'
|
||||
'58eec4f73d116f33766f2cc10ed1233b84f21f2679120569ad47eb4e30665ff1')
|
||||
|
||||
|
||||
def _xtime(a: int) -> int:
|
||||
a <<= 1
|
||||
return (a ^ 0x1B) & 0xFF if a & 0x100 else a
|
||||
|
||||
|
||||
def expand_schedule_transposed(key16: bytes) -> bytes:
|
||||
"""标准 AES-128 轮密钥,按 UdbAESUtil 的存储序输出(176B)。
|
||||
|
||||
设备布局: rk[round] 为 16B,按 state 列序存放(rk0..3 = key 的第0列)。
|
||||
"""
|
||||
assert len(key16) == 16
|
||||
words = [list(key16[4 * i:4 * i + 4]) for i in range(4)]
|
||||
rcon = 1
|
||||
for i in range(4, 44):
|
||||
t = list(words[i - 1])
|
||||
if i % 4 == 0:
|
||||
t = t[1:] + t[:1]
|
||||
t = [_SBOX[b] for b in t]
|
||||
t[0] ^= rcon
|
||||
rcon = _xtime(rcon)
|
||||
words.append([words[i - 4][j] ^ t[j] for j in range(4)])
|
||||
# 设备实测布局: 每轮16B按字表转置存放 dev[r*4+c] = words[rnd*4+c][r]
|
||||
out = bytearray()
|
||||
for rnd in range(11):
|
||||
blk = bytearray(16)
|
||||
for c in range(4):
|
||||
for r in range(4):
|
||||
blk[r * 4 + c] = words[rnd * 4 + c][r]
|
||||
out += blk
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def encrypt_block(key16: bytes, block16: bytes) -> bytes:
|
||||
return AES.new(key16, AES.MODE_ECB).encrypt(block16)
|
||||
|
||||
|
||||
def decrypt_block(key16: bytes, block16: bytes) -> bytes:
|
||||
return AES.new(key16, AES.MODE_ECB).decrypt(block16)
|
||||
|
||||
|
||||
def zero_pad(data: bytes) -> bytes:
|
||||
"""UdbAESUtil::encrypt 的填充:补 '\\0' 到 16 的倍数(不追加整块)。"""
|
||||
if len(data) % 16 == 0:
|
||||
return data
|
||||
return data + b"\x00" * (16 - len(data) % 16)
|
||||
|
||||
|
||||
def udb_encrypt(key16: bytes, plain: bytes) -> bytes:
|
||||
p = zero_pad(plain)
|
||||
return AES.new(key16, AES.MODE_ECB).encrypt(p)
|
||||
|
||||
|
||||
def udb_decrypt(key16: bytes, ct: bytes) -> bytes:
|
||||
return AES.new(key16, AES.MODE_ECB).decrypt(ct).rstrip(b"\x00")
|
||||
|
||||
|
||||
def selftest() -> None:
|
||||
# FIPS-197
|
||||
kat_k = bytes(range(16))
|
||||
kat_p = bytes.fromhex("00112233445566778899aabbccddeeff")
|
||||
assert encrypt_block(kat_k, kat_p).hex() == "69c4e0d86a7b0430d8cdb78070b4c55a"
|
||||
# 与设备 dump 的轮密钥对拍
|
||||
import json
|
||||
from pathlib import Path
|
||||
j = Path(__file__).resolve().parent.parent / "evidence" / "aes_diff.json"
|
||||
if j.exists():
|
||||
dev = json.loads(j.read_text())["sched_kat"]["rk"]
|
||||
assert expand_schedule_transposed(kat_k).hex() == dev, "schedule mismatch!"
|
||||
dev_ct = json.loads(j.read_text())["cipher_huyaudb_first16"]["after"]
|
||||
# harness 该样本实际喂入的 key = "4875796155646231"(hex串被二次编码, 见脚本注释)
|
||||
ref = encrypt_block(b"4875796155646231", b"\x00" * 16).hex()
|
||||
assert dev_ct == ref, f"huyaudb mismatch {dev_ct} vs {ref}"
|
||||
# 主密钥前16字节的标准指纹(供真实样本比对)
|
||||
ref_master = encrypt_block(MASTER_KEY_STR[:16], b"\x00" * 16).hex()
|
||||
print("master first16 enc(zeros) =", ref_master)
|
||||
print("udb_aes selftest OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
selftest()
|
||||
Reference in New Issue
Block a user