1. 打开任意一首歌曲页面,很明显看到歌曲id=553310243
。
2. Network查看异步请求XHR,找到评论接口
由于网易云使用了了iframe,如果network勾选了GroupByFrame选项,则注意接口可能在iframe下。
3. 分析一下接口参数
可以看到参数如下:
Query String Param:
csrf_token=
Form Data:
params=tCXVWGXv7AbTPYxwM5Nx9kkPXT0C4NufRsLk7yWolAcvJEi3fNk1b2jM%2Bi0jq6vckt7Fi7hkhyWkTVHmfBQGCVhfPlFwKSjPOf7DHJJ5S01NvEA637ddn6CTuUuHYxvQ0029Ju8ddYUvO9NfMpa%2BV%2BxYHDUq1ECHTuIJwIZov7rilCdP4sFgIBLQarbWFPwZ
&
encSecKey=7ec9d5faf5a1143895d392cb921abcfd8fc5e4b31a9dc8b031550ec8955eddba4cc20e952f6c4c1db846bb996c5961bbb1dfb3f1049fd6a797d2802580433fa01c159d3fa630ad2d8461ddabfbc4c534bb001cf4ba9ce8a617e181d07d7fe861bbe462e516fd7b7b56e09bc0293fed9f19679a86d911d12ac6997966f0f23fde
csrf_token
为空即可,主要是分析两个表单参数params
和encSecKey
从何而来。
4. 分析
ctrl+左键
进入js,点击美化js
方便阅读。
分析一下前后代码,都是闭包,在方法头下断,看一下有没有需要的参数。
看到闭包返回的函数的形参就包含了需要的参数。那么接下来分析哪里调用了闭包返回的函数。
接下来分析d0x
这个参数从何而来。
看到需要的参数是形参获得,继续查看调用栈谁传的参数。
参数又是形参获得,继续向上分析调用栈。
var bBB5G = window.asrsea(JSON.stringify(j0x), bor2x(["流泪", "强"]), bor2x(Ow3x.md), bor2x(["爱心", "女孩", "惊恐", "大笑"]));
e0x.data = k0x.cD1x({
params: bBB5G.encText,
encSecKey: bBB5G.encSecKey
})
四个参数。
参数:JSON.stringify(j0x)
"{"rid":"R_SO_4_551816010","offset":"0","total":"true","limit":"20","csrf_token":""}"
参数:bor2x(["流泪", "强"])
"010001"
参数:bor2x(Ow3x.md)
"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
参数:bor2x(["爱心", "女孩", "惊恐", "大笑"])
"0CoJUm6Qyw8W8jud"
四个参数值如上所示,凭借经验(第一个参数中,csrf_token一直为空,rid为R_SO_4_歌曲id,其余字段固定即可;Ow3x.md是一个数组,内容类似emoji;其余参数是常量;当然不绝对是这样,也许js是后台动态生成填充了常量)分析后3个参数为固定参数。
bBB5G
对象中包含了encText
和encSecKey
。接下来分析window.asrsea(a0,a1,a2,a3)
如何生成的bBB5G
对象。
有开发经验的大概能猜到这个
window.asrsea
函数是对参数进行加密操作,每次请求前加密参数,因此每次ajax请求都会调用这个加密方法,也就是说如果能逆向出加密方法,即可抓取该网页所有请求数据。
可以确定,a(16)
代表产生一个16位的随机数(其实看a()
函数逻辑也可以发现就是产生随机长度的字母数字组合字符串)
i
由a
生成,则i
可以为固定值,将其固定为0000000000000000
。
encSecKey
由i
/e
/f
加密获得,因此可以得到(由于i
被固定为0000000000000000
)固定值babc57ca9e9ffb0a879ae290ac6cba6f60620aa9ae3b36a84585e23bbc73d73b13a2ebab4aa2ee80544d255727adc5a04db613d77d02a62a52b3a03134d16f191d54675f560f797c7f03e3a30c43df8b1b49878fd225b62f5f78041427debc3e95b93582f130618630702621da4eda9c71af91836cc39ab3b760b033643a1889
。
e0x.data = k0x.cD1x({
params: bBB5G.encText,
encSecKey: bBB5G.encSecKey
})
此时,已经得到了encSecKey
,再看看params
是怎么得到的。
b
函数内部很直观,使用了谷歌的CryptoJS
库,使用方法可以参考CryptoJS 加密的使用方法。
b
函数先对"{"rid":"R_SO_4_551816010","offset":"0","total":"true","limit":"20","csrf_token":""}"
与固定参数"0CoJUm6Qyw8W8jud"
进行一次AES对称加密;再对第一次的加密结果与i
即0000000000000000
进行一次AES对称加密,返回结果encText
。
事实证明思路没错。
5. 总结:
请求的URL:http://music.163.com/weapi/v1/resource/comments/R_SO_4_551816010?csrf_token=
表单参数:
// 加密伪函数
function aesEnc(rawData,key,iv){
var key = CryptoJS.enc.Utf8.parse(key)
, iv = CryptoJS.enc.Utf8.parse(iv)
, rawData = CryptoJS.enc.Utf8.parse(rawData)
, encData = CryptoJS.AES.encrypt(rawData, key , {
iv: iv,
mode: CryptoJS.mode.CBC
});
return encData .toString()
}
params: aesEnc(aesEnc('{"rid":"R_SO_4_歌曲ID","offset":"0","total":"true","limit":"20","csrf_token":""}','0CoJUm6Qyw8W8jud','0102030405060708'),'0000000000000000','0102030405060708')
encSecKey: babc57ca9e9ffb0a879ae290ac6cba6f60620aa9ae3b36a84585e23bbc73d73b13a2ebab4aa2ee80544d255727adc5a04db613d77d02a62a52b3a03134d16f191d54675f560f797c7f03e3a30c43df8b1b49878fd225b62f5f78041427debc3e95b93582f130618630702621da4eda9c71af91836cc39ab3b760b033643a1889