Files
live-hub-py/tools/udb_aes.py
T
yml2213 a0ba2fe020 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
2026-08-25 15:21:11 +08:00

115 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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()