1. mtgsig签名算法概述在美团外卖APP的风控体系中mtgsig签名算法扮演着至关重要的角色。这个算法的主要作用是将设备指纹、环境检测结果与业务请求体动态绑定形成一个防篡改的请求签名。简单来说它就像是一个数字指纹能够唯一标识每个请求的来源和合法性。我逆向分析过多个版本的mtgsig实现发现它的核心设计理念可以概括为三点动态性、唯一性和不可伪造性。动态性体现在每次请求生成的签名都不同唯一性保证了不同设备和不同请求都能被准确区分不可伪造性则通过多层加密机制确保签名无法被轻易破解。在实际应用中mtgsig签名会被附加到每个API请求的Header中。服务端收到请求后会用相同的算法验证签名的有效性。如果签名验证失败请求就会被拒绝这就是为什么有些用户会突然遇到请求异常的提示。2. 签名算法的逆向分析2.1 Native层调用流程mtgsig签名的生成主要在Native层完成通过libmtguard.so这个动态库实现。Java层会调用如下方法初始化签名环境// Java层调用示例 Object[] result MTGuard.main(3, new Object[]{context, requestData});这里的参数3代表执行签名操作。Native层的对应处理逻辑大致如下// Native层伪代码 JNIEXPORT jobjectArray JNICALL Java_com_meituan_MTGuard_main (JNIEnv *env, jclass clazz, jint funcId, jobjectArray params) { switch(funcId) { case 3: // 签名生成 return generateMtgsig(env, params); // 其他case处理... } }在逆向过程中我发现Native层会先收集大量设备和环境信息包括设备硬件信息CPU架构、内存大小等系统属性ro.build.fingerprint等应用信息包名、签名等网络环境IP、代理检测等这些信息会被压缩和加密后作为签名的输入参数。2.2 动态密钥生成机制mtgsig最核心的安全机制在于它的动态密钥生成。通过分析我发现密钥由多个部分组成基础密钥组件应用包名的SHA1哈希值META-INF/SANKUAI.RSA文件的MD5值设备特定标识符如dfpid动态组件当前时间戳精确到分钟随机生成的16字节盐值环境检测结果的状态码这些组件会通过特定的组合算法生成最终使用的加密密钥。我记录了一个典型的密钥生成过程1. 计算包名哈希com.sankuai.meituan → sha1 → 69fe5963f3b95d9718c8d3e4f924ad9379500e9b 2. 读取RSA文件MD5638C81261479C2104EDE3F2518E91725 3. 组合静态部分69fe5963f3b95d9718c8d3e4f924ad9379500e9b|638C81261479C2104EDE3F2518E91725 4. 添加动态部分|1659326400|a3f5e2d1c8b7 → 最终密钥这种设计使得即使攻击者获取了某一时刻的密钥也无法预测下一次请求使用的密钥。3. 加密流程详解3.1 多层加密架构mtgsig采用了多层加密的设计主要包括三个阶段数据预处理阶段收集的原始数据会被序列化为JSON格式使用zlib进行压缩压缩级别通常为6添加4字节的CRC校验码核心加密阶段第一层RC4流加密使用动态生成的128位密钥第二层AES-CBC加密密钥由基础密钥动态盐值生成第三层自定义的字节混淆算法后处理阶段Base64编码添加版本标识头和校验尾我通过Hook测试发现一个典型的请求数据会经历如下变换过程原始JSON → 压缩(约60%体积) → RC4加密 → AES加密 → 混淆 → Base643.2 关键算法实现在逆向过程中我定位到了几个关键的加密函数RC4加密实现void rc4_crypt(unsigned char *key, int key_len, unsigned char *data, int data_len) { unsigned char S[256]; int i 0, j 0; // 初始化S盒 for(i 0; i 256; i) S[i] i; // KSA for(i 0; i 256; i) { j (j S[i] key[i % key_len]) % 256; swap_byte(S[i], S[j]); } // PRGA i j 0; for(int n 0; n data_len; n) { i (i 1) % 256; j (j S[i]) % 256; swap_byte(S[i], S[j]); data[n] ^ S[(S[i] S[j]) % 256]; } }AES密钥扩展 逆向显示美团使用了修改版的AES密钥扩展算法主要区别在于轮常数生成使用了自定义的伪随机算法每轮密钥都会与设备指纹的部分字节进行异或最后的4轮使用了不同的S盒这种修改大大增加了直接使用标准AES库破解的难度。4. 系统风险检测的影响系统风险检测结果是mtgsig签名的重要组成部分。美团主要检测以下几类风险4.1 设备环境检测Root检测 通过检查以下路径实现/system/bin/su /system/xbin/su /system/bin/.ext/su还会检测Magisk等常见root管理器的包名。Xposed检测 通过以下方式检测try { Class.forName(de.robv.android.xposed.XposedBridge); return true; // 检测到Xposed } catch(Exception e) {}- **模拟器检测** 检查以下特征 - 特定硬件参数如goldfish - 异常的系统属性 - 传感器数量异常 ### 4.2 检测结果编码 所有检测结果会被编码为一个状态字符串格式如下root_status|xposed_status|emulator_status|debug_status|proxy_status每个状态用数字表示例如 - 0未检测到 - 1检测到 - 2检测异常 这个状态字符串会经过SHA256哈希后作为签名的一部分。我观察到不同的风险等级会导致最终生成的签名有明显差异这说明风控系统会根据设备风险程度调整签名策略。 ## 5. 请求绑定与验证机制 ### 5.1 请求体绑定 mtgsig签名最关键的特性是与具体请求内容的强绑定。实现方式是对整个请求体包括URL、Header和Body进行哈希计算过程如下 1. 将请求URL按path部分分割为字符串数组 2. 将所有Header按key排序后拼接 3. 如果存在请求体计算其MD5值 4. 组合以上内容进行SHA256计算 得到的哈希值会与设备信息一起作为签名的输入。这意味着即使修改请求中的一个参数也会导致签名验证失败。 ### 5.2 服务端验证 服务端的验证流程大致分为三步 1. **基础校验** - 签名格式检查 - 时间戳有效期验证通常允许±5分钟 - 版本号兼容性检查 2. **密钥重构** 服务端会根据请求中的设备标识符重构加密密钥过程与客户端一致。 3. **签名对比** - 用重构的密钥解密签名 - 验证请求哈希值的匹配度 - 检查设备风险状态是否异常 我通过抓包分析发现当签名验证失败时服务端会返回412状态码并在响应头中包含详细的错误原因X-Mtguard-Code: 1003 X-Mtguard-Msg: invalid timestamp## 6. 对抗分析与优化建议 ### 6.1 常见对抗手段分析 在实际对抗过程中攻击者常尝试以下方法 1. **静态密钥提取** 直接逆向so文件查找硬编码密钥。美团通过动态密钥生成机制有效防御了这种方法。 2. **算法模拟** 尝试用高级语言重写签名算法。但由于存在大量Native层依赖和环境检测这种方法很难完全模拟。 3. **签名重放** 捕获合法签名重复使用。时间戳和随机盐机制使得签名有效期很短。 ### 6.2 优化建议 对于开发者而言要绕过这种风控系统几乎是不可能的。更合理的做法是 1. **保持设备环境干净** - 避免root或越狱 - 不使用改机工具 - 关闭开发者选项 2. **模拟真实用户行为** - 控制请求频率 - 使用真实的网络环境 - 避免自动化操作模式 3. **及时更新应用版本** 美团会定期更新风控策略旧版本APP更容易被识别。 在逆向过程中我发现美团的风控系统会学习用户的行为模式。那些行为像真实用户的设备即使在某些检测项上存在异常也能获得较高的信任度。这说明除了技术层面的防护行为分析也是风控的重要组成部分。