百趣云 - 美团
type: channel
link: https://teawhites.cn/index.php/tag/%E7%BE%8E%E5%9B%A2/
items: 美团 mtgsig 参数逆向分析

美团 mtgsig 参数逆向分析
type: item
date: 2026-08-28T09:05:44+08:00
description: mtgsig：把签名元信息摆在明面上的设计美团 H5 接口的请求头里有一个 mtgsig，值是一串编码后的 json。解码后字段含义几乎写在脸上：{ "a1": "1.1", // 算法版本 "a2": 1787968800000, // 毫秒时间戳 "a3": "f3d4...", // 随机数/nonce "a4": "dfp值", // 设备指纹 "a5": "GET", // 请求方法 "a6": "/api/xxx", // 请求路径 "a7": "query摘要", "a8": "body摘要", "a9": "最终签名" }字段编号是语义化的：a5、a6 告诉你签名覆盖了方法和路径，a7、a8 告诉你 query 和 body 都参与了摘要。这种"元信息透明"的设计让服务端校验策略可以灵活升级，也给逆向者画了张地图。定位：签名是异步的美团 H5 的签名 SDK 是独立加载的 mtgsig 相关 chunk。定位时注意一个特点：签名函数返回 Promise——因为它要先异步采集设备指纹（a4 字段的 dfp 值），再算签名。所以 hook 点要选在异步完成之后：const _open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { this._method = method; this._url = url; return _open.apply(this, arguments); }; const _setH = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.setRequestHeader = function(k, v) { if (k.toLowerCase() === 'mtgsig') { console.log('mtgsig for', this._method, this._url); debugger; // 断下后看调用栈，Promise then 链的上一环就是签名函数 } return _setH.apply(this, arguments); };算法拆解跟进签名主函数后，流程很标准：采集 dfp：设备指纹（ua、canvas、屏幕、字体等），独立模块异步生成，结果有缓存拼接待签串：method + path + 排序后的 query + body 的 SHA256 + 时间戳 + nonce + dfp，按固定模板拼接HMAC-SHA256：密钥来自混淆字符串池，按接口分组多组 key组装输出：签名连同 a1-a8 元信息一起编码成 mtgsig 头密钥的抠取技巧：美团前端把密钥做了字符串加密存储，运行时通过解密函数还原。别去静态分析解密算法——直接在密钥使用点下断点（HMAC 的 key 参数），内存里拿到的就是明文，抄下来即可。工程实现import hashlib, hmac, json, time, random, string def make_mtgsig(method, path, query, body, dfp, key): ts = int(time.time() * 1000) nonce = ''.join(random.choices(string.hexdigits.lower(), k=16)) body_hash = hashlib.sha256(body.encode()).hexdigest() if body else '' # 待签串模板以实际版本为准，结构示意： raw = "&".join([method.upper(), path, query, body_hash, str(ts), nonce, dfp]) sign = hmac.new(key.encode(), raw.encode(), hashlib.sha256).hexdigest() return {"a1": "1.1", "a2": ts, "a3": nonce, "a4": dfp, "a5": method.upper(), "a6": path, "a7": query, "a8": body_hash, "a9": sign}精髓：四个实战要点dfp 是独立 SDK，可以固定。dfp 采集一次后有缓存机制，协议模拟时从真实浏览器采一个固定使用。但注意 dfp 和 ua 的匹配关系，换 ua 必须换 dfp。时间戳容忍窗口约 5 分钟。a2 和服务器时钟偏差超窗直接返回 mtgsig expired。走代理链路时注意延迟，必要时本机校时。App …