记一次某云商的 sign 算法逆向
前段时间闲着没事,朋友给我推了一个活动,大概是关于毛子的。刚开始我是拒绝的,后来发现连实名认证都得抢,于是打算用电脑试试。
结果一打开才发现它只支持手机端访问。既然这样,那就顺手抓个包看看。
抓包分析
先看抓到的请求:

从请求里能看到一个比较关键的字段:sign。看格式和长度,八九不离十是 MD5。
除了 sign 以外,请求头里还有两个字段:
1 | noncestr |
timestamp 不用多说,就是时间戳;noncestr 盲猜是随机字符串。这样一来,核心问题就变成了:只要知道它的签名规则,就能模拟这个请求。
小程序解包
小程序不像网页,不能直接打开 F12 看源码,所以需要用到 wxappUnpacker 这类小程序分包工具。
小程序包需要先从手机上提取出来,再进行解包分析。
源码分析
用 VS Code 打开解包后的源码,直接搜索关键词 noncestr,很快就能定位到加密逻辑附近。

接着继续找 PLMKEY:

noncestr 也是有生成规则的,这里当时踩了一个坑:

简单还原一下字符串拼接和签名逻辑,大概是这样:
1 | function getSign(api, body, token, st, nonce) { |
整体规则不复杂:
- 取
body里的参数名,并按字母顺序排序 - 普通值按
key=value&拼接 - 对象值先
JSON.stringify,再把字符串拆开排序后拼接 - 追加
url - 如果有
accessToken,继续追加 - 追加
timestamp、nonceStr - 最后追加固定 key
- 对最终字符串做 MD5
签名校验踩坑
签名校验时我一直对不上,后来才发现字符串里带了中文。
这类情况不同语言处理起来会有细节差异,尤其是编码、序列化和字符串排序。还原签名算法时,不能只看拼接顺序,也要注意语言之间对中文和 JSON 的处理差异。
开始编码
签名规则确认以后,就可以开始写请求了。必要的请求头补齐即可:
1 | Headers = { |
这里还有一个很坑的地方:请求地址后面千万不要多加斜杠 /。
当时为了这个斜杠耗了好几个小时。签名算法里把 url 也算进去了,所以:
1 | /api/example |
和:
1 | /api/example/ |
算出来的签名完全不是一回事。
小结
这次逆向本质上就是三件事:
1 | 抓包确认关键字段 |
真正容易浪费时间的地方反而不是 MD5 本身,而是那些细节:noncestr 的生成、中文字符串处理、对象参数排序,以及 URL 末尾有没有多一个 /。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 墨鱼札记!
评论



