百趣云 百趣云的博客

QQ 音乐 sign 参数逆向:从混淆 JS 里抠算法

sign 参数:看着像 md5,确实是 md5(的变种)

QQ 音乐 web 端 u.y.qq.com 的 CGI 接口带一个 sign 参数,32 位 hex。和别家把签名算法藏在 VMP 里不同,QQ 音乐的签名就是魔改 md5 + 字符置换,混淆程度中等,是练"从 webpack chunk 里抠函数"的好目标。

定位:搜对关键词事半功倍

QQ 音乐前端是 webpack 打包,直接搜 sign 会被淹没。正确的搜索词是功能命名:

"securitySign"    ← 直接命中签名函数
"getSign"         ← 部分版本

命中后能看到签名函数在一个独立 chunk 里,入参是请求体对象,返回值就是 sign。

算法拆解

签名逻辑拆开三步:

  1. 拼接:固定前缀 salt + 请求体的 JSON 字符串。salt 形如 "CJBPACrRuNy7"(这是历史上公开过的版本,当前版本以你抓的为准——2024 年后改成了动态下发)
  2. md5:对拼接串算 md5,转大写 hex
  3. 字符置换:对 hex 串做奇偶位互换类的前置变换后输出
// 抠出来的最小可运行单元(结构示意)
function getSecuritySign(reqData) {
    var salt = window.__SIGN_SALT__ || "CJBPACrRuNy7";  // 动态 salt 从页面配置取
    var raw = salt + JSON.stringify(reqData);
    var hash = md5(raw).toUpperCase();
    // 置换:相邻字符互换
    var out = '';
    for (var i = 0; i < hash.length; i += 2) {
        out += (hash[i+1] || '') + hash[i];
    }
    return out;
}

动态 salt:版本演进的关键

早期版本 salt 硬编码在 JS 里,被公开后腾讯改成了页面配置下发:salt 藏在首页 HTML 或某个配置接口的返回里。工程上的应对:

import re, requests

def get_sign_salt(session):
    html = session.get('https://y.qq.com/').text
    # salt 在页面内嵌配置里,正则特征以实际页面为准
    m = re.search(r'"signSalt"\s*:\s*"([A-Za-z0-9]+)"', html)
    return m.group(1) if m else None

把 salt 做成启动时提取 + 定时刷新,不要写死。写死的下场是腾讯换 salt 那天你的服务集体 401。

接口体系的另一半:comm 公共参数

sign 之外,QQ 音乐接口还有一套 comm 公共参数体系:

{
  "comm": {
    "uin": "QQ号",
    "guid": "10位随机数",
    "qqmusic_key": "登录态key",
    "ct": 24, "cv": 80800, "format": "json"
  },
  "req_0": { "module": "...", "method": "...", "param": {...} }
}
  • guid:设备标识,10 位数字,协议模拟时固定一个
  • qqmusic_key:登录态令牌,扫码登录后下发
  • 业务参数包在 req_0(可多个,req_1、req_2...)里,sign 签的是整个请求体,包括 comm——所以改 comm 也要重签

精髓:三个坑

  1. JSON.stringify 的 key 顺序。sign 签的是 JSON.stringify(reqData) 的结果,JS 的对象 key 顺序是插入序,Python 复现时 dict 构造顺序必须和浏览器一致。对不上时先把两边的待签字符串打印出来 diff。
  2. vkey(歌曲播放地址)是独立体系。获取播放 URL 的 musicu.fcg 接口除了 sign 还要 qqmusic_key,且返回的 vkey 有有效期(约 2 小时),别缓存播放地址。
  3. 签名错误返回 param error 而非明确提示。调试基准:先用"搜索"这种公开接口验证签名正确性,通了再接登录态接口。

总结

  1. sign = 置换(md5(salt + body JSON)),算法本身半小时能搞定
  2. salt 已动态化,启动时从页面提取,别写死
  3. comm 公共参数参与签名,guid 固定、key 顺序保序
  4. 抠代码优先于手搓:整个 chunk 连带 md5 依赖一起抠,对拍后上线
By 百趣云 阅读量:1 On