mtgsig 1.2 完整逆向拆解(美团小程序 H5guard 加密)
一、先明确核心结论
- 你现在用的 9521 端口服务 = 官方封装好的黑盒 RPC 加密服务,本质是内置了完整逆向还原后的 H5guard 1.2 算法,输入请求参数直接输出可用 mtgsig,不用自己写加密逻辑;
- 纯逆向(脱离 RPC 服务,本地复现算法)分两大路径:JS 端原生逆向、纯数学公式还原;
- mtgsig1.2 不是单纯请求签名,是设备指纹 + 请求报文 + 时间戳混合加密的风控校验串,包含多层 AES、MD5、位运算、HKDF 密钥派生。
二、mtgsig 1.2 各字段含义(逆向基础)
json
"mtgsig": {"a1":"1.2","a2":时间戳,"a3":"WEBDFPID","a5":"AES加密环境串","a6":"设备指纹密文","a8":"MD5哈希","a9":"SDK版本","a10":"固定随机因子","x0":4,"d1":"全局请求校验哈希"}表格
| 字段 | 生成逻辑(逆向关键点) |
|---|---|
| a1 | 固定字符串1.2,版本标识 |
| a2 | 毫秒级 Unix 时间戳Date.now(),防重放 |
| a3 | 入参传入的 WEBDFPID(设备指纹 Cookie) |
| x0 | 固定常量4,算法版本标记 |
| a6 | 采集小程序环境(wx api、屏幕、系统)→ AES-CBC 加密 → Base64 输出,密钥动态派生自 a2+a3 |
| a8 | 多段环境数组异或运算后转 16 进制 MD5 摘要 |
| a5 | 请求报文(url+method+body)拼接盐值,HKDF-SHA256 派生密钥后 AES 加密 Base64 |
| d1 | 全量 mtgsig 字段有序拼接后的 SHA256 哈希最终校验值 |
| a9/a10 | SDK 固定版本、固定混淆因子,可静态写死 |
三、完整逆向分步流程(两种方案)
方案 1:抓小程序 JS 原生逆向(最标准,还原完整方程式)
步骤 1:定位加密入口(H5guard.js)
- 微信小程序调试,抓包加载的
h5guard.min.js,格式化混淆代码; - Hook
JSON.stringify、XMLHttpRequest,断点拦截生成 mtgsig 的函数(特征:同时存在 a1、a2、a3 变量); - 找到顶层自执行加密函数
gU(),这是 mtgsig 生成总入口。
步骤 2:分层拆解加密方程式(核心数学逻辑)
- 密钥派生方程(HKDF)
MasterKey=SHA256("mtgsig1.2"+a2+a3)
SessionKey=HKDF−SHA256(MasterKey,salt="wxmtg") 所有 AES 加密统一使用该会话密钥,CBC 模式,固定 IV 向量。
- a6 环境加密方程式
plaintext
env_raw = JSON.stringify(小程序wx环境信息数组) a6 = Base64(AES_CBC_Encrypt(env_raw, SessionKey, iv))- a5 请求报文加密方程式
plaintext
src = method + "|" + url + "|" + data + "|" + a2 a5 = Base64(AES_CBC_Encrypt(src, SessionKey, iv))- a8 异或哈希方程三段内置固定大数字数组循环逐位异或,合并后转 16 进制小写 MD5:
a8=MD5(arr1⊕arr2⊕arr3).toString(16) 5.d1 最终校验哈希(顶层签名方程)将所有 mtgsig 字段按a1,a2,a3,a5,a6,a8,a9,a10,x0字典序拼接字符串:
d1=SHA256(concatAllFields).toString(16)
步骤 3:去混淆、扣完整可运行 JS 代码
- 使用 Babel/AST 工具解密十六进制混淆变量(
_0xXXxx类数组字符串); - 剥离小程序 wx 环境检测代码,补全缺失全局变量(模拟 wx、window、localStorage);
- 提取纯加密数学函数(AES、MD5、位运算、HKDF),剥离环境检测垃圾代码。
方案 2:脱离 JS,纯 Python 数学逆向复现(不用补小程序环境)
- 固定常量提取:x0=4、a9 写死
3.1.0,7,61、a10 固定c5; - 复现 HKDF-SHA256 密钥派生函数;
- 复现 AES-CBC 加解密(固定 IV);
- 复现三段数组异或逻辑生成 a8;
- 拼接全字段计算 SHA256 得到 d1;
- 入参仅需:method、url、data、dfpId (a3)、当前时间戳。
四、你当前 RPC 服务(9521 端口)本质说明
你调用的/api/get_16_mtgsig是别人已经完成完整逆向封装的成品服务:
- 内部已经实现了上面全部加密方程式、HKDF/AES/ 位运算逻辑;
- 屏蔽了复杂的小程序环境采集,外部只需要传入 4 个业务参数;
- 内部自动生成 a2 时间戳、计算 a5/a6/a8/d1 全套字段,直接返回组装好的 mtgsig 对象;
- 健康检查
/health仅用于探测服务存活,无加密逻辑。
五、逆向难点 & 避坑点
- 动态密钥坑:AES 密钥由 a2 时间戳 + dfpId 实时派生,固定密钥无法长期生成有效签名,每一次请求密钥都不同;
- 小程序环境校验坑:原生 JS 会校验 wx 全局对象,纯 Python 复现时可以直接写死模拟环境字符串,不影响签名校验;
- 字段排序坑:计算 d1 哈希时必须严格固定字段顺序,乱序会导致 d1 校验失败,服务端直接 403 拦截;
- 时间戳防重放:a2 时间戳和服务器时差不能超过 30 秒,否则 mtgsig 直接失效;
- 混淆代码陷阱:H5guard 每次小程序版本更新会刷新变量名混淆,但是底层加密数学方程式完全不变,只需要重新定位入口函数。
六、两种使用选择
- 省事方案(你现在在用):继续调用 9521 端口 RPC 接口,不用自己逆向数学公式,业务层仅处理请求转发;
- 完全自主逆向(脱离第三方服务):按上面 JS 逆向流程扣出加密函数,用 Python 重写全套 HKDF、AES、位运算方程,本地独立生成 mtgsig。