记一次QQ音乐推荐歌单的sign值逆向过程

抓包

打开F12,点击QQ音乐的推荐歌单,可知https://u6.y.qq.com/cgi-bin/musics.fcg?_=1742693335800&sign=zzcecbb419p7k2j0ebwdn0bzilh7zdzjrqzo046bf9695为目标

image-20250323092940341

image-20250323093022964

有两个参数,_sign,由内容1742693335800推测_为当前13位时间戳不需要管,sign为需要逆向的内容,再看请求体为

1
{"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":{"param":{"caller":"0","category_id":3248,"size":20,"page":0,"use_page":1},"method":"get_category_content","module":"music.playlist.PlayListCategory"},"req_2":{"method":"get_category_basic","module":"playlist.PlayListCategoryServer","param":{"caller":"0","category_id":3248}}}: 

并没有什么加密参数,忽略

逆向sign

搜索关键词sign:cgi-bin/musicu.fcg推测这部分就是需要断点的内容,有两个带有cgi-bin/musicu.fcg的,都下一个断点

image-20250323093509541

下好断点,点击其他歌单,发现断在了第一个断点处,分析参数可知i的值即为sign

image-20250323093720161

1
i = "GET" === t.type.toUpperCase() ? o(t.data.data) : o(t.data)

由抓包得知该请求是POST请求,所以i的值是o(t.data)计算出来的,验证

image-20250323094014941

打印一下t.data的值为

1
{"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":{"param":{"caller":"0","category_id":3248,"size":20,"page":0,"use_page":1},"method":"get_category_content","module":"music.playlist.PlayListCategory"},"req_2":{"method":"get_category_basic","module":"playlist.PlayListCategoryServer","param":{"caller":"0","category_id":3248}}}

与上方请求体一致,接下来进入o函数内部,可以看到是webpack打包的

image-20250323094208545

image-20250323102423597

接下来需要寻找加载器了,在n(80)打上断点,进入到n,加载器就是

1
2
3
4
5
6
7
8
9
10
11
12
function d(t) {
if (a[t])
return a[t].exports;
var r = a[t] = {
i: t,
l: !1,
exports: {}
};
return e[t].call(r.exports, r, r.exports, d),
r.l = !0,
r.exports
}

将加载器所在的js文件全部copy出来,先直接运行看看

image-20250323102909068

缺少window,那就直接在文件顶部赋值一个windowml用来导出函数

1
2
const window = global
let ml

image-20250323103757091

在最后将加载器d赋值ml导出

image-20250323103121162

再次运行,没有报错,那就将o所在的函数全部抠出来

在加载器部分和文件最后打印,查看缺少哪些模块

1
console.log(t) //在加载器return之前
1
console.log(ml(0)) //在文件最后

image-20250323104433601

可知缺少80模块,接下来寻找80模块

image-20250323104639181

可知80模块为

1
2
3
4
5
6
7
8
9
10
11
12
function(e, t) {
var n;
n = function() {
return this
}();
try {
n = n || new Function("return this")()
} catch (r) {
"object" === typeof window && (n = window)
}
e.exports = n
}

image-20250323104751741

复制到代码,将原本的n(80)改成n(1)再次运行可发现不缺任何模块了

image-20250323104851149

t.data传入看看结果

1
2
let 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":"music.musicsearch.HotkeyService","method":"GetHotkeyForQQMusicMobile","param":{"searchid":"25206183845204131","remoteplace":"txt.yqq.top","from":"yqqweb"}},"req_2":{"module":"yqq.WhiteListServer","method":"Pass","param":{}},"req_3":{"module":"music.paycenterapi.LoginStateVerificationApi","method":"GetChargeAccount","param":{"appid":"mlive"}}}'
console.log(ml(0).default(data))

image-20250323105113538

image-20250323105133059

对比发现结果并不一致,这就需要补环境了(

用蜜汁小脚本吐一下环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
window = global
navigator = {}
location= {}
document = {}
function getEnv(proxy_array) {
for(let i=0; i<proxy_array.length; i++){
handler = `{
get: function(target, property, receiver) {
console.log('方法:get',' 对象:${proxy_array[i]}',' 属性:',property,' 属性类型:',typeof property,' 属性值类型:',typeof target[property]);
return target[property];
},
set: function(target, property, value, receiver){
console.log('方法:set',' 对象:${proxy_array[i]}',' 属性:',property,' 属性类型:',typeof property,' 属性值类型:',typeof target[property]);
return Reflect.set(...arguments);
}
}`;
eval(`
try{
${proxy_array[i]};
${proxy_array[i]} = new Proxy(${proxy_array[i]},${handler});
}catch(e){
${proxy_array[i]}={};
${proxy_array[i]} = new Proxy(${proxy_array[i]},${handler});
}
`);
}
}
proxy_array = ['window','document','location', 'navigator', 'history', 'screen', 'history']
getEnv(proxy_array);

这里补药用ES模块!!!

image-20250323112912881

将缺失的环境补上

1
2
3
4
5
6
location = {
"host": "y.qq.com"
}
navigator = {
appName: "Netscape"
}

image-20250323113131498

比较数据与接口返回一致,完事