# 某航空 APP wToken 生成机制深度分析报告(v3.0 整合版)

整合来源:看雪社区 shmilyaxy、博客园 🐳.城南、GitHub wtoken2023、CSDN 等多篇逆向分析文章


一、wToken 架构总览

某航空 APP 的 wToken 参数并非单一算法产物,而是阿里聚安全(SecurityGuard)多阶段初始化 + AVMP虚拟化签名的结果。整个调用链涉及6次 doCommandNative 调用,最终通过命令码 60902 生成 wToken。

┌──────────────────────────────────────────────────────────────────┐
│                    wToken 生成完整调用链                           │
├─────────┬──────────────────────────────────┬─────────────────────┤
│ 命令码   │ 功能                              │ 关键输入参数         │
├─────────┼──────────────────────────────────┼─────────────────────┤
│ 10101   │ SDK主初始化                       │ Context, 3, "",     │
│         │                                   │ /app_SGLib, ""      │
│ 10102   │ libsgmainso 插件初始化             │ "main", 5.4.193,    │
│         │                                   │ so路径               │
│ 10102   │ libsgsecuritybodyso 初始化         │ "securitybody",     │
│         │                                   │ 5.4.112, so路径      │
│ 10102   │ libsgavmpso 插件初始化             │ "avmp", 5.4.1002,   │
│         │                                   │ so路径               │
│ 60901   │ AVMP 实例创建                     │ "0335_mwua",        │
│         │                                   │ "sgcipher"          │
│ 60902   │ ★ wToken 签名生成 ★              │ AVMP实例ID, "sign",  │
│         │                                   │ 请求体JSON, 参数数组 │
└─────────┴──────────────────────────────────┴─────────────────────┘

二、各阶段详细分析

阶段1: SDK主初始化 (10101)

调用方法: JNICLibrary.doCommandNative(10101, args)

输入参数:

args[0] = Context对象 (com.rytong.ceair.CeairApp)
args[1] = 3 (SDK版本标志)
args[2] = "" (空字符串)
args[3] = "/data/user/0/com.rytong.ceair/app_SGLib" (数据目录)
args[4] = "" (空字符串)

返回值: 0 (成功)

关键说明: 此阶段为SecurityGuard SDK的核心初始化,低版本unidbg(0.8.0)返回null而非0,需手动补环境。建议使用 unidbg 0.9.5 版本,可避免 SecException(0xC7) 错误。


阶段2: 插件链初始化 (10102 × 3)

此命令码被连续调用3次,分别初始化3个不同的安全插件SO:

2.1 libsgmainso-5.4.193.so

args[0] = "main"
args[1] = "5.4.193"
args[2] = "/data/app/.../lib/arm64/libsgmainso-5.4.193.so"

2.2 libsgsecuritybodyso-5.4.112.so

args[0] = "securitybody"
args[1] = "5.4.112"
args[2] = "/data/app/.../lib/arm64/libsgsecuritybodyso-5.4.112.so"

2.3 libsgavmpso-5.4.1002.so

args[0] = "avmp"
args[1] = "5.4.1002"
args[2] = "/data/user/0/.../app_SGLib/.../main/libsgavmpso-5.4.1002.so"

返回值: 全部为 0

注意: 初始化顺序不可更改,AVMP实例依赖于前序插件的注册。


阶段3: AVMP实例创建 (60901)

调用方法: JNICLibrary.doCommandNative(60901, args)

输入参数:

args[0] = "0335_mwua"      (业务标识/场景码)
args[1] = "sgcipher"        (加密类型标识)

返回值: 475517112478 (AVMP实例ID,需 & 0xffffffffL 处理)

作用: 创建一个AVMP(阿里虚拟机保护)实例,用于后续的加密签名操作。0335_mwua 是某航APP的业务标识,不同APP此值不同。


阶段4: wToken 生成 (60902) ★

调用方法: JNICLibrary.doCommandNative(60902, args)

外层参数:

args[0] = AVMP实例ID (475517112478)
args[1] = "sign"               (操作类型)
args[2] = byte[].class         (返回类型声明)
args[3] = innerArgs (内层参数数组)

内层参数 (aryobj1):

innerArgs[0] = 3 (标志位)
innerArgs[1] = 请求体JSON的字节数组 (如: {"req":"S6TVWfiiBE2Z..."})
innerArgs[2] = 请求体JSON的长度
innerArgs[3] = "" (空字符串)
innerArgs[4] = new byte[4] (4字节空数组)
innerArgs[5] = 0 (标志位)

Hook返回结果:

