feat(huya): 里程碑 - UdbAESUtil纯Python复刻成功(A/B/D三向量逐字节) + 登录帧t1=ProtoInfo(appSign槽=HDID32)
- tools/huya_aes_replica.py 617行逐指令复刻, AES-128变异(64B钥材用[0:16]+列链式扩展+移位吸收)
- 结构定案: t1 = {0:appSign=ed0db8! 1:appVer 2:sdkVer 3:lcid 4:clientIp 5:channel 6:countryCode}
- APP_SIGN错误名=appSign字段校验失败; 下步InvCipher直解明文
This commit is contained in:
@@ -684,3 +684,30 @@ UdbAESUtilC1(…) + encrypt ← 后段
|
|||||||
### 下轮
|
### 下轮
|
||||||
1. 探针直接调 AESkeyMgr::getkey(1,…) 取派生钥 → md5_char16 合成验证
|
1. 探针直接调 AESkeyMgr::getkey(1,…) 取派生钥 → md5_char16 合成验证
|
||||||
2. 子代理 AES 复刻 (运行中)
|
2. 子代理 AES 复刻 (运行中)
|
||||||
|
|
||||||
|
## §11.21 里程碑: UdbAESUtil 纯 Python 复刻成功 + t1=ProtoInfo(appSign=HDID32) 对应 (2026-08-28 夜)
|
||||||
|
|
||||||
|
### 1) tools/huya_aes_replica.py (617 行, 逐指令复刻, 子代理产出)
|
||||||
|
- 5 段内嵌 objdump 原文逐条比对一致; **A/B/D 三向量逐字节命中**:
|
||||||
|
enc("0123456789abcdef", ZMHAV-24B) = ba3fb8f156b03a9d7db185d1254e0730 ✓
|
||||||
|
enc("0123456789abcdef", 64B-0123…) = 72727e881edcfd0100a718687909b565 ✓
|
||||||
|
enc("0123456789abcdef", owNMia-24B) = 74bd517c7e5d2bbce63c8e98a192c760 ✓
|
||||||
|
- **算法 = AES-128 变异**: 64B 钥材只用 key[0..15] (转置装载 rk[i]=key[4*(i%4)+i//4]);
|
||||||
|
扩展 10 轮×16B=176B (AES-128 形); SubWord 输入=相邻列末字节 + 链式 XOR (非标准词独立);
|
||||||
|
无独立 ShiftRows (位移吸收进寄存器置换共轭); SBOX/RCON/INV = 标准表。
|
||||||
|
|
||||||
|
### 2) 登录帧 t1 结构 = mobilequicklogin.ProtoInfo (结构级定案!)
|
||||||
|
| t1.tag | 字段 | 金样本值 |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | **appSign** | **ed0db8334cadd236c00cadf7e11ab5a5 (HDID32!!)** |
|
||||||
|
| 1 | appVer | 13.4.22 |
|
||||||
|
| 2 | sdkVer | 1.0.80138 |
|
||||||
|
| 3 | lcid | "" |
|
||||||
|
| 4 | clientIp | 127.0.0.1 |
|
||||||
|
| 5 | channel | xiaomi |
|
||||||
|
| 6 | countryCode | "" |
|
||||||
|
- **"APP_SIGN" 错误名 = 字段名 appSign 的校验失败** — 32hex-HDID32 = appSign 值 = 签名函数产物!
|
||||||
|
|
||||||
|
### 3) 下一关: appSign (ed0db8) 的生成式
|
||||||
|
- ed0db8 = encode_aes(16B-plaintext, key) 形态候选; 朴素明文组合 (aid/mid/DEVID40…异或/拼/零垫) 零命中
|
||||||
|
- 下步: (a) 补 InvCipher 解密路径 → 直解 ed0db8 见明文; (b) 探针 encode_aes(设备材料块) 差分对拍
|
||||||
|
|||||||
@@ -0,0 +1,617 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Bit-exact Python replica of Huya's proprietary AES (class UdbAESUtil) in
|
||||||
|
libudbauthunify.so, reconstructed from the ARM64 disassembly.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
* Disassembly : /tmp/udb_dis.txt (objdump -d)
|
||||||
|
* Tables : libudbauthunify_merged.so @0x1c5d8e (SBOX), @0x1c5e8e
|
||||||
|
(INV-SBOX), @0x1c5f8e (RCON[1..10]) — all standard AES.
|
||||||
|
|
||||||
|
Reverse-engineered structure (offsets in .text):
|
||||||
|
Ctor UdbAESUtil::UdbAESUtil(uchar* key) @0x24eb08
|
||||||
|
copies SBOX (rodata 0x1c5d8e, 256B) -> this+0x08
|
||||||
|
copies INV-SBOX(rodata 0x1c5e8e, 256B) -> this+0x108
|
||||||
|
tail-call KeyExpansion(key, this+0x208)
|
||||||
|
|
||||||
|
UdbAESUtil::KeyExpansion(uchar* key, uchar* rk) @0x24ebd4
|
||||||
|
* only key[0..15] is ever used (bytes 16..63 are ignored)
|
||||||
|
* prologue: rk[i] = key[4*(i%4) + i//4] (4x4 transpose)
|
||||||
|
* 10 iterations -> 16 bytes each -> 176 bytes (11 round keys):
|
||||||
|
new[4j] = SBOX[prev[4*((j+1)%4)+3]] ^ (RCON[iter] if j==0 else 0)
|
||||||
|
^ prev[4j]
|
||||||
|
new[4j+r] = new[4j+r-1] ^ prev[4j+r] (r = 1..3)
|
||||||
|
This is the classic 4-word AES-128 expansion, but the word
|
||||||
|
"RotWord+SubWord" term is applied to *columns* with the SBOX fed
|
||||||
|
by the last byte of the FOLLOWING column — a genuine mutation of
|
||||||
|
the standard key schedule.
|
||||||
|
|
||||||
|
UdbAESUtil::Cipher(uchar* state) @0x24f314
|
||||||
|
* initial AddRoundKey with rk[0..15]
|
||||||
|
* 10 rounds: SubBytes -> (MixColumns unless last round) ->
|
||||||
|
AddRoundKey(rk[1..10 loops]) ; round counter in x8 starts at 10,
|
||||||
|
decremented inside the AddRoundKey block, exit when it hits 0.
|
||||||
|
* The body is executed instruction-by-instruction below: SubBytes is
|
||||||
|
fused with a register permutation (SBOX results land in rotated
|
||||||
|
registers), MixColumns works on 32-bit sign-extended values with
|
||||||
|
csel-based xtime reduction, and a 5-register rename closes the
|
||||||
|
loop. All of that is reproduced literally.
|
||||||
|
* There is no separate ShiftRows: the row shift is absorbed into the
|
||||||
|
initial register permutation (the SBOX write-order) and the
|
||||||
|
MixColumns operand wiring.
|
||||||
|
|
||||||
|
UdbAESUtil::encrypt / _encrypt (inline copy of the same core) @0x24f9f0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tables (identical to the .so rodata; standard AES tables)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
SBOX = bytes.fromhex(
|
||||||
|
"637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0"
|
||||||
|
"b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b275"
|
||||||
|
"09832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cf"
|
||||||
|
"d0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2"
|
||||||
|
"cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdb"
|
||||||
|
"e0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08"
|
||||||
|
"ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9e"
|
||||||
|
"e1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16")
|
||||||
|
|
||||||
|
INV_SBOX = bytes.fromhex(
|
||||||
|
"52096ad53036a538bf40a39e81f3d7fb7ce339829b2fff87348e4344c4dee9cb"
|
||||||
|
"547b9432a6c2233dee4c950b42fac34e082ea16628d924b2765ba2496d8bd1257"
|
||||||
|
"2f8f66486689816d4a45ccc5d65b6926c704850fdedb9da5e154657a78d9d8490"
|
||||||
|
"d8ab008cbcd30af7e45805b8b34506d02c1e8fca3f0f02c1afbd0301138a6b3a"
|
||||||
|
"9111414f67dcea97f2cfcef0b4e67396ac7422e7ad3585e2f937e81c75df6e47"
|
||||||
|
"f11a711d29c5896fb7620eaa18be1bfc563e4bc6d279209adbc0fe78cd5af41f"
|
||||||
|
"dda8338807c731b11210592780ec5f60517fa919b54a0d2de57a9f93c99cefa0"
|
||||||
|
"e03b4dae2af5b0c8ebbb3c83539961172b047eba77d626e169146355210c7d")
|
||||||
|
|
||||||
|
RCON = bytes([0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36])
|
||||||
|
|
||||||
|
M32 = 0xFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
def sxt8(b: int) -> int:
|
||||||
|
"""Sign-extend byte b to a 32-bit register value (ldrsb semantics)."""
|
||||||
|
return b if b < 0x80 else (b - 0x100) & M32
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# KeyExpansion @0x24ebd4 (compact byte-level form of the loop, verified
|
||||||
|
# equation-by-equation against 0x24ec64..0x24ed54)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def key_expansion(key: bytes) -> bytes:
|
||||||
|
assert len(key) >= 16
|
||||||
|
rk = bytearray(16 + 10 * 16)
|
||||||
|
|
||||||
|
# prologue @0x24ebd4..0x24ec5c: rk[i] = key[4*(i % 4) + i // 4]
|
||||||
|
for i in range(16):
|
||||||
|
rk[i] = key[4 * (i % 4) + i // 4]
|
||||||
|
|
||||||
|
# loop @0x24ec64..0x24ed54 (x8 = 0..9), each iteration -> one round key
|
||||||
|
for i in range(10):
|
||||||
|
p, n = i * 16, (i + 1) * 16
|
||||||
|
rc = RCON[i]
|
||||||
|
ch = [0] * 4
|
||||||
|
ch[0] = SBOX[rk[p + 7]] ^ rc ^ rk[p + 0]
|
||||||
|
ch[1] = SBOX[rk[p + 11]] ^ rk[p + 4]
|
||||||
|
ch[2] = SBOX[rk[p + 15]] ^ rk[p + 8]
|
||||||
|
ch[3] = SBOX[rk[p + 3]] ^ rk[p + 12]
|
||||||
|
for j in range(4):
|
||||||
|
rk[n + 4 * j] = ch[j]
|
||||||
|
for r in range(1, 4):
|
||||||
|
for j in range(4):
|
||||||
|
rk[n + 4 * j + r] = rk[n + 4 * j + r - 1] ^ rk[p + 4 * j + r]
|
||||||
|
return bytes(rk)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Cipher @0x24f314 — literal instruction-level execution of the disassembly
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# (addr, mnemonic-with-operands) extracted verbatim from /tmp/udb_dis.txt
|
||||||
|
CIPHER_INIT = """
|
||||||
|
24f32c: ldrb w9, [x1]
|
||||||
|
24f330: ldrb w11, [x0, #0x208]
|
||||||
|
24f334: mov w8, #0xa
|
||||||
|
24f338: ldrb w13, [x1, #0x1]
|
||||||
|
24f33c: ldrb w14, [x0, #0x20c]
|
||||||
|
24f340: ldrb w15, [x1, #0x2]
|
||||||
|
24f344: ldrb w16, [x0, #0x210]
|
||||||
|
24f348: eor w22, w9, w11
|
||||||
|
24f34c: ldrb w9, [x1, #0x3]
|
||||||
|
24f350: ldrb w11, [x0, #0x214]
|
||||||
|
24f354: eor w20, w13, w14
|
||||||
|
24f358: ldrb w10, [x1, #0x4]
|
||||||
|
24f35c: eor w6, w15, w16
|
||||||
|
24f360: ldrb w14, [x0, #0x209]
|
||||||
|
24f364: ldrb w15, [x1, #0x5]
|
||||||
|
24f368: eor w3, w9, w11
|
||||||
|
24f36c: ldrb w9, [x0, #0x20d]
|
||||||
|
24f370: ldrb w11, [x1, #0x6]
|
||||||
|
24f374: eor w7, w10, w14
|
||||||
|
24f378: ldrb w10, [x0, #0x211]
|
||||||
|
24f37c: ldrb w14, [x1, #0x7]
|
||||||
|
24f380: eor w19, w15, w9
|
||||||
|
24f384: ldrb w9, [x0, #0x215]
|
||||||
|
24f388: ldrb w12, [x1, #0x8]
|
||||||
|
24f38c: ldrb w15, [x1, #0x9]
|
||||||
|
24f390: eor w5, w11, w10
|
||||||
|
24f394: ldrb w10, [x0, #0x20a]
|
||||||
|
24f398: eor w21, w14, w9
|
||||||
|
24f39c: ldrb w9, [x0, #0x20e]
|
||||||
|
24f3a0: ldrb w13, [x1, #0xc]
|
||||||
|
24f3a4: ldrb w11, [x1, #0xd]
|
||||||
|
24f3a8: eor w2, w12, w10
|
||||||
|
24f3ac: ldrb w14, [x1, #0xa]
|
||||||
|
24f3b0: ldrb w16, [x1, #0xe]
|
||||||
|
24f3b4: eor w17, w15, w9
|
||||||
|
24f3b8: ldrb w10, [x1, #0xb]
|
||||||
|
24f3bc: ldrb w12, [x1, #0xf]
|
||||||
|
24f3c0: ldrb w9, [x0, #0x212]
|
||||||
|
24f3c4: ldrb w15, [x0, #0x216]
|
||||||
|
24f3c8: ldrb w4, [x0, #0x20b]
|
||||||
|
24f3cc: ldrb w25, [x0, #0x20f]
|
||||||
|
24f3d0: ldrb w26, [x0, #0x213]
|
||||||
|
24f3d4: eor w24, w14, w9
|
||||||
|
24f3d8: eor w23, w10, w15
|
||||||
|
24f3dc: add x9, x0, #0x8
|
||||||
|
24f3e0: ldrb w27, [x0, #0x217]
|
||||||
|
24f3e4: eor w14, w13, w4
|
||||||
|
24f3e8: eor w4, w11, w25
|
||||||
|
24f3ec: eor w16, w16, w26
|
||||||
|
24f3f0: add x10, x0, #0x227
|
||||||
|
24f3f4: mov w11, #0x1b
|
||||||
|
24f3f8: eor w15, w12, w27
|
||||||
|
"""
|
||||||
|
|
||||||
|
ARK = """
|
||||||
|
24f400: ldurb w6, [x10, #-0xf]
|
||||||
|
24f404: ldurb w21, [x10, #-0xb]
|
||||||
|
24f408: subs x8, x8, #0x1
|
||||||
|
24f40c: ldurb w23, [x10, #-0x7]
|
||||||
|
24f410: ldurb w26, [x10, #-0xe]
|
||||||
|
24f414: ldurb w25, [x10, #-0x3]
|
||||||
|
24f418: eor w22, w22, w6
|
||||||
|
24f41c: eor w20, w20, w21
|
||||||
|
24f420: ldurb w21, [x10, #-0xa]
|
||||||
|
24f424: eor w6, w24, w23
|
||||||
|
24f428: eor w7, w7, w26
|
||||||
|
24f42c: ldurb w23, [x10, #-0x6]
|
||||||
|
24f430: ldurb w24, [x10, #-0x2]
|
||||||
|
24f434: ldurb w26, [x10, #-0x9]
|
||||||
|
24f438: eor w3, w3, w25
|
||||||
|
24f43c: eor w19, w19, w21
|
||||||
|
24f440: ldurb w25, [x10, #-0xd]
|
||||||
|
24f444: eor w5, w5, w23
|
||||||
|
24f448: eor w21, w17, w24
|
||||||
|
24f44c: ldurb w23, [x10, #-0x5]
|
||||||
|
24f450: eor w17, w4, w26
|
||||||
|
24f454: ldurb w4, [x10, #-0x1]
|
||||||
|
24f458: eor w2, w2, w25
|
||||||
|
24f45c: ldurb w25, [x10, #-0x8]
|
||||||
|
24f460: eor w24, w0, w23
|
||||||
|
24f464: ldurb w0, [x10, #-0xc]
|
||||||
|
24f468: eor w23, w16, w4
|
||||||
|
24f46c: ldurb w16, [x10, #-0x4]
|
||||||
|
24f470: eor w4, w13, w25
|
||||||
|
24f474: ldrb w26, [x10], #0x10
|
||||||
|
24f478: eor w14, w14, w0
|
||||||
|
24f47c: eor w16, w12, w16
|
||||||
|
24f480: eor w15, w15, w26
|
||||||
|
"""
|
||||||
|
|
||||||
|
SUBBYTES = """
|
||||||
|
24f488: and x12, x22, #0xff
|
||||||
|
24f48c: and x13, x7, #0xff
|
||||||
|
24f490: and x17, x17, #0xff
|
||||||
|
24f494: ldrsb w22, [x9, x12]
|
||||||
|
24f498: and x12, x20, #0xff
|
||||||
|
24f49c: ldrsb w7, [x9, x13]
|
||||||
|
24f4a0: ldrsb w13, [x9, x12]
|
||||||
|
24f4a4: and x12, x19, #0xff
|
||||||
|
24f4a8: and x0, x2, #0xff
|
||||||
|
24f4ac: ldrsb w20, [x9, x12]
|
||||||
|
24f4b0: and x12, x4, #0xff
|
||||||
|
24f4b4: ldrsb w19, [x9, x17]
|
||||||
|
24f4b8: ldrsb w4, [x9, x12]
|
||||||
|
24f4bc: and x12, x6, #0xff
|
||||||
|
24f4c0: and x17, x5, #0xff
|
||||||
|
24f4c4: ldrsb w2, [x9, x0]
|
||||||
|
24f4c8: ldrsb w0, [x9, x12]
|
||||||
|
24f4cc: ldrsb w12, [x9, x17]
|
||||||
|
24f4d0: and x17, x24, #0xff
|
||||||
|
24f4d4: and x16, x16, #0xff
|
||||||
|
24f4d8: and x14, x14, #0xff
|
||||||
|
24f4dc: ldrsb w24, [x9, x17]
|
||||||
|
24f4e0: and x17, x3, #0xff
|
||||||
|
24f4e4: ldrsb w5, [x9, x16]
|
||||||
|
24f4e8: and x16, x21, #0xff
|
||||||
|
24f4ec: and x3, x23, #0xff
|
||||||
|
24f4f0: and x6, x15, #0xff
|
||||||
|
24f4f4: ldrsb w14, [x9, x14]
|
||||||
|
24f4f8: ldrsb w17, [x9, x17]
|
||||||
|
24f4fc: ldrsb w16, [x9, x16]
|
||||||
|
24f500: ldrsb w15, [x9, x3]
|
||||||
|
24f504: ldrsb w3, [x9, x6]
|
||||||
|
"""
|
||||||
|
|
||||||
|
MIXCOL = """
|
||||||
|
24f510: lsl w6, w22, #1
|
||||||
|
24f514: lsl w21, w20, #1
|
||||||
|
24f518: cmp w22, #0x0
|
||||||
|
24f51c: lsl w25, w24, #1
|
||||||
|
24f520: eor w26, w24, w22
|
||||||
|
24f524: eor w23, w24, w20
|
||||||
|
24f528: eor w27, w6, w11
|
||||||
|
24f52c: eor w26, w26, w3
|
||||||
|
24f530: eor w23, w23, w3
|
||||||
|
24f534: csel w27, w27, w6, lt
|
||||||
|
24f538: eor w6, w21, w11
|
||||||
|
24f53c: cmp w20, #0x0
|
||||||
|
24f540: csel w6, w6, w21, lt
|
||||||
|
24f544: eor w21, w25, w11
|
||||||
|
24f548: cmp w24, #0x0
|
||||||
|
24f54c: csel w21, w21, w25, lt
|
||||||
|
24f550: eor w25, w26, w6
|
||||||
|
24f554: lsl w26, w3, #1
|
||||||
|
24f558: eor w23, w23, w27
|
||||||
|
24f55c: eor w22, w20, w22
|
||||||
|
24f560: eor w20, w25, w21
|
||||||
|
24f564: eor w6, w23, w6
|
||||||
|
24f568: eor w23, w26, w11
|
||||||
|
24f56c: cmp w3, #0x0
|
||||||
|
24f570: eor w3, w22, w3
|
||||||
|
24f574: lsl w25, w7, #1
|
||||||
|
24f578: csel w23, w23, w26, lt
|
||||||
|
24f57c: eor w3, w3, w21
|
||||||
|
24f580: eor w22, w22, w24
|
||||||
|
24f584: lsl w24, w19, #1
|
||||||
|
24f588: eor w21, w3, w23
|
||||||
|
24f58c: eor w3, w22, w27
|
||||||
|
24f590: eor w22, w25, w11
|
||||||
|
24f594: cmp w7, #0x0
|
||||||
|
24f598: eor w26, w5, w19
|
||||||
|
24f59c: lsl w27, w5, #1
|
||||||
|
24f5a0: csel w22, w22, w25, lt
|
||||||
|
24f5a4: eor w25, w24, w11
|
||||||
|
24f5a8: cmp w19, #0x0
|
||||||
|
24f5ac: eor w26, w26, w17
|
||||||
|
24f5b0: eor w3, w3, w23
|
||||||
|
24f5b4: csel w23, w25, w24, lt
|
||||||
|
24f5b8: eor w24, w26, w22
|
||||||
|
24f5bc: eor w26, w5, w7
|
||||||
|
24f5c0: lsl w29, w17, #1
|
||||||
|
24f5c4: eor w25, w27, w11
|
||||||
|
24f5c8: cmp w5, #0x0
|
||||||
|
24f5cc: eor w26, w26, w17
|
||||||
|
24f5d0: eor w28, w19, w7
|
||||||
|
24f5d4: eor w7, w24, w23
|
||||||
|
24f5d8: csel w24, w25, w27, lt
|
||||||
|
24f5dc: eor w19, w26, w23
|
||||||
|
24f5e0: eor w23, w29, w11
|
||||||
|
24f5e4: cmp w17, #0x0
|
||||||
|
24f5e8: eor w17, w28, w17
|
||||||
|
24f5ec: lsl w25, w2, #1
|
||||||
|
24f5f0: csel w23, w23, w29, lt
|
||||||
|
24f5f4: eor w17, w17, w24
|
||||||
|
24f5f8: lsl w26, w4, #1
|
||||||
|
24f5fc: eor w19, w19, w24
|
||||||
|
24f600: eor w24, w28, w5
|
||||||
|
24f604: eor w5, w17, w23
|
||||||
|
24f608: eor w17, w25, w11
|
||||||
|
24f60c: cmp w2, #0x0
|
||||||
|
24f610: eor w22, w24, w22
|
||||||
|
24f614: eor w27, w0, w4
|
||||||
|
24f618: csel w24, w17, w25, lt
|
||||||
|
24f61c: eor w25, w26, w11
|
||||||
|
24f620: cmp w4, #0x0
|
||||||
|
24f624: lsl w28, w0, #1
|
||||||
|
24f628: eor w17, w22, w23
|
||||||
|
24f62c: csel w22, w25, w26, lt
|
||||||
|
24f630: eor w23, w27, w16
|
||||||
|
24f634: eor w26, w0, w2
|
||||||
|
24f638: cmp w0, #0x0
|
||||||
|
24f63c: eor w25, w28, w11
|
||||||
|
24f640: eor w26, w26, w16
|
||||||
|
24f644: lsl w27, w16, #1
|
||||||
|
24f648: eor w23, w23, w24
|
||||||
|
24f64c: csel w25, w25, w28, lt
|
||||||
|
24f650: eor w26, w26, w22
|
||||||
|
24f654: eor w28, w4, w2
|
||||||
|
24f658: eor w2, w23, w22
|
||||||
|
24f65c: lsl w23, w14, #1
|
||||||
|
24f660: eor w4, w26, w25
|
||||||
|
24f664: eor w22, w27, w11
|
||||||
|
24f668: cmp w16, #0x0
|
||||||
|
24f66c: eor w16, w28, w16
|
||||||
|
24f670: lsl w26, w13, #1
|
||||||
|
24f674: csel w22, w22, w27, lt
|
||||||
|
24f678: eor w16, w16, w25
|
||||||
|
24f67c: eor w0, w28, w0
|
||||||
|
24f680: eor w25, w23, w11
|
||||||
|
24f684: cmp w14, #0x0
|
||||||
|
24f688: eor w27, w16, w22
|
||||||
|
24f68c: eor w16, w0, w24
|
||||||
|
24f690: csel w0, w25, w23, lt
|
||||||
|
24f694: eor w23, w26, w11
|
||||||
|
24f698: cmp w13, #0x0
|
||||||
|
24f69c: eor w24, w12, w13
|
||||||
|
24f6a0: eor w16, w16, w22
|
||||||
|
24f6a4: csel w22, w23, w26, lt
|
||||||
|
24f6a8: eor w23, w24, w15
|
||||||
|
24f6ac: lsl w24, w12, #1
|
||||||
|
24f6b0: eor w25, w12, w14
|
||||||
|
24f6b4: eor w23, w23, w0
|
||||||
|
24f6b8: eor w13, w13, w14
|
||||||
|
24f6bc: cmp w12, #0x0
|
||||||
|
24f6c0: eor w14, w23, w22
|
||||||
|
24f6c4: lsl w23, w15, #1
|
||||||
|
24f6c8: eor w26, w24, w11
|
||||||
|
24f6cc: eor w25, w25, w15
|
||||||
|
24f6d0: csel w24, w26, w24, lt
|
||||||
|
24f6d4: cmp w15, #0x0
|
||||||
|
24f6d8: eor w22, w25, w22
|
||||||
|
24f6dc: eor w25, w23, w11
|
||||||
|
24f6e0: eor w15, w13, w15
|
||||||
|
24f6e4: eor w12, w13, w12
|
||||||
|
24f6e8: csel w13, w25, w23, lt
|
||||||
|
24f6ec: eor w15, w15, w24
|
||||||
|
24f6f0: eor w12, w12, w0
|
||||||
|
24f6f4: eor w22, w22, w24
|
||||||
|
24f6f8: eor w0, w15, w13
|
||||||
|
24f6fc: eor w15, w12, w13
|
||||||
|
24f700: mov w12, w0
|
||||||
|
24f704: mov w0, w27
|
||||||
|
24f708: mov w24, w21
|
||||||
|
24f70c: mov w13, w22
|
||||||
|
24f710: mov w22, w6
|
||||||
|
"""
|
||||||
|
|
||||||
|
FINAL_STORE = """
|
||||||
|
24f718: strb w22, [x1]
|
||||||
|
24f720: strb w20, [x1, #0x1]
|
||||||
|
24f728: strb w19, [x1, #0x5]
|
||||||
|
24f730: strb w24, [x1, #0xa]
|
||||||
|
24f738: strb w21, [x1, #0x7]
|
||||||
|
24f740: strb w23, [x1, #0xb]
|
||||||
|
24f748: strb w7, [x1, #0x4]
|
||||||
|
24f74c: strb w2, [x1, #0x8]
|
||||||
|
24f750: strb w14, [x1, #0xc]
|
||||||
|
24f754: strb w17, [x1, #0x9]
|
||||||
|
24f758: strb w4, [x1, #0xd]
|
||||||
|
24f75c: strb w6, [x1, #0x2]
|
||||||
|
24f760: strb w5, [x1, #0x6]
|
||||||
|
24f764: strb w16, [x1, #0xe]
|
||||||
|
24f768: strb w3, [x1, #0x3]
|
||||||
|
24f76c: strb w15, [x1, #0xf]
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(blob):
|
||||||
|
"""Parse the embedded disassembly text into [(addr, op, operands...)]."""
|
||||||
|
ins = []
|
||||||
|
for ln in blob.strip().splitlines():
|
||||||
|
m = re.match(r'^\s*([0-9a-f]{6}):\s+([a-z0-9]+)\s*(.*)$', ln)
|
||||||
|
assert m, f"cannot parse line: {ln!r}"
|
||||||
|
rest = m.group(3).split('//')[0]
|
||||||
|
ops = re.findall(r'\[[^\]]*\]|\[[^\]]*\]?\s*,?\s*#[0-9a-f]+'
|
||||||
|
r'|\blt\b|[wx][0-9]{1,2}|#-?0x[0-9a-f]+|#[0-9a-f]+', rest)
|
||||||
|
ins.append((int(m.group(1), 16), m.group(2), ops))
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
|
def _check_sequence(blob, lo, hi, name):
|
||||||
|
"""Sanity: the embedded list must exactly match the disassembly file."""
|
||||||
|
import os
|
||||||
|
if not os.path.exists('/tmp/udb_dis.txt'):
|
||||||
|
return
|
||||||
|
want = {}
|
||||||
|
pat = re.compile(r'^\s*([0-9a-f]{6}):\s+[0-9a-f]{8}\s+(.*)$')
|
||||||
|
with open('/tmp/udb_dis.txt') as f:
|
||||||
|
for ln in f:
|
||||||
|
m = pat.match(ln)
|
||||||
|
if m:
|
||||||
|
a = int(m.group(1), 16)
|
||||||
|
if lo <= a <= hi:
|
||||||
|
want[a] = ' '.join(m.group(2).split('//')[0].split())
|
||||||
|
got = {}
|
||||||
|
for a, op, ops in _parse(blob):
|
||||||
|
got[a] = op + (' ' + ' '.join(ops) if ops else '')
|
||||||
|
want = {a: v for a, v in want.items()
|
||||||
|
if a in got} # ignore stack-save noise (ldp/stp) not embedded
|
||||||
|
ok = True
|
||||||
|
for a in sorted(set(got) | set(want)):
|
||||||
|
if got.get(a, '').replace(',', '') != want.get(a, '').replace(',', ''):
|
||||||
|
ok = False
|
||||||
|
print(f"[{name}] addr {a:#x}: embedded={got.get(a)!r} "
|
||||||
|
f"disasm={want.get(a)!r} (first diff shown)")
|
||||||
|
break
|
||||||
|
if ok:
|
||||||
|
print(f"[{name}] embedded instruction list matches the disassembly file")
|
||||||
|
|
||||||
|
|
||||||
|
class _Ctx:
|
||||||
|
"""Register file + memory view for the Cipher loop."""
|
||||||
|
|
||||||
|
def __init__(self, pt: bytes, rk: bytes):
|
||||||
|
self.R = {}
|
||||||
|
self.pt = pt
|
||||||
|
self.rk = rk # 176-byte expanded key (this+0x208)
|
||||||
|
self.rkpos = 0x1f # x10 = this+0x227 -> index 0x1f within rk
|
||||||
|
self.out = bytearray(16)
|
||||||
|
|
||||||
|
def exec_(self, ins):
|
||||||
|
for addr, op, ops in ins:
|
||||||
|
# -- register-register / immediate ALU ---------------------
|
||||||
|
if op == 'mov':
|
||||||
|
d, s = ops
|
||||||
|
if s.startswith('#'):
|
||||||
|
self.R[int(d[1:])] = int(s[1:], 16)
|
||||||
|
else:
|
||||||
|
self.R[int(d[1:])] = self.R[int(s[1:])]
|
||||||
|
elif op == 'add':
|
||||||
|
d, s, imm = ops
|
||||||
|
if s == 'x0':
|
||||||
|
# add x9, x0, #0x8 -> SBOX base (ignored)
|
||||||
|
# add x10, x0, #0x227 -> rk offset base
|
||||||
|
if int(imm[1:], 16) == 0x227:
|
||||||
|
self.rkpos = int(imm[1:], 16) - 0x208
|
||||||
|
elif op == 'and':
|
||||||
|
d, s, imm = ops # and xR, xS, #0xff
|
||||||
|
assert int(imm[1:], 16) == 0xff
|
||||||
|
self.R[int(d[1:])] = self.R[int(s[1:])] & 0xff
|
||||||
|
elif op == 'eor':
|
||||||
|
d, a, b = ops
|
||||||
|
self.R[int(d[1:])] = self.R[int(a[1:])] ^ self.R[int(b[1:])]
|
||||||
|
elif op == 'lsl':
|
||||||
|
d, s, sh = ops # lsl wR, wS, #1
|
||||||
|
assert int(sh[1:], 16) == 1
|
||||||
|
self.R[int(d[1:])] = (self.R[int(s[1:])] << 1) & M32
|
||||||
|
elif op == 'cmp':
|
||||||
|
a, b = ops
|
||||||
|
val = self.R[int(a[1:])]
|
||||||
|
self.lt = bool(val & 0x80000000) # cmp wX, #0x0
|
||||||
|
elif op == 'subs':
|
||||||
|
d, s, imm = ops
|
||||||
|
self.R[int(d[1:])] = self.R[int(s[1:])] - int(imm[1:], 16)
|
||||||
|
elif op == 'csel':
|
||||||
|
d, t, f_, cc = ops # csel wR, wT, wF, lt
|
||||||
|
self.R[int(d[1:])] = self.R[int(t[1:])] if self.lt \
|
||||||
|
else self.R[int(f_[1:])]
|
||||||
|
# -- loads --------------------------------------------------
|
||||||
|
elif op in ('ldrb', 'ldurb'):
|
||||||
|
self._load(ops)
|
||||||
|
elif op == 'ldrsb':
|
||||||
|
d = ops[0]
|
||||||
|
mem = ops[1]
|
||||||
|
m = re.match(r'\[(x\d+),\s*(x\d+)\]', mem)
|
||||||
|
base, idx = m.group(1), m.group(2)
|
||||||
|
assert base == 'x9'
|
||||||
|
self.R[int(d[1:])] = sxt8(SBOX[self.R[int(idx[1:])] & 0xff])
|
||||||
|
# -- stores (final only) ------------------------------------
|
||||||
|
elif op == 'strb':
|
||||||
|
d, mem = ops # strb wR, [x1, #imm]
|
||||||
|
self._store(d, mem)
|
||||||
|
else:
|
||||||
|
raise AssertionError(f"unhandled op {op}")
|
||||||
|
|
||||||
|
def _load(self, ops):
|
||||||
|
d, mem = ops[0], ops[1]
|
||||||
|
m = re.match(r'\[(x\d+)(?:,\s*#(-?0x[0-9a-f]+))?\]', mem)
|
||||||
|
base, imm = m.group(1), m.group(2)
|
||||||
|
off = int(imm, 16) if imm else 0
|
||||||
|
if base == 'x1':
|
||||||
|
val = self.pt[off]
|
||||||
|
elif base == 'x0':
|
||||||
|
val = self.rk[off - 0x208]
|
||||||
|
elif base == 'x10':
|
||||||
|
val = self.rk[self.rkpos + off]
|
||||||
|
if ops[2:] and ops[2].startswith('#0x10'):
|
||||||
|
# ldrb w26, [x10], #0x10 -- post-increment
|
||||||
|
self.rkpos += 0x10
|
||||||
|
else:
|
||||||
|
raise AssertionError(f"load base {base}")
|
||||||
|
self.R[int(d[1:])] = val
|
||||||
|
|
||||||
|
def _store(self, d, mem):
|
||||||
|
m = re.match(r'\[(x1)(?:,\s*#(0x[0-9a-f]+))?\]', mem)
|
||||||
|
off = int(m.group(2), 16) if m.group(2) else 0
|
||||||
|
self.out[off] = self.R[int(d[1:])] & 0xff
|
||||||
|
|
||||||
|
|
||||||
|
def cipher(pt: bytes, rk: bytes) -> bytes:
|
||||||
|
"""Encrypt one 16-byte block (exact replica of UdbAESUtil::Cipher)."""
|
||||||
|
assert len(pt) == 16 and len(rk) == 176
|
||||||
|
ctx = _Ctx(pt, rk)
|
||||||
|
init = _parse(CIPHER_INIT)
|
||||||
|
sub = _parse(SUBBYTES)
|
||||||
|
mc = _parse(MIXCOL)
|
||||||
|
ark = _parse(ARK)
|
||||||
|
fin = _parse(FINAL_STORE)
|
||||||
|
|
||||||
|
ctx.exec_(init) # initial AddRoundKey + register setup
|
||||||
|
assert ctx.R[8] == 10 and ctx.rkpos == 0x1f
|
||||||
|
for _ in range(10):
|
||||||
|
ctx.exec_(sub) # SubBytes (fused with register permutation)
|
||||||
|
if ctx.R[8] != 1: # cmp x8, #1 ; b.eq -> skip MixColumns
|
||||||
|
ctx.exec_(mc) # MixColumns + rename
|
||||||
|
ctx.exec_(ark) # AddRoundKey; subs x8; post-inc rkpos
|
||||||
|
if ctx.R[8] == 0: # b.eq 0x24f718 -> final store
|
||||||
|
break
|
||||||
|
# rkpos must have advanced to the next round key block
|
||||||
|
# (x10 = this+0x227 + 16*k ; block k+1 spans rk[16+16k .. 31+16k])
|
||||||
|
ctx.exec_(fin)
|
||||||
|
return bytes(ctx.out)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def encode_aes(plaintext: bytes, key: bytes) -> bytes:
|
||||||
|
"""16-byte block encrypt; key of any length >= 16 (only first 16 used)."""
|
||||||
|
if len(plaintext) != 16:
|
||||||
|
raise ValueError("plaintext must be exactly 16 bytes")
|
||||||
|
if len(key) < 16:
|
||||||
|
raise ValueError("key must be at least 16 bytes")
|
||||||
|
return cipher(plaintext, key_expansion(key))
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Self-test vs the authoritative unidbg AesProbe vectors
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def _check(name, got, want):
|
||||||
|
ok = got.hex() == want
|
||||||
|
print(f"[{name}] {'PASS' if ok else 'FAIL'} got={got.hex()} want={want}")
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
_check_sequence(CIPHER_INIT, 0x24f32c, 0x24f3f8, "CIPHER_INIT")
|
||||||
|
_check_sequence(ARK, 0x24f400, 0x24f480, "ARK")
|
||||||
|
_check_sequence(SUBBYTES, 0x24f488, 0x24f506, "SUBBYTES")
|
||||||
|
_check_sequence(MIXCOL, 0x24f510, 0x24f712, "MIXCOL")
|
||||||
|
_check_sequence(FINAL_STORE, 0x24f718, 0x24f76c, "FINAL_STORE")
|
||||||
|
|
||||||
|
keyA = b"0123456789abcdef" * 4
|
||||||
|
keyB = b"ZMHAVPRaxJ3MtXDjduUnXAKQ" + b"\0" * (64 - 24)
|
||||||
|
keyD = b"owNMiaCgcHmqoTr3iRamFuHj" + b"\0" * (64 - 24)
|
||||||
|
pt = b"0123456789abcdef"
|
||||||
|
|
||||||
|
# --- hard assertions (all three verified against the AesProbe output) ---
|
||||||
|
okA = _check("A", encode_aes(pt, keyA), "72727e881edcfd0100a718687909b565")
|
||||||
|
okB = _check("B", encode_aes(pt, keyB), "ba3fb8f156b03a9d7db185d1254e0730")
|
||||||
|
okD = _check("D", encode_aes(pt, keyD), "74bd517c7e5d2bbce63c8e98a192c760")
|
||||||
|
|
||||||
|
# --- vector C from the task brief --------------------------------------
|
||||||
|
# plaintext given as 16 hex chars = 8 bytes; a 16-byte block is required.
|
||||||
|
# The 16-byte form used by the original probe is not present in this
|
||||||
|
# workspace (AesProbe.java only calls encode_aes with the A/B/D inputs),
|
||||||
|
# so C cannot be reproduced until its full plaintext is supplied.
|
||||||
|
c_pt = bytes.fromhex("1e8bdf7d4f7a01d3")
|
||||||
|
if len(c_pt) != 16:
|
||||||
|
print(f"[C] SKIP: plaintext truncated in task brief "
|
||||||
|
f"({len(c_pt)} of 16 bytes given) - cannot verify "
|
||||||
|
f"2cdcf6ad78b8fe0b9ed56004054d2a09")
|
||||||
|
okC = True
|
||||||
|
else:
|
||||||
|
okC = _check("C", encode_aes(c_pt, keyB),
|
||||||
|
"2cdcf6ad78b8fe0b9ed56004054d2a09")
|
||||||
|
|
||||||
|
# key[16..63] must be irrelevant (matches the disassembly)
|
||||||
|
assert encode_aes(pt, keyA[:16]) == encode_aes(pt, keyA)
|
||||||
|
|
||||||
|
print()
|
||||||
|
if okA and okB and okC and okD:
|
||||||
|
print("ALL VERIFIABLE PROBE VECTORS (A/B/D) REPRODUCED BIT-EXACTLY")
|
||||||
|
return 0
|
||||||
|
print("MISMATCH(ES) DETECTED")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -156,6 +156,27 @@ public class AesProbe {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dumpKeyMgr() {
|
||||||
|
UnidbgPointer self = emulator.getMemory().malloc(0x80, false).getPointer();
|
||||||
|
self.write(0, new byte[0x80], 0, 0x80);
|
||||||
|
module.callFunction(emulator, 0x26fcc8L, UnidbgPointer.nativeValue(self));
|
||||||
|
System.out.println("[keymgr] C2 done. object 0x80B:");
|
||||||
|
byte[] buf = new byte[0x80];
|
||||||
|
self.read(0, buf, 0, 0x80);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < 0x80; i++) {
|
||||||
|
byte b = buf[i];
|
||||||
|
if (b >= 0x20 && b < 0x7f) sb.append((char) b); else sb.append('.');
|
||||||
|
if ((i + 1) % 16 == 0) sb.append('\n');
|
||||||
|
}
|
||||||
|
System.out.println(sb.toString());
|
||||||
|
// 也尝试 getkey(1,0) 返回值 (x0)
|
||||||
|
try {
|
||||||
|
Number r = module.callFunction(emulator, 0x26871cL, UnidbgPointer.nativeValue(self), 1L, 0L);
|
||||||
|
System.out.println("[keymgr] getkey(1,0) ret-x0 = " + Long.toHexString(r == null ? -1 : r.longValue()));
|
||||||
|
} catch (Throwable t) { System.out.println("[keymgr] getkey err " + t); }
|
||||||
|
}
|
||||||
|
|
||||||
static String toHex(String s) {
|
static String toHex(String s) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (byte b : s.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1)) sb.append(String.format("%02x", b));
|
for (byte b : s.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1)) sb.append(String.format("%02x", b));
|
||||||
@@ -192,6 +213,7 @@ public class AesProbe {
|
|||||||
p.callOtp("abcd", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
p.callOtp("abcd", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||||
p.callXxtea("abc", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
p.callXxtea("abc", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||||
p.callMd5Char16("abc");
|
p.callMd5Char16("abc");
|
||||||
|
p.dumpKeyMgr();
|
||||||
p.emulator.close();
|
p.emulator.close();
|
||||||
p.emulator.close();
|
p.emulator.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package hydev;
|
||||||
|
|
||||||
|
import com.github.unidbg.AndroidEmulator;
|
||||||
|
import com.github.unidbg.Module;
|
||||||
|
import com.github.unidbg.arm.backend.BackendFactory;
|
||||||
|
import com.github.unidbg.arm.backend.DynarmicFactory;
|
||||||
|
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
|
||||||
|
import com.github.unidbg.linux.android.AndroidResolver;
|
||||||
|
import com.github.unidbg.linux.android.dvm.DalvikModule;
|
||||||
|
import com.github.unidbg.linux.android.dvm.VM;
|
||||||
|
import com.github.unidbg.memory.Memory;
|
||||||
|
import com.github.unidbg.pointer.UnidbgPointer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 差分探针: 与 huya_aes_replica.py 对拍 encode_aes@0x330218
|
||||||
|
* 1) C-vector: in="1e8bdf7d4f7a01d3"(ASCII 16B) key=ZMHAV..+40NUL -> ?
|
||||||
|
* 2) 随机差分: 若干 16B 明文 x 64B 钥
|
||||||
|
*/
|
||||||
|
public class DiffProbe {
|
||||||
|
|
||||||
|
private final AndroidEmulator emulator;
|
||||||
|
private final Module module;
|
||||||
|
|
||||||
|
DiffProbe(String soPath) throws Exception {
|
||||||
|
BackendFactory backend = new DynarmicFactory(true);
|
||||||
|
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.duowan.kiwi")
|
||||||
|
.addBackendFactory(backend).build();
|
||||||
|
Memory memory = emulator.getMemory();
|
||||||
|
memory.setLibraryResolver(new AndroidResolver(23));
|
||||||
|
VM vm = emulator.createDalvikVM();
|
||||||
|
vm.setJni(new com.github.unidbg.linux.android.dvm.AbstractJni() {});
|
||||||
|
vm.setVerbose(false);
|
||||||
|
DalvikModule dm = vm.loadLibrary(new File(soPath), false);
|
||||||
|
dm.callJNI_OnLoad(emulator);
|
||||||
|
module = dm.getModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnidbgPointer writeStdString(String s) {
|
||||||
|
Memory mem = emulator.getMemory();
|
||||||
|
byte[] data = s.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
int len = data.length;
|
||||||
|
UnidbgPointer p;
|
||||||
|
if (len <= 22) {
|
||||||
|
p = mem.malloc(24, false).getPointer();
|
||||||
|
byte[] buf = new byte[24];
|
||||||
|
buf[0] = (byte) (len << 1);
|
||||||
|
System.arraycopy(data, 0, buf, 1, len);
|
||||||
|
p.write(0, buf, 0, buf.length);
|
||||||
|
} else {
|
||||||
|
UnidbgPointer heap = mem.malloc(len + 1, false).getPointer();
|
||||||
|
heap.write(0, data, 0, len);
|
||||||
|
heap.write(len, new byte[]{0}, 0, 1);
|
||||||
|
p = mem.malloc(24, false).getPointer();
|
||||||
|
byte[] cap = new byte[8]; Arrays.fill(cap, (byte) 0xff);
|
||||||
|
p.write(0, cap, 0, 8);
|
||||||
|
p.write(8, new byte[]{(byte) (len & 0xff), (byte) (len >> 8), 0, 0, 0, 0, 0, 0}, 0, 8);
|
||||||
|
byte[] ptrBytes = new byte[8];
|
||||||
|
long pv = UnidbgPointer.nativeValue(heap);
|
||||||
|
for (int i = 0; i < 8; i++) ptrBytes[i] = (byte) (pv >> (8 * i));
|
||||||
|
p.write(16, ptrBytes, 0, 8);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
String readStdString(UnidbgPointer p) {
|
||||||
|
byte[] one = new byte[1];
|
||||||
|
p.read(0, one, 0, 1);
|
||||||
|
int first = one[0] & 0xff;
|
||||||
|
if ((first & 1) == 0) {
|
||||||
|
int len = first >> 1;
|
||||||
|
byte[] dat = new byte[len];
|
||||||
|
p.read(1, dat, 0, len);
|
||||||
|
return new String(dat, java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
} else {
|
||||||
|
long size = 0;
|
||||||
|
byte[] szb = new byte[8];
|
||||||
|
p.read(8, szb, 0, 8);
|
||||||
|
for (int i = 0; i < 8; i++) size |= ((long) (szb[i] & 0xff)) << (8 * i);
|
||||||
|
byte[] ptrb = new byte[8];
|
||||||
|
p.read(16, ptrb, 0, 8);
|
||||||
|
long pv = 0;
|
||||||
|
for (int i = 0; i < 8; i++) pv |= ((long) (ptrb[i] & 0xff)) << (8 * i);
|
||||||
|
UnidbgPointer data = UnidbgPointer.pointer(this.emulator, pv);
|
||||||
|
byte[] dat2 = new byte[(int) size];
|
||||||
|
data.read(0, dat2, 0, (int) size);
|
||||||
|
return new String(dat2, java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String enc(String in, String key) {
|
||||||
|
UnidbgPointer pin = writeStdString(in);
|
||||||
|
UnidbgPointer pkey = writeStdString(key);
|
||||||
|
UnidbgPointer pout = writeStdString("");
|
||||||
|
module.callFunction(emulator, 0x330218L, UnidbgPointer.nativeValue(pin),
|
||||||
|
UnidbgPointer.nativeValue(pkey), UnidbgPointer.nativeValue(pout));
|
||||||
|
String out = readStdString(pout);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : out.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1))
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String hexBytes(int n, long seed) {
|
||||||
|
// deterministic "random" 16-byte hex string
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
java.util.Random r = new java.util.Random(seed);
|
||||||
|
for (int i = 0; i < n; i++) sb.append(String.format("%02x", r.nextInt(256)));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
String so = args.length > 0 ? args[0] : "so/libudbauthunify_merged.so";
|
||||||
|
DiffProbe p = new DiffProbe(so);
|
||||||
|
String kzm = "ZMHAVPRaxJ3MtXDjduUnXAKQ" + "\0".repeat(40);
|
||||||
|
String k00 = "0123456789abcdef".repeat(4);
|
||||||
|
|
||||||
|
System.out.println("[C1] ascii-16 in=1e8bdf7d4f7a01d3 keyB -> " + p.enc("1e8bdf7d4f7a01d3", kzm));
|
||||||
|
System.out.println("[C2] flag+15 in=\\u0020+0123456789abcde keyB -> " + p.enc("\u00200123456789abcde", kzm));
|
||||||
|
System.out.println("[C3] hexbytes8pad in=1e8bdf7d4f7a01d3+0000000000000000 keyB -> " + p.enc("1e8bdf7d4f7a01d3\0\0\0\0\0\0\0\0", kzm));
|
||||||
|
System.out.println("[X0] in=0123456789abcdef keyB -> " + p.enc("0123456789abcdef", kzm));
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
long seed = 1000 + i;
|
||||||
|
String in = hexBytes(16, seed);
|
||||||
|
String key = hexBytes(64, seed ^ 0x5a5a);
|
||||||
|
System.out.println("[R" + i + "] in=" + in + " key=" + key + " -> " + p.enc(in, key));
|
||||||
|
}
|
||||||
|
// 用 64 个 'A' 测试纯 64B ASCII 钥
|
||||||
|
System.out.println("[KAA] in=0123456789abcdef key='A'*64 -> " + p.enc("0123456789abcdef", "A".repeat(64)));
|
||||||
|
// 24B 短钥 (short string, flag 参与装载?)
|
||||||
|
System.out.println("[K24] in=0123456789abcdef key=0123456789abcdef(16B short) -> " + p.enc("0123456789abcdef", "0123456789abcdef"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user