mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
918 字
2 分钟
扣扣音乐双参数纯算
2026-06-15

某音乐接口加密算法逆向分析#

一、目标概述#

本文通过对扣扣音乐Web端请求加密机制的深入分析,揭示了其核心加密算法的实现原理。分析对象为该页面发送的API请求。

二、请求结构分析#

2.1 请求参数#

通过Chrome DevTools抓包分析,发现QQ音乐API请求包含以下关键参数:

参数名说明示例值
_时间戳(毫秒)1781411109906
encoding编码方式ag-1(表示加密)
sign请求签名zzc23c81b08ln0xcliusuovjw58qfbye8cy19660417

2.2 请求体格式#

请求体为Base64编码的加密数据,服务器响应同样为加密的二进制数据。

三、Sign签名算法分析#

3.1 算法流程#

根据插桩日志分析,签名生成流程如下:

  1. SHA-1哈希:对JSON请求数据进行SHA-1哈希,结果转换为大写十六进制字符串
  2. 索引提取:从哈希结果中按固定索引提取字符
  3. XOR混淆:将哈希字节与固定密钥进行异或操作
  4. Base64编码:对异或结果进行Base64编码
  5. 格式拼接:组合前缀、索引字符串和Base64结果

d

e

f

g

整体流程

h

3.2 核心代码实现#

function get_sign(data) {
// 1. SHA-1 哈希(大写)
data = JSON.stringify(data);
const hash = crypto.createHash('sha1').update(data).digest('hex').toUpperCase();
// 2. 固定索引提取
const idx1 = [23, 14, 6, 36, 16, 40, 7, 19];
const idx2 = [16, 1, 32, 12, 19, 27, 8, 5];
const str1 = idx1.map(i => hash[i]).join('');
const str2 = idx2.map(i => hash[i]).join('');
// 3. 固定密钥(20字节,对应SHA-1输出长度)
const key = [89, 39, 179, 150, 218, 82, 58, 252, 177, 52, 186, 123, 120, 64, 242, 133, 143, 161, 121, 179];
// 4. 哈希字符串转字节数组
const hashBytes = [];
for (let i = 0; i < hash.length; i += 2) {
hashBytes.push(parseInt(hash.substring(i, i + 2), 16));
}
// 5. XOR操作
const xorResult = hashBytes.map((byte, i) => byte ^ key[i]);
// 6. Base64编码并移除末尾等号
const xorBuffer = Buffer.from(xorResult);
const base64Sign = xorBuffer.toString('base64').replace(/=+$/, '');
// 7. 组合结果(前缀zzc + str1 + base64 + str2)
return ("zzc" + str1 + base64Sign + str2).toLowerCase();
}

3.3 索引数组来源#

插桩日志显示,索引数组 [23, 14, 6, 36, 16, 40, 7, 19] 来自域名检测逻辑:

[53535] [obj[key] = value类型] obj: [null,null,null,null,null,null,null,null] key:[0] value: 23
[53537] [obj[key] = value类型] obj: [23,null,null,null,null,null,null,null] key:[1] value: 14
[53538] [obj[key] = value类型] obj: [23,14,null,null,null,null,null,null] key:[2] value: 6
...

该数组通过检测当前页面域名 y.qq.com 是否在白名单 ['qq.com', 'joox.com', 'tencentmusic.com', ...] 中生成,是一种简单的环境指纹机制。

四、请求体加密算法(AES-GCM)#

日志分析

a

b

c

4.1 算法识别#

从表单插桩日志可以清晰看到加密流程:

// 插桩日志关键信息提取
// [11] new Uint8Array([189, 48, 95, 16, 208, 255, 116, 182, 239, 84, 218, 184, 53, 181, 225, 207]) - 密钥
// [12] new Uint8Array(12) - IV容器
// [13] crypto.getRandomValues(iv) - 生成随机IV
// [14] importKey() - 导入密钥
// [19] encrypt() - AES-GCM加密

4.2 加密流程#

┌─────────────────────────────────────────────────────────────┐
│ AES-GCM 加密流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. JSON序列化 → 将请求对象转为JSON字符串 │
│ 2. UTF-8编码 → TextEncoder.encode() │
│ 3. 生成随机IV → crypto.getRandomValues(12字节) │
│ 4. AES-GCM加密 → SubtleCrypto.encrypt() │
│ 5. 数据拼接 → IV(12) + 密文 + AuthTag(16) │
│ 6. Base64编码 → btoa() 或 Buffer.toString('base64') │
└─────────────────────────────────────────────────────────────┘

4.3 核心代码实现#

