"""Weapon p0 ``Engine.pr(99999, 0, ...)`` 的纯 Python VIMG base 生成器。""" from __future__ import annotations import base64 import struct VIMG_PREFIX = "VIMG_" _MASK32 = 0xFFFFFFFF _XOR_BYTE = 0x55 _BLAKE2S_SIGMA = ( (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), (14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), (11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), (7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), (9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), (2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), (12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), (13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), (6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), (10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), ) _H1_INITIAL_STATE = ( 0xAA98186E, 0xF3CCD768, 0x99531AAE, 0x669781D3, 0x125FD5B4, 0x9883595E, 0x74F4CBCD, 0x98C26A78, ) _H1_IV = (0xAB99184E, *_H1_INITIAL_STATE[1:]) _H2_ADD_BASE = bytes.fromhex("0c35ff3dcbfefb3f3efd6539fd39efcd") _AI_XOR_MASK = bytes.fromhex("2dd345c0") # 0x1a6b28 初始化、0x1a79f8 执行 20-round ChaCha block。这里保留 native # 实际状态,不替换为标准的 "expand 32-byte k" 常量。 _CHACHA_STATE = ( 0x1783595E, 0x8DC26A78, 0x2599184E, 0x729781D3, 0x2ADEF3F4, 0x9876EF16, 0x9ABED34F, 0x9103DE12, 0xA92157F6, 0xA9A24FF4, 0x9138D3FD, 0x2A2193F3, 1, 0x74F4CBCD, 0x98C26A78, 0xAB99184E, ) def _rotate_left32(value: int, shift: int) -> int: return ((value << shift) | (value >> (32 - shift))) & _MASK32 def _rotate_right32(value: int, shift: int) -> int: return ((value >> shift) | (value << (32 - shift))) & _MASK32 def _quarter_round(words: list[int], a: int, b: int, c: int, d: int) -> None: words[a] = (words[a] + words[b]) & _MASK32 words[d] = _rotate_left32(words[d] ^ words[a], 16) words[c] = (words[c] + words[d]) & _MASK32 words[b] = _rotate_left32(words[b] ^ words[c], 12) words[a] = (words[a] + words[b]) & _MASK32 words[d] = _rotate_left32(words[d] ^ words[a], 8) words[c] = (words[c] + words[d]) & _MASK32 words[b] = _rotate_left32(words[b] ^ words[c], 7) def _chacha_block(counter: int) -> bytes: if not 0 <= counter <= _MASK32: raise ValueError("VIMG ChaCha counter 超出 uint32") initial = list(_CHACHA_STATE) initial[12] = counter working = initial.copy() for _ in range(10): _quarter_round(working, 0, 4, 8, 12) _quarter_round(working, 1, 5, 9, 13) _quarter_round(working, 2, 6, 10, 14) _quarter_round(working, 3, 7, 11, 15) _quarter_round(working, 0, 5, 10, 15) _quarter_round(working, 1, 6, 11, 12) _quarter_round(working, 2, 7, 8, 13) _quarter_round(working, 3, 4, 9, 14) return struct.pack( "<16I", *((value + original) & _MASK32 for value, original in zip(working, initial)), ) def _java_modified_utf8(value: str) -> bytes: """复现 JNI ``GetStringUTFChars`` 对 Java String 的 modified UTF-8。""" utf16 = value.encode("utf-16-be", errors="surrogatepass") output = bytearray() for offset in range(0, len(utf16), 2): code_unit = int.from_bytes(utf16[offset : offset + 2], "big") if 0x01 <= code_unit <= 0x7F: output.append(code_unit) elif code_unit <= 0x7FF: output.extend((0xC0 | (code_unit >> 6), 0x80 | (code_unit & 0x3F))) else: output.extend( ( 0xE0 | (code_unit >> 12), 0x80 | ((code_unit >> 6) & 0x3F), 0x80 | (code_unit & 0x3F), ) ) return bytes(output) def _decode_java_modified_utf8(value: bytes) -> str: code_units: list[int] = [] offset = 0 while offset < len(value): first = value[offset] if 0x01 <= first <= 0x7F: code_units.append(first) offset += 1 elif first & 0xE0 == 0xC0 and offset + 1 < len(value): code_units.append(((first & 0x1F) << 6) | (value[offset + 1] & 0x3F)) offset += 2 elif first & 0xF0 == 0xE0 and offset + 2 < len(value): code_units.append( ((first & 0x0F) << 12) | ((value[offset + 1] & 0x3F) << 6) | (value[offset + 2] & 0x3F) ) offset += 3 else: raise ValueError("VIMG payload 含非法 modified UTF-8") utf16 = b"".join(code_unit.to_bytes(2, "big") for code_unit in code_units) return utf16.decode("utf-16-be", errors="surrogatepass") def _xor_chacha(data: bytes) -> bytes: output = bytearray(len(data)) for block_index, offset in enumerate(range(0, len(data), 64), start=1): key_stream = _chacha_block(block_index) chunk = data[offset : offset + 64] output[offset : offset + len(chunk)] = ( value ^ key_stream[index] for index, value in enumerate(chunk) ) return bytes(output) def generate_vimg_base(payload: str) -> str: """生成 ``Engine.pr(..., mode=0, payload)`` 的 ``VIMG_`` 部分。""" payload_bytes = _java_modified_utf8(payload) if len(payload_bytes) > 0xFFFF: raise ValueError("VIMG payload 超过 native uint16 长度上限") plain = b"\x2d\x3d\x00\x00\x7d\x01" + struct.pack(" list[int]: """复现 0x1ab3d0 的自定义 IV BLAKE2s 压缩。""" if len(message) != 16: raise ValueError("H1 压缩块必须包含 16 个 uint32") working = state.copy() + list(_H1_IV) working[12] ^= counter & _MASK32 working[13] ^= (counter >> 32) & _MASK32 if is_last: working[14] ^= _MASK32 def mix(a: int, b: int, c: int, d: int, x: int, y: int) -> None: working[a] = (working[a] + working[b] + x) & _MASK32 working[d] = _rotate_right32(working[d] ^ working[a], 16) working[c] = (working[c] + working[d]) & _MASK32 working[b] = _rotate_right32(working[b] ^ working[c], 12) working[a] = (working[a] + working[b] + y) & _MASK32 working[d] = _rotate_right32(working[d] ^ working[a], 8) working[c] = (working[c] + working[d]) & _MASK32 working[b] = _rotate_right32(working[b] ^ working[c], 7) for schedule in _BLAKE2S_SIGMA: mix(0, 4, 8, 12, message[schedule[0]], message[schedule[1]]) mix(1, 5, 9, 13, message[schedule[2]], message[schedule[3]]) mix(2, 6, 10, 14, message[schedule[4]], message[schedule[5]]) mix(3, 7, 11, 15, message[schedule[6]], message[schedule[7]]) mix(0, 5, 10, 15, message[schedule[8]], message[schedule[9]]) mix(1, 6, 11, 12, message[schedule[10]], message[schedule[11]]) mix(2, 7, 8, 13, message[schedule[12]], message[schedule[13]]) mix(3, 4, 9, 14, message[schedule[14]], message[schedule[15]]) return [ (state[index] ^ working[index] ^ working[index + 8]) & _MASK32 for index in range(8) ] def _generate_h1_words(vimg_base: str) -> list[int]: base_bytes = vimg_base.encode("ascii") word_count = (len(base_bytes) + 3) // 4 padded = base_bytes.ljust(word_count * 4, b"\0") words = list(struct.unpack(f"<{word_count}I", padded)) state = list(_H1_INITIAL_STATE) # Native 每批读取最多 64 个字,再按索引模 16 折叠成 BLAKE2s 块。 for offset in range(0, word_count, 64): source = words[offset : offset + 64] folded = [0] * 16 for index, value in enumerate(source): folded[index % 16] ^= value counter = offset + len(source) state = _compress_h1( state, folded, counter, is_last=counter == word_count, ) return state def _generate_ai_hex(vimg_base: str) -> str: h1_text = "".join(f"{value:08x}" for value in _generate_h1_words(vimg_base)) + " " h2 = bytes( (((_H2_ADD_BASE[index] + 3) & 0xFF) ^ ord(h1_text[index])) for index in range(16) ) return bytes( value ^ _AI_XOR_MASK[index % len(_AI_XOR_MASK)] for index, value in enumerate(h2) ).hex() def generate_passport_account_image(payload: str) -> str: """纯 Python 生成完整 ``passport_account_image``。""" vimg_base = generate_vimg_base(payload) return f"{vimg_base}$AI_{_generate_ai_hex(vimg_base)}" def decode_passport_account_image_payload(value: str) -> str: """反解本地 ``Engine.pr(..., mode=0)`` 票据并返回原始 Java 字符串。""" vimg_base = str(value).split("$AI_", 1)[0] if not vimg_base.startswith(VIMG_PREFIX): raise ValueError("passport_account_image 缺少 VIMG_ 前缀") try: cipher = base64.b64decode(vimg_base[len(VIMG_PREFIX) :], validate=True) except (ValueError, base64.binascii.Error) as exc: raise ValueError("passport_account_image Base64 非法") from exc native_buffer = _xor_chacha(cipher) plain = bytes(value ^ _XOR_BYTE for value in native_buffer) if len(plain) < 8 or plain[:6] != b"\x2d\x3d\x00\x00\x7d\x01": raise ValueError("passport_account_image VIMG 头非法") payload_length = struct.unpack_from("