1.目标
网址:https://y.qq.com/n/ryqq/toplist/26
我们知道了 sign= P(n.data)
,其中n.data
是明文的请求参数
2.webpack生成data加密参数
那么 L(n.data)
就是密文的请求参数
。返回一个Promise {<pending>}
,所以L(n.data)
是一个异步函数。
L(n.data).then(result => {
console.log('结果是:', result);
}).catch(error => {
console.error('发生错误:', error);
});
window.shark(0).cgiEncrypt(data).then(result => {
console.log('结果是:', result);
}).catch(error => {
console.error('发生错误:', error);
});
运行之后报错
p[r[++h]] = p[r[++h]].call(p[r[++h]], p[r[++h]]);
TypeError: Cannot read properties of undefined (reading 'call')
因为请求参数是用了AES-GCM
加密,且使用了随机iv
AES-GCM
是一种高级加密标准(AES)与伽罗瓦 / 计数器模式(GCM)结合的加密方式
在浏览器环境中,window.crypto
对象是 Web Crypto API 的入口,用于实现加密相关的功能。window.crypto.subtle
是 SubtleCrypto
接口的实例,它提供了更底层、更强大的加密功能。
SubtleCrypto
的所有方法均返回 Promise
,需通过 then/catch
或 async/await
处理异步结果(例如密钥生成、加密解密等操作)。
所以我们只需要导入
window = globalThis;;
window.crypto = require('crypto');
就可以正常生成结果
3.逆向还原data加密参数
密钥就是vTBfEND/dLbvVNq4NbXhzw==
这样生成的结果长度为448,而网页长度是464,少了16位,而这16位正是iv
AES-GCM
通常需要将 IV 和密文一起传输,接收方才能正确解密
这样就没有问题
4.响应数据解密
返回的数据是二进制
数据解密是用j.__cgiDecrypt
,当然也是AES-GCM
解密
搜JSON.parse(N(
可快速定位
5.绕过加密验证
我们请求携带了"encoding": "ag-1"
,正是告诉QQ音乐服务器要采用某种加密方式
请求不要携带这个,同时data
参数保持明文传输
import requests
import json
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
"Accept": "application/octet-stream",
"accept-language": "zh-CN,zh;q=0.9",
"cache-control": "no-cache",
"content-type": "text/plain",
"origin": "https://y.qq.com",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://y.qq.com/",
"sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
}
url = "https://u6.y.qq.com/cgi-bin/musics.fcg"
params = {
"sign": "zzc308f0fefeuogbgecps0gpzosg6fnvxujyw0474ee6d"
}
data = {
"comm": {
"cv": 4747474,
"ct": 24,
"format": "json",
"inCharset": "utf-8",
"outCharset": "utf-8",
"notice": 0,
"platform": "yqq.json",
"needNewCode": 1,
"uin": 0,
"g_tk_new_20200303": 5381,
"g_tk": 5381
},
"req_1": {
"module": "musicToplist.ToplistInfoServer",
"method": "GetDetail",
"param": {
"topid": 27,
"offset": 0,
"num": 20,
"period": "2025-05-29"
}
}
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, params=params, data=data)
print(response)
print(response.text)
也就是说只需要分析sign
即可