function encryptSync(data) {
// 固定密钥(16字节 = 128位)
const key = Buffer.from([189, 48, 95, 16, 208, 255, 116, 182, 239, 84, 218, 184, 53, 181, 225, 207]);
// 随机IV(12字节,GCM推荐长度)
const iv = crypto.randomBytes(12);
// 创建AES-GCM加密器
const cipher = crypto.createCipheriv('aes-128-gcm', key, iv);
// 支持对象或字符串输入
const jsonStr = typeof data === 'string' ? data : JSON.stringify(data);
// 执行加密
const encrypted = Buffer.concat([
cipher.update(jsonStr, 'utf8'),
cipher.final()
]);
// 获取认证标签(16字节)
const authTag = cipher.getAuthTag();
// 拼接格式:IV + 密文 + AuthTag
const cipherWithTag = Buffer.concat([iv, encrypted, authTag]);
// Base64编码输出
return cipherWithTag.toString('base64');
}

4.4 数据格式说明#

数据段长度说明
IV12字节初始化向量,每次加密随机生成
密文可变AES-GCM加密结果
AuthTag16字节认证标签,用于完整性校验

五、响应解密算法#

5.1 解密入口定位#

vendor.chunk.js 中定位到解密函数:

var ae = oe.__cgiEncrypt, // 加密函数
se = oe.__cgiDecrypt; // 解密函数
(delete oe.__cgiEncrypt, delete oe.__cgiDecrypt); // 执行后立即删除

5.2 解密流程#

响应解密使用与加密相同的密钥和算法:

function decryptResponse(encryptedData) {
const key = Buffer.from([189, 48, 95, 16, 208, 255, 116, 182, 239, 84, 218, 184, 53, 181, 225, 207]);
// Base64解码(如果是字符串)
if (typeof encryptedData === 'string') {
encryptedData = Buffer.from(encryptedData, 'base64');
}
// 解析数据结构
const iv = encryptedData.slice(0, 12); // 前12字节:IV
const authTag = encryptedData.slice(-16); // 后16字节:AuthTag
const ciphertext = encryptedData.slice(12, -16); // 中间:密文
// 创建解密器并解密
const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(authTag);
const plaintext = Buffer.concat([
decipher.update(ciphertext),
decipher.final()
]);
return plaintext.toString('utf8');
}

六、完整请求流程#

┌─────────────────────────────────────────────────────────────────────────┐
│ QQ音乐API请求完整流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 构造请求数据 │───▶│ 生成Sign签名 │───▶│ AES-GCM加密 │ │
│ │ (JSON) │ │ (SHA-1+XOR) │ │ (固定密钥) │ │
│ └──────────────┘ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 发送HTTP请求│◀───│ Base64编码 │◀───│ IV+密文+Tag │ │
│ └──────┬───────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 接收响应 │───▶│ AES-GCM解密 │───▶│ JSON解析 │ │
│ │ (加密数据) │ │ (固定密钥) │ │ (业务数据) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

七、关键发现总结#

7.1 加密参数汇总#

参数说明
加密算法AES-128-GCM认证加密模式
密钥[189, 48, 95, 16, 208, 255, 116, 182, 239, 84, 218, 184, 53, 181, 225, 207]固定128位密钥
IV长度12字节GCM推荐长度
AuthTag长度16字节完整性校验
签名算法SHA-1 + XOR + Base64多层混淆

7.2 安全评估#

优点:

  • 使用标准AES-GCM算法,提供机密性和完整性保障
  • IV随机生成,避免相同明文产生相同密文

缺陷:

  • 密钥硬编码在前端代码中,可通过逆向分析获取
  • Sign签名密钥同样固定,缺乏动态性
  • 未使用HTTPS之外的额外通道加密

7.3 绕过策略#

由于密钥和算法均已完全还原,可直接在Node.js环境中模拟完整请求流程:

import requests
import execjs
# 使用Node.js执行加密
with open('sign.js', 'r', encoding='utf-8') as f:
sig = execjs.compile(f.read())
data = {"comm": {...}, "req_1": {...}}
sign = sig.call("get_sign", data)
encrypted_data = sig.call("encryptSync", data)
# 发送请求
response = requests.post(url, params={"sign": sign, "encoding": "ag-1"}, data=encrypted_data)
# 解密响应
decrypted = sig.call("decryptSync", response.content)

八、总结#

本文通过插桩日志分析,完整还原了QQ音乐Web端的API加密机制:

  1. Sign签名:采用SHA-1哈希+固定索引提取+XOR混淆+Base64编码的组合方式
  2. 请求体加密:使用固定密钥的AES-GCM算法,随机IV保证安全性
  3. 响应解密:使用相同密钥逆向解密

该加密方案在前端实现,存在密钥暴露风险,但通过混淆手段增加了逆向难度。对于爬虫开发者而言,只需提取固定密钥即可完整模拟请求。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

扣扣音乐双参数纯算
https://fatdog.20060113.xyz/posts/qqmusic/
作者
神秘大胖狗
发布于
2026-06-15
许可协议
MIT

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00