QllUUl9yUncwbkFVNFNpWTg5WnlnVldBcnZ2bmZuYXhVZnY4WHE4WDREcGhvNmNY
SkdzM2dMMjZ5YUV6dndoRUorSjF5V1ZRVHNxZnBndk1PdVW5zNVdyMzg3cFM2VTJi
akFPZTExZ1NzakNSRWplcEJ0NmVYU2w0eHNtWll2UFV0MmYxVTM5TEFyN01ZbzVo
ZDMzS0gzKzQ1aXd4aDY0TGdVaVViPaXRTRjExTmNoRnpjcHlyOFE3Q2NZPSZBV0VS
X2EwMDEzOTIzNzc5M2I1YWE5YjJiYWVkZWI4NWYwOWM1ZDAwYWYxZDU3ZTE4NQ==

Base64解码后:

BYTR_rRw0nAU4SiY89ZyGVArvvnfnaxUfv8Xq8X4Dpho6cXJGs3gL26yaEvzwhE
J+J1yWVPQsqfpgvMOuUns5Wr387pS6U2bjAOe11gSsjCREjepBt6eXSl4xsmZYvP
Ut2f1U39LAr7MYo5hd33KH3+45iwxh64LgUiUbPiTSF11NchFzcpyr8Q7CcY==&A
WER_a00139237793b5aa9b2baedeb85f09c5d00af1d57e185

wToken 结构解析:

┌────────────────────────────────────────────────────────────────────┐
│                        wToken 格式分解                              │
├────────────────────────────────────────────────────────────────────┤
│ BYTR_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==   │
│  └┬┘  └─────────────────── 签名主体 ────────────────────────┘      │
│   │                                                         │      │
│   └─ 签名类型标识 (Base64+RSA密文)                           │      │
│                                                             │      │
│ &AWER_a00139237793b5aa9b2baedeb85f09c5d00af1d57e185          │      │
│  └┬─┘ └─────────────────── 设备指纹 ────────────────────────┘       │
│   │                                                              │
│   └─ 设备标识符 (a001 = appKey前缀, 后跟设备哈希)                    │
└────────────────────────────────────────────────────────────────────┘

三、Java 层调用链

3.1 完整调用栈(从Frida Hook获取)

acc.intercept(EncodeRequestInterceptor.kt:92)          ← 加密拦截器注入点
└── aej.a(AliPreWorm.java:86)                          ← 阿里安全壳封装
    └── avmpSign(AVMPInstance, "sign", data, args)     ← AVMP签名入口
        └── invokeAVMP(instance, "sign", byte[], args)  ← JNI调用
            └── JNICLibrary.doCommandNative(60902, ...)  ← Native层入口
                └── libsgmainso                          ← 主安全库
                    └── libsgavmpso                      ← AVMP虚拟机

3.2 关键类文件定位

类文件 功能 行号
EncodeRequestInterceptor.kt OkHttp请求拦截器,注入wToken 92
AliPreWorm.java 阿里安全壳调用封装 54(初始化), 86(签名)
TransactionIdInterceptor.kt 交易ID注入 59
SplashActivity.kt 启动页,触发SDK初始化 477
CeairApp.java Application类,同意隐私后初始化 86

四、Unidbg 复现方案

4.1 版本选择

版本 结果 说明
0.8.0 ⚠️ 需大量补环境 无自动环境修补,但可逐一手动补
0.9.5 ✓ 推荐 社区验证的最佳版本,SecException最少
0.9.6+ ✗ SecException(0xC7) 自动补环境反而导致问题
0.9.7 ✗ 需特殊处理 null初始化SecException可绕过

4.2 SO 文件选择

必须使用 32 位 (armeabi-v7a) SO 文件,64位在unidbg中兼容性差。

需要提取的SO文件:

libsgmainso-5.4.193.so           (主安全库)
libsgsecuritybodyso-5.4.112.so   (安全体插件)
libsgavmpso-5.4.1002.so          (AVMP虚拟机)

4.3 完整 Unidbg 代码

基于看雪 shmilyaxy 的分析,核心调用顺序如下:

// 1. 创建模拟器(32位)
emulator = new AndroidARMEmulator("com.rytong.ceair");

// 2. 创建DalvikVM
vm = emulator.createDalvikVM(new File("base.apk"));
vm.setJni(this);

// 3. 加载主SO
DalvikModule dm = vm.loadLibrary(new File("libsgmainso-5.4.193.so"), true);
dm.callJNI_OnLoad(emulator);

// 4. 10101 - SDK初始化
ArrayObject init_arg = new ArrayObject(
    vm.resolveClass("android/content/Context").newObject(null),
    DvmInteger.valueOf(vm, 3),
    new StringObject(vm, ""),
    new StringObject(vm, "/data/user/0/com.rytong.ceair/app_SGLib"),
    new StringObject(vm, "")
);
DvmObject<?> res1 = JNICLibrary.callStaticJniMethodObject(
    emulator, "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
    10101, init_arg
);

