ksjsb/docs/libweapon_vimg_feasibility.md
2026-07-30 20:25:56 +08:00

4.2 KiB
Raw Permalink Blame History

libweapon VIMG 纯算实现记录

状态:已完成

约束:运行时不依赖 Frida、抓包、APK、ELF、Unicorn 或 app-fields。

1. 最终结论

passport_account_image 是客户端本地确定性生成值:

mf(context, event).a().toString()
  -> Engine.pr(99999, 0, javaLength * 2, json)
  -> VIMG_<base64>$AI_<32hex>
  -> WeaponHI.img / wcfg["a_y_q_z"]
  -> mobile/checker、requestMobileCode、mobileVerifyCode

wcfg 只负责本地缓存。$AI_ 不是服务端签发值,/f/a/p 也不是 登录票据的生成前置条件。

目标样本:

out/p0_64_extract/lib/arm64-v8a/libweapon.2.2174a68a..so
SHA256 5d7681c583c66b35e0ee3ce558e5a1ff7fa5be44f19a1e217acc2224455837d7

2. Java 与 JNI 链

  • wc.k() 调用 Engine.pr(ze.v0, 0, s2.a(str) * 2, str)
  • ze.v0 = 99999
  • str = new mf(context, event).a().toString()
  • mode 0 对应 native 0x19ea90
  • 0x19ea90 依次调用 base 生成器 0x1a1850 和 AI 生成器 0x1a2d94

3. VIMG Base

payload_bytes = JNI modified UTF-8(payload)
plain = 2d 3d 00 00 7d 01 || LE16(len(payload_bytes)) || payload_bytes
native_buffer = plain XOR 0x55
cipher = ChaCha20-IETF(native_buffer, fixed_state, counter=1)
base = "VIMG_" + Base64(cipher)

ChaCha 初始状态:

1783595e 8dc26a78 2599184e 729781d3
2adef3f4 9876ef16 9abed34f 9103de12
a92157f6 a9a24ff4 9138d3fd 2a2193f3
00000001 74f4cbcd 98c26a78 ab99184e

轮函数是标准 20 轮 ChaCha旋转量为 16/12/8/7

4. AI H1

将完整 VIMG_... ASCII 文本按小端 uint32 分组。调用方每批处理最多 64 个字native 压缩函数先折叠为 16 个字:

folded = [0] * 16
for index, word in enumerate(source_words):
    folded[index % 16] ^= word

随后执行自定义 IV 的 10 轮 BLAKE2s 压缩:

H0:
aa98186e f3ccd768 99531aae 669781d3
125fd5b4 9883595e 74f4cbcd 98c26a78

IV:
ab99184e f3ccd768 99531aae 669781d3
125fd5b4 9883595e 74f4cbcd 98c26a78
  • counter 是累计处理的 uint32 字数。
  • 最后一批令 v[14] ^= 0xffffffff
  • sigma 是标准 BLAKE2s 10x16 消息置换表。
  • 输出为 8 个低 32 位链值。
  • H1 文本为 8 个 %08x 直接拼接,末尾附一个空格。

5. AI H2

H2 只使用 H1 文本前 16 个 ASCII 字节:

ADD_BASE = 0c35ff3dcbfefb3f3efd6539fd39efcd
H2[i] = ((ADD_BASE[i] + 3) & 0xff) XOR H1_TEXT[i]
AI[i] = H2[i] XOR (2dd345c0)[i % 4]

最终:

passport_account_image = base + "$AI_" + hex(AI)

6. mf Payload

core/weapon_mf.pymf.a() 的 JSONObject 插入顺序构造字段,包括:

  • 设备型号、厂商、Android 版本和本地 DID。
  • elapsedRealtime、uptime、启动时间和 boot count。
  • Weapon 上报时间与本地报告计数器。
  • 样本中的反调试/环境状态字段。

同一轮登录只生成一次票据,并复用于预检、发码和验码。

7. 实现与验证

生产实现:

  • core/weapon_vimg.pyVIMG base、H1/H2、完整票据与反解。
  • core/weapon_mf.py:设备 JSON 和 profile 票据生成。
  • tools/sms_login_cli.py:纯算票据接入登录链。

分析 oracle

  • tools/libweapon_vimg_oracle.py:仅用于离线差分,不进入生产路径。

验证结果:

  • 固定 native 向量覆盖空输入、UTF-8、ChaCha 跨块、H1 跨 16/64 字。
  • 21 组不同长度随机输入与 Unicorn native oracle 逐字节一致。
  • 捕获票据反解出的 757 字节真实 mf JSON 可由 oracle 重新生成完全相同 的 base 和 $AI_

8. CLI

短信登录每次生成全新画像、在线注册 DFP并在注册完成后用当前云 DID 现场生成 passport_account_image

uv run python -m tools.sms_login_cli --mobile MOBILE

已有验证码时跳过发码:

uv run python -m tools.sms_login_cli `
  --mobile MOBILE `
  --code CODE

CLI 不提供设备画像、app-fields、WCFG 或复现种子的注入入口,避免把历史设备 状态带入下一次登录。

DFP 在线注册可能把本地初始 DID 替换为云 DID。主 APP 随后调用 WeaponHI.setG(currentDid),所以 mf 字段 03000 必须使用注册后的 DeviceProfile.did,并与登录 query 的 did 保持一致,不能继续使用 local_did