""" 虎牙 App 登录 — OTP/dckey 加密算法(已通过 Frida 动态双向验证) 完整加密链(libudbauthunify.so,com.duowan.kiwi 13.4.22 / versionCode 115315): ┌─────────────────────────────────────────────────────────────┐ │ OTP 加密(hyudb_otp_encrypt) │ │ │ │ 1. XXTEA: xxtea_key = md5(uid字符串).hexdigest()[:16] │ │ xxtea_in = arg7(8字节小端) + 4字节原始长度 │ │ (标准 XXTEA, rounds = 6 + 52/n, DELTA=0x9e3779b9)│ │ │ │ 2. cred_pack 拼装: │ │ [0x02] # 固定 header │ │ [u16_le len][xxtea密文] # string 打包 │ │ [u16_le len][设备指纹] # string 打包 │ │ │ │ 3. AES-128-ECB + ZeroPadding: │ │ aes_key = md5(sessHex32 + AESKEYMGR_KEY[b])[4:12].hex() │ │ cipher = AES_ECB(拼装数据 + 补零) │ └─────────────────────────────────────────────────────────────┘ 动态验证样本(两次 hook,两个独立 PID,结果一致): uid = "1471238907296" sessHex32 = "865a4924a40897ac1fcfe6b4c2cbb0e3" (固定) AESKEYMGR = "MKDKeridjing7avnsasdSDHI" (b=2) aes_key = "312334e88d8c35cb" XXTEA 双向、AES 加密方向均已验证通过。 """ from __future__ import annotations import hashlib import struct from binascii import unhexlify from Crypto.Cipher import AES # --------------------------------------------------------------------------- # AESkeyMgr 硬编码 key 表(按 b 参数索引,1-based) # b=2..5 已通过 Frida hook AESkeyMgr::getkey(a=1, b) 动态确认; # 其余为 rodata 相邻字符串推断,待最终确认顺序。 # --------------------------------------------------------------------------- AESKEYMGR_KEYS: dict[int, str] = { 1: "owNMiaCgcHmqoTr3iRamFuHj", 2: "MKDKeridjing7avnsasdSDHI", # 已验证 3: "nskdI7MDGKSDJsnadjdoonvs", # 已验证 4: "whdFNGFdfJHMJbdfg6jhdsFG", # 已验证 5: "SHBfgytjtoikooruhogji7ER", # 已验证 6: "KNSDNjfohweeromnmkladj3g", 7: "KJNzmZaxUd9kGLoAbtefNzCv", 8: "YnzbiMPNXaGuxxuoNRd9Lzyj", } # 固定 32 位 hex 会话值(OTP AES key 源的第一半) SESS_HEX32 = "865a4924a40897ac1fcfe6b4c2cbb0e3" DELTA = 0x9E3779B9 def huya_arg7(now_ms: int | None = None) -> int: """arg7 = System.currentTimeMillis() << 16(低 16 位恒为 0)。 Frida 实测:arg7 >> 16 ≈ 事件时间戳(ms),误差为 hook 传播延迟(<600ms)。 """ import time if now_ms is None: now_ms = int(time.time() * 1000) return now_ms << 16 # --------------------------------------------------------------------------- # XXTEA(标准实现,已验证) # --------------------------------------------------------------------------- def _xxtea_mx(z, y, total, key, p, e): return (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((total ^ y) + (key[(p & 3) ^ e] ^ z))) & 0xFFFFFFFF def _xxtea_rounds(n: int) -> int: return 6 + 52 // n def xxtea_encrypt_u32(v: list[int], key: list[int]) -> list[int]: n = len(v) if n < 2: return v total = 0 z = v[n - 1] for _ in range(_xxtea_rounds(n)): total = (total + DELTA) & 0xFFFFFFFF e = (total >> 2) & 3 for p in range(n): y = v[(p + 1) % n] v[p] = (v[p] + _xxtea_mx(z, y, total, key, p, e)) & 0xFFFFFFFF z = v[p] return v def xxtea_decrypt_u32(v: list[int], key: list[int]) -> list[int]: n = len(v) if n < 2: return v rounds = _xxtea_rounds(n) total = (rounds * DELTA) & 0xFFFFFFFF y = v[0] while total != 0: e = (total >> 2) & 3 for p in range(n - 1, 0, -1): z = v[p - 1] v[p] = (v[p] - _xxtea_mx(z, y, total, key, p, e)) & 0xFFFFFFFF y = v[p] z = v[n - 1] v[0] = (v[0] - _xxtea_mx(z, y, total, key, 0, e)) & 0xFFFFFFFF y = v[0] total = (total - DELTA) & 0xFFFFFFFF return v def xxtea_key_from_uid(uid: str) -> list[int]: """XXTEA key = md5(uid字符串).hexdigest()[:16] 的 16 个 ASCII 字节(4 个小端 u32)。""" h = hashlib.md5(uid.encode("ascii")).hexdigest()[:16] return list(struct.unpack("<4I", h.encode("ascii"))) def huya_xxtea_encrypt(data: bytes, uid: str) -> bytes: """XXTEA 加密:数据 + 4字节原始长度 padding,再标准 XXTEA。""" key = xxtea_key_from_uid(uid) pad = (-len(data)) % 4 buf = data + b"\x00" * pad + struct.pack(" bytes: """AES-128 key = md5(otp_input) digest 第 5~12 字节转 hex(16 ASCII 字符)。""" d = hashlib.md5(otp_input).digest() return d[4:12].hex().encode("ascii") def huya_aes_encrypt(plain: bytes, otp_input: bytes) -> bytes: key = huya_aes_key(otp_input) pad = (-len(plain)) % 16 return AES.new(key, AES.MODE_ECB).encrypt(plain + b"\x00" * pad) def huya_aes_decrypt(cipher: bytes, otp_input: bytes) -> bytes: key = huya_aes_key(otp_input) return AES.new(key, AES.MODE_ECB).decrypt(cipher).rstrip(b"\x00") # --------------------------------------------------------------------------- # cred_pack 打包 # --------------------------------------------------------------------------- def cred_pack_string(data: bytes) -> bytes: """string = [2字节小端长度][数据](cred_pack::push_varstr)。""" return struct.pack(" str: """密码登录的 password 字段 = 标准 SHA1(明文密码) 的 hex,无盐。 Frida 抓包证据(msgType=0x1001 密码登录请求): password = "772ed992b0e161276f44ec63671e60155c506294" (40 位 hex) """ return hashlib.sha1(password.encode("utf-8")).hexdigest() def huya_name(hyname: str) -> str: """登录 name 字段 = "hy_" + 虎牙号(纯数字账号)。""" return f"hy_{hyname}" def huya_otp_encrypt( uid: str, arg7: int, device_fingerprint: bytes, b: int = 2, sess_hex32: str = SESS_HEX32, ) -> bytes: """完整 OTP 加密,返回 AES 密文。 uid : 账号 uid 的十进制字符串(XXTEA key 源) arg7 : 8 字节 ulong(时间戳/计数器) device_fingerprint : 设备指纹二进制(arg5) b : AESkeyMgr key 表索引(1..8) sess_hex32 : 固定 32 位 hex 会话值 """ # 1. XXTEA 加密 arg7 xxtea_ct = huya_xxtea_encrypt(struct.pack("