// 5. 10102 - 初始化3个插件(依次执行)
// ... libsgmainso, libsgsecuritybodyso, libsgavmpso

// 6. 60901 - 创建AVMP实例
ArrayObject vmp_arg = new ArrayObject(
    new StringObject(vm, "0335_mwua"),
    new StringObject(vm, "sgcipher")
);
DvmObject<?> vmp_res = JNICLibrary.callStaticJniMethodObject(..., 60901, vmp_arg);
long avmpId = Long.valueOf(vmp_res.getValue().toString()) & 0xffffffffL;

// 7. 60902 - 生成wToken
String requestBody = "{\"req\":\"...base64...\"}";
ArrayObject inner = new ArrayObject(
    DvmInteger.valueOf(vm, 3),
    new ByteArray(vm, requestBody.getBytes()),
    DvmInteger.valueOf(vm, requestBody.length()),
    new StringObject(vm, ""),
    new ByteArray(vm, new byte[4]),
    DvmInteger.valueOf(vm, 0)
);
ArrayObject outer = new ArrayObject(
    DvmLong.valueOf(vm, avmpId),
    new StringObject(vm, "sign"),
    vm.resolveClass("[B"),
    inner
);
DvmObject<?> wToken = JNICLibrary.callStaticJniMethodObject(..., 60902, outer);
System.out.println(wToken); // 输出: BYTR_...&AWER_...

4.4 必补 JNIEnv 环境

@Override
public DvmObject<?> callObjectMethod(VM vm, DvmObject<?> dvmObject, 
                                     String signature, VarArg varArg) {
    switch (signature) {
        // 应用路径
        case "android/content/Context->getPackageCodePath()Ljava/lang/String;":
            return new StringObject(vm, "/data/app/com.rytong.ceair.apk");
        
        // 文件目录
        case "android/content/Context->getFilesDir()Ljava/io/File;":
            return vm.resolveClass("java/io/File").newObject(
                new File("/data/data/com.rytong.ceair/files"));
        
        // WiFi MAC地址(固定值)
        case "android/net/wifi/WifiInfo->getMacAddress()Ljava/lang/String;":
            return new StringObject(vm, "02:00:00:00:00:00");
    }
    return super.callObjectMethod(vm, dvmObject, signature, varArg);
}

@Override
public DvmObject<?> getObjectField(VM vm, DvmObject<?> dvmObject, 
                                    String signature) {
    switch (signature) {
        case "android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;":
            return new StringObject(vm, "/data/app/.../lib/arm");
    }
    return super.getObjectField(vm, dvmObject, signature);
}

@Override
public DvmObject<?> callStaticObjectMethod(VM vm, DvmClass dvmClass,
                                            String signature, VarArg varArg) {
    switch (signature) {
        case "android/app/ActivityThread->currentPackageName()Ljava/lang/String;":
            return new StringObject(vm, "com.rytong.ceair");
        
        case "android/os/SystemProperties->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;":
            String key = (String) varArg.getObject(0).getValue();
            if (key.contains("ro.serialno")) return new StringObject(vm, "94CX1Z56A");
            return new StringObject(vm, "");
    }
    return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
}

@Override
public int getStaticIntField(VM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "android/os/Build$VERSION->SDK_INT:I":
            return 23; // Android 6.0
    }
    return super.getStaticIntField(vm, dvmClass, signature);
}

4.5 SecException(1910) 解决方案

错误含义: 非法的AVMP实例(Invalid AVMP instance)

根本原因: 60901创建的AVMP实例ID未正确传递,或实例在后续调用时已被释放。

解决步骤:

  1. 确认60901返回非null值(应为一个Long数值)
  2. 对返回值执行 & 0xffffffffL 转换为有效实例ID
  3. 确认使用 32位 SO文件
  4. 如仍报错,尝试降级到 unidbg 0.9.5
  5. 极端方案:自行构造SecException对象返回,跳过异常中断

五、梆梆加固与阿里安全的关系

