以《平凡之路》为例
我们通过分析Network变化发现了这样一个数据接口https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=
(后期事实证明它是通用的接口)
它的响应结果里有一个url,打开这个url,发现居然正好是我们想要的音频,于是我们非常高兴,开始尝试访问这个url
我们将他这个url复制进浏览器却发现没有任何返回
于是我们开始分析他的请求标头
发现这是一个POST请求而且携带有params和encSecKey这两个数据
我们瞬间明白,请求不成功是因为缺少了这两个关键参数
所以我们的核心目标就是找出这两个参数是如何获取的
需要安装requests和execjs这两个模块
通过使用全局搜索方法搜索encSecKey,我们发现它以下三个js文件有出现,一个个点进去查看代码
发现在第一个js文件中有如下代码,params和encSecKey同时出现,我们就猜测这一段代码可能是生成这两个参数的代码。
var bUE3x = window.asrsea(JSON.stringify(i0x), bsl6f(["流泪", "强"]), bsl6f(WT9K.md), bsl6f(["爱心", "女孩", "惊恐", "大笑"]));
e0x.data = j0x.cr1x({
params: bUE3x.encText,
encSecKey: bUE3x.encSecKey
})
我们点击左侧设置断点,点击页面中的播放开始调试,发现程序果然在这里停下,而且显示的参数看起来就像是我们需要的参数(实际上就是)
于是我们开始分析这段代码
这两个参数是由bUE3x得到,而bUE3x 是由 window.asrsea 得到,我在代码中搜索window.asrsea发现仅在两处出现,另一处如下
!function() {
function a(a) {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1)
e = Math.random() * b.length,
e = Math.floor(e),
c += b.charAt(e);
return c
}
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b)
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}
function c(a, b, c) {
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b,"",c),
e = encryptedString(d, a)
}
function d(d, e, f, g) {
var h = {}
, i = a(16);
return h.encText = b(d, g),
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f),
h
}
function e(a, b, d, e) {
var f = {};
return f.encText = c(a + e, b, d),
f
}
window.asrsea = d,
window.ecnonasr = e
}();
由此我们可以肯定params和encSecKey是在d这个函数中生成的且bUE3x就是d函数中返回的h对象
进一步发现d函数还调用了a,b,c三个函数
于是我们将上述代码的a,b,c,d四个函数复制放入js调试工具中,等待后续使用
再回头分析传入d的四个参数,我们通过分析整体代码(分析过程省略)认为后三个参数是一个固定值,而第一个参数暂时无从得知,于是便在d函数中设置断点进行调试,记录下了后三个数据,同时意外的发现第一个参数似乎只和id有关
格式为"{\"ids\":\"["+id+"]\",\"level\":\"standard\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}";
我们又发现id值正是上方网址中的id
所以至此我们解决了所有的传入d函数的参数的问题
我们将bUE3X处的代码改写成如下函数一并加入js调试工具中
function generate(id)
{
x ="{\"ids\":\"["+id+"]\",\"level\":\"standard\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}";
var bUE3x = d(x, "010001", "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7","0CoJUm6Qyw8W8jud");
params = bUE3x.encText,
encSecKey = bUE3x.encSecKey
return params+","+encSecKey
}
在js调试工具中点击加载代码,显示加载成功,说明没有语法错误
再在下方输入generate("1299844911")
点击生成表达式
却显示 ‘CryptoJS’ 未定义,说明我们还得加入CryptoJS相关的代码
我们在代码中搜索CryptoJS,发现总共有13处,其中12045到12756行(不同人不同时间的这个代码可能有差别)之间的代码,包括了所有定义CryptoJS及其方法的内容,所以我们将这些全部复制放入js调试工具中
再次加载代码并计算表达式,发现 ‘CryptoJS’ 未定义的问题解决了,但是又出现了缺少对象setMaxDigits的问题
同样的我们去搜索并且引入了这段函数,但是发现这个函数里还引用了其余一堆函数,十分的复杂。
但是我们发现这些使用的函数位置十分相近,基本都在12700到13200行之间,说以我们把这些函数和相关代码(RSAKeyPair函数到BarrettMu_powMod函数)全部复制放入js调试工具中
这次显示bitPerDigit未定义,于是我们再次查找搜索js代码,把如下定义bitPerDigit及其他东西的相关内容加入
var maxDigits, ZERO_ARRAY, bigZero, bigOne, dpl10, lr10, hexatrigesimalToChar, hexToChar, highBitMasks, lowBitMasks, biRadixBase = 2, biRadixBits = 16, bitsPerDigit = biRadixBits, biRadix = 65536, biHalfRadix = biRadix >>> 1, biRadixSquared = biRadix * biRadix, maxDigitVal = biRadix - 1, maxInteger = 9999999999999998;
setMaxDigits(20),
dpl10 = 15,
lr10 = biFromNumber(1e15),
hexatrigesimalToChar = new Array("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"),
hexToChar = new Array("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"),
highBitMasks = new Array(0,32768,49152,57344,61440,63488,64512,65024,65280,65408,65472,65504,65520,65528,65532,65534,65535),
lowBitMasks = new Array(0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535);
再次加载代码,发现成功生成了params和encSecKey这两个参数(由于和随机算法有关,每次生成的都不一样),终于成功了!!!
最后我将它导出命名为wangyi.js文件
wanyi.js下载地址:https://download.csdn.net/download/for_none/20814273
调用js代码的方式如下
def getdata(id):#调用js代码,获取params和encSecKey
node = execjs.get()
ctx = node.compile(open('wangyi.js', encoding='utf-8').read())
exp = 'generate(\"' + id + '\")'
pwd = ctx.eval(exp).split(",")
data = {'params': pwd[0], 'encSecKey': pwd[1]}
print('生成参数为:')
print(data)
return data
我们已经得到了params和encSecKey接下来程序实现就比较简单了,有想要代码参考的直接找我就行