┌────────────────────────────────────────────────────────────────────┐
│                    某航APP 安全架构全景图                           │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │                      梆梆加固企业版                           │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │  │
│  │  │ libDexHelper │  │ libdexjni.so │  │ CheckCode (MD5)  │  │  │
│  │  │   (SO加载)    │  │ (JNI桥接)    │  │ 设备指纹校验      │  │  │
│  │  └──────────────┘  └──────────────┘  └──────────────────┘  │  │
│  │                       ↓ 透明代理加密请求体                     │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                              │                                     │
│                              ↓                                     │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │                  阿里聚安全 (SecurityGuard)                  │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │  │
│  │  │  libsgmainso │  │libsgsecurity │  │  libsgavmpso     │  │  │
│  │  │   (主引擎)    │  │   bodyso      │  │  (AVMP虚拟机)     │  │  │
│  │  └──────────────┘  └──────────────┘  └──────────────────┘  │  │
│  │                       ↓ doCommandNative(60902)               │  │
│  │                       ↓ 生成 wToken                          │  │
│  │  格式: BYTR_{签名}==&AWER_{设备ID}                           │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                              │                                     │
│                              ↓                                     │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │                    其他风控组件                               │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │  │
│  │  │   同盾 SDK   │  │  极光推送    │  │   支付宝风控      │  │  │
│  │  │ (libtdvm.so) │  │(libjiguang)  │  │  (apdidToken)    │  │  │
│  │  │  ★ 未启用    │  │   独立运行    │  │  color令牌       │  │  │
│  │  └──────────────┘  └──────────────┘  └──────────────────┘  │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

分工说明

组件 功能 状态
梆梆加固 SO保护、代码混淆、请求体透明加密 主防线
阿里聚安全 wToken生成、设备指纹、反篡改 风控核心
CheckCode 设备信息MD5校验(梆梆) 辅助校验
同盾SDK 备用风控(初始化失败) 未实际工作
支付宝 apdidToken设备标识 辅助风控

六、关键数据汇总

6.1 插件版本号(随APP版本变化)

插件 版本 文件路径示例
libsgmainso 5.4.193 .../lib/arm64/libsgmainso-5.4.193.so
libsgsecuritybodyso 5.4.112 .../lib/arm64/libsgsecuritybodyso-5.4.112.so
libsgavmpso 5.4.1002 .../app_SGLib/.../main/libsgavmpso-5.4.1002.so

6.2 AVMP业务标识

场景码: 0335_mwua
加密类型: sgcipher

6.3 wToken 返回示例(真实Hook数据)

BYTR_rRw0nAU4SiY89ZyGVArvvnfnaxUfv8Xq8X4Dpho6cXJGs3gL26yaEvzwhEJ+
J1yWVPQsqfpgvMOuUns5Wr387pS6U2bjAOe11gSsjCREjepBt6eXSl4xsmZYvPUt2
f1U39LAr7MYo5hd33KH3+45iwxh64LgUiUbPiTSF11NchFzcpyr8Q7CcY==&AWER_
a00139237793b5aa9b2baedeb85f09c5d00af1d57e185

BYTR = 阿里签名标识,AWER 后的 a001 为APP密钥前缀,后续为设备哈希。


七、总结与建议

7.1 核心结论

  1. wToken来源: 100% 确认来自阿里SecurityGuard SDK的 doCommandNative(60902),非梆梆生成
  2. 调用入口: EncodeRequestInterceptor.kt:92 → AliPreWorm.java:86
  3. 虚拟化保护: 使用AVMP(阿里虚拟机保护),直接逆向native代码极难
  4. Unidbg可行: 通过unidbg模拟调用可复现完整wToken生成流程
  5. 环境敏感: 对Android环境参数(SDK版本、MAC地址、序列号等)有强校验

7.2 实战建议

方案 难度 成功率 说明
Unidbg模拟调用 中 高 推荐方案,需补全JNIEnv环境
Frida Hook注入 中 中 需绕过梆梆反Frida检测
算法还原 极高 低 AVMP虚拟化保护,不建议
真机RPC转发 低 高 最稳定方案,需维护设备池

7.3 Unidbg版本建议

强烈推荐使用 unidbg 0.9.5,该版本经社区多人大规模验证,对阿里SecurityGuard系列的兼容性最佳。高于此版本(0.9.6+)的自动环境修补机制反而会导致 SecException(0xC7/1910) 系列错误。


八、参考资料

来源 作者 链接 核心贡献
看雪社区 shmilyaxy bbs.kanxue.com/thread-277006.htm 完整doCommandNative调用链 + Unidbg代码
博客园 🐳.城南 cnblogs.com/dockers/p/19238044 梆梆去壳 + Frida分析
GitHub irabbit666666 github.com/irabbit666666/wtoken2023 wToken2023通用库
91fans - 91fans.com.cn/post/iboxone/ genericNt3 Frida Hook
看雪社区 - bbs.kanxue.com/thread-279374.htm 梆梆企业版加固分析
CSDN 编程达人_study blog.csdn.net/weixin_44251614 ibox wToken加密分析

本报告仅供安全研究和学习交流使用,请遵守相关法律法规。

posted @ 2026-09-01 11:46  郭楷丰  阅读(125)  评论(0)    收藏  举报
Live2D