【爬虫实战】使用Python和JS两种方式逆向网易云音乐接口并下载歌曲

前言
目前绝大部分网站只提供在线听歌的功能,几乎都无法下载,就算能下载也不能直接播放或者需要使用专有的播放器 。那么如何获取歌曲的源文件呢?接下来以网易云为例下载某一首歌 。本文使用两种JS逆向的方式 , 便于不同场景的学习和使用 。
一、目标分析
在网易云音乐的首页可以搜索也可以直接点击播放,然后就会进入一个歌曲的单独页面 。
以当前歌曲为例,可以看到当点击播放的时候会出现很多请求的接口 , 接下来一个接口一个接口的看,直到发现和歌曲下载有关的那一个为止 。
最后定位到了/weapi/song///url/v1?=这个接口 。并且接口返回值里有一个URL为,这是一个m4a文件 。其实这就是一个音频文件,类似于之前常见的mp3格式等等 。
M4A 是一种音频文件格式,它是 MPEG-4 Part 14 标准的一部分 。M4A 文件通常包含以Audio(AAC) 编码的音频数据,但它也可以包含其他音频编码类型 。这个格式的名称 “M4A” 是由 Apple 公司引入的,用于标识其在中使用的音频文件 。
M4A 文件通常具有较高的音频质量,并且相对于其他一些音频格式 , 文件大小较小 。它是一种无损音频格式 , 可以提供高质量的音频体验 。由于其高效的压缩算法 , M4A 文件经常用于存储音乐、音效和其他类型的音频内容 。
尽管 M4A 格式在音质和文件大小上有一些优势 , 但它可能不如其他格式如 MP3 在广泛的多平台兼容性上好 。不过 , 许多现代设备和音频播放器都支持 M4A 格式,特别是在苹果的生态系统中,它是一种常见的音频文件格式 。
接下来可以请求一下这个链接的文件是否能播放,如果能播放就是我们的目标 。
的确这就是我们要的文件 。
至此,已经找到了最重要的目标,获取m4a文件 。
二、接口分析
前文中找到了获取文件的接口,那么这个接口是如何工作的呢 。
1. 查看请求头
可以看到并没有加密的参数,唯一有可能影响的就是,但是我们可以试试先不带,其他的UA和最好先带上 。
2. 查看参数
参数有两个,都是加密后的,所以重点就是这个两个参数 。
三、 获取加密方式 1. 定位功能函数
定位加密函数有多种方式,这里以全局搜索为例,可以搜索这样的特殊字段,大概率能搜到 。其实这个关键字还是搜索到了很多条记录,但是可以都打上断点用排除的方式进行判断,最终定位到了.js这个文件 。
可以看到有两个字段parms和,都是密文,但是不确定是不是我们要的结果 。所以可以等这次执行完和浏览器上的值对比一下 。需要注意前面有个x4b的值是一个URL , 得确保这个URL是刚才查到的那个接口,否则就走错路了 。
等请求跑完,对比后确认这个就是我们要找的加密函数 。所以关键点就在下面的代码中:

【爬虫实战】使用Python和JS两种方式逆向网易云音乐接口并下载歌曲

文章插图
var bVg5l = window.asrsea(JSON.stringify(i3x), bsk0x(["流泪", "强"]), bsk0x(WH9y.md), bsk0x(["爱心", "女孩", "惊恐", "大笑"]));
可以在控制台中依次看一下这几个参数的输出是什么
函数中的第一个参数是由歌曲id拼接的,其他的参数有的是固定值 , 有的不确定,可以检查一下 。
WH9y.md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手", "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]
在代码中搜索可以看到第三个参数确定也是固定的 。
接下来就重点关注 .()这个函数了,然后定位到这个d函数
d函数也调用了当前作用域下的其他几个函数 。接下来就可以使用JS的方式进行逆向了 。
2. 方式1、逐个补函数
主要思想就是用到哪个函数就复制哪个函数,优点就是减少了代码量和减少思考;缺点就是得一个个补,耗时较长 。
首先补d函数所在的作用域里的函数:
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
由于需要将d函数输出,所以可以简单改造一下 , 一下 。
【【爬虫实战】使用Python和JS两种方式逆向网易云音乐接口并下载歌曲】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}const s1 = '{"ids":"[2095868608]","level":"standard","encodeType":"aac","csrf_token":""}'const s2 = "010001"const s3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"const s4 = "0CoJUm6Qyw8W8jud"console.log(d(s1, s2, s3, s4))
接下来就运行当前js文件,报错缺哪个函数就补哪个函数 。最后代码如下:
const CryptoJS = require('crypto-js');function setMaxDigits(a) {maxDigits = a, ZERO_ARRAY = new Array(maxDigits);for (var b = 0; b < ZERO_ARRAY.length; b++) ZERO_ARRAY[b] = 0;bigZero = new BigInt, bigOne = new BigInt, bigOne.digits[0] = 1}function BigInt(a) {this.digits = "boolean" == typeof a && 1 == a ? null : ZERO_ARRAY.slice(0), this.isNeg = !1}function RSAKeyPair(a, b, c) {this.e = biFromHex(a), this.d = biFromHex(b), this.m = biFromHex(c), this.chunkSize = 2 * biHighIndex(this.m), this.radix = 16, this.barrett = new BarrettMu(this.m)}function biFromHex(a) {var d, e, b = new BigInt, c = a.length;for (d = c, e = 0; d > 0; d -= 4, ++e) b.digits[e] = hexToDigit(a.substr(Math.max(d - 4, 0), Math.min(d, 4)));return b}function charToHex(a) {var h, b = 48, c = b + 9, d = 97, e = d + 25, f = 65, g = 90;return h = a >= b && c >= a ? a - b : a >= f && g >= a ? 10 + a - f : a >= d && e >= a ? 10 + a - d : 0}function hexToDigit(a) {var d, b = 0, c = Math.min(a.length, 4);for (d = 0; c > d; ++d) b <<= 4, b |= charToHex(a.charCodeAt(d));return b}function biHighIndex(a) {for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];) --b;return b}function BarrettMu(a) {this.modulus = biCopy(a), this.k = biHighIndex(this.modulus) + 1;var b = new BigInt;b.digits[2 * this.k] = 1, this.mu = biDivide(b, this.modulus), this.bkplus1 = new BigInt, this.bkplus1.digits[this.k + 1] = 1, this.modulo = BarrettMu_modulo, this.multiplyMod = BarrettMu_multiplyMod, this.powMod = BarrettMu_powMod}function biCopy(a) {var b = new BigInt(!0);return b.digits = a.digits.slice(0), b.isNeg = a.isNeg, b}function biDivide(a, b) {return biDivideModulo(a, b)[0]}function biModulo(a, b) {return biDivideModulo(a, b)[1]}function biMultiplyMod(a, b, c) {return biModulo(biMultiply(a, b), c)}function biDivideModulo(a, b) {var f, g, h, i, j, k, l, m, n, o, p, q, r, s, c = biNumBits(a), d = biNumBits(b), e = b.isNeg;if (d > c) return a.isNeg ? (f = biCopy(bigOne), f.isNeg = !b.isNeg, a.isNeg = !1, b.isNeg = !1, g = biSubtract(b, a), a.isNeg = !0, b.isNeg = e) : (f = new BigInt, g = biCopy(a)), new Array(f, g);for (f = new BigInt, g = a, h = Math.ceil(d / bitsPerDigit) - 1, i = 0; b.digits[h] < biHalfRadix;) b = biShiftLeft(b, 1), ++i, ++d, h = Math.ceil(d / bitsPerDigit) - 1;for (g = biShiftLeft(g, i), c += i, j = Math.ceil(c / bitsPerDigit) - 1, k = biMultiplyByRadixPower(b, j - h); -1 != biCompare(g, k);) ++f.digits[j - h], g = biSubtract(g, k);for (l = j; l > h; --l) {for (m = l >= g.digits.length ? 0 : g.digits[l], n = l - 1 >= g.digits.length ? 0 : g.digits[l - 1], o = l - 2 >= g.digits.length ? 0 : g.digits[l - 2], p = h >= b.digits.length ? 0 : b.digits[h], q = h - 1 >= b.digits.length ? 0 : b.digits[h - 1], f.digits[l - h - 1] = m == p ? maxDigitVal : Math.floor((m * biRadix + n) / p), r = f.digits[l - h - 1] * (p * biRadix + q), s = m * biRadixSquared + (n * biRadix + o); r > s;) --f.digits[l - h - 1], r = f.digits[l - h - 1] * (p * biRadix | q), s = m * biRadix * biRadix + (n * biRadix + o);k = biMultiplyByRadixPower(b, l - h - 1), g = biSubtract(g, biMultiplyDigit(k, f.digits[l - h - 1])), g.isNeg && (g = biAdd(g, k), --f.digits[l - h - 1])}return g = biShiftRight(g, i), f.isNeg = a.isNeg != e, a.isNeg && (f = e ? biAdd(f, bigOne) : biSubtract(f, bigOne), b = biShiftRight(b, i), g = biSubtract(b, g)), 0 == g.digits[0] && 0 == biHighIndex(g) && (g.isNeg = !1), new Array(f, g)}function biHighIndex(a) {for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];) --b;return b}function biNumBits(a) {var e, b = biHighIndex(a), c = a.digits[b], d = (b + 1) * bitsPerDigit;for (e = d; e > d - bitsPerDigit && 0 == (32768 & c); --e) c <<= 1;return e}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);function biFromNumber(a) {var c, b = new BigInt;for (b.isNeg = 0 > a, a = Math.abs(a), c = 0; a > 0;) b.digits[c++] = a & maxDigitVal, a >>= biRadixBits;return b}function biShiftLeft(a, b) {var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;for (arrayCopy(a.digits, 0, d.digits, c, d.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = d.digits.length - 1, h = g - 1; g > 0; --g, --h) d.digits[g] = d.digits[g] << e & maxDigitVal | (d.digits[h] & highBitMasks[e]) >>> f;return d.digits[0] = d.digits[g] << e & maxDigitVal, d.isNeg = a.isNeg, d}function arrayCopy(a, b, c, d, e) {var g, h, f = Math.min(b + e, a.length);for (g = b, h = d; f > g; ++g, ++h) c[h] = a[g]}function biMultiplyByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, 0, c.digits, b, c.digits.length - b), c}function biDivideByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, b, c.digits, 0, c.digits.length - b), c}function biModuloByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, 0, c.digits, 0, b), c}function biCompare(a, b) {if (a.isNeg != b.isNeg) return 1 - 2 * Number(a.isNeg);for (var c = a.digits.length - 1; c >= 0; --c) if (a.digits[c] != b.digits[c]) return a.isNeg ? 1 - 2 * Number(a.digits[c] > b.digits[c]) : 1 - 2 * Number(a.digits[c] < b.digits[c]);return 0}function biSubtract(a, b) {var c, d, e, f;if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg, c = biAdd(a, b), b.isNeg = !b.isNeg; else {for (c = new BigInt, e = 0, f = 0; f < a.digits.length; ++f) d = a.digits[f] - b.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d);if (-1 == e) {for (e = 0, f = 0; f < a.digits.length; ++f) d = 0 - c.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d);c.isNeg = !a.isNeg} else c.isNeg = a.isNeg}return c}function biMultiplyDigit(a, b) {var c, d, e, f;for (result = new BigInt, c = biHighIndex(a), d = 0, f = 0; c >= f; ++f) e = result.digits[f] + a.digits[f] * b + d, result.digits[f] = e & maxDigitVal, d = e >>> biRadixBits;return result.digits[1 + c] = d, result}function biShiftRight(a, b) {var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;for (arrayCopy(a.digits, c, d.digits, 0, a.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = 0, h = g + 1; g < d.digits.length - 1; ++g, ++h) d.digits[g] = d.digits[g] >>> e | (d.digits[h] & lowBitMasks[e]) << f;return d.digits[d.digits.length - 1] >>>= e, d.isNeg = a.isNeg, d}function BarrettMu_modulo(a) {var i, b = biDivideByRadixPower(a, this.k - 1), c = biMultiply(b, this.mu), d = biDivideByRadixPower(c, this.k + 1),e = biModuloByRadixPower(a, this.k + 1), f = biMultiply(d, this.modulus),g = biModuloByRadixPower(f, this.k + 1), h = biSubtract(e, g);for (h.isNeg && (h = biAdd(h, this.bkplus1)), i = biCompare(h, this.modulus) >= 0; i;) h = biSubtract(h, this.modulus), i = biCompare(h, this.modulus) >= 0;return h}function BarrettMu_multiplyMod(a, b) {var c = biMultiply(a, b);return this.modulo(c)}function BarrettMu_powMod(a, b) {var d, e, c = new BigInt;for (c.digits[0] = 1, d = a, e = b; ;) {if (0 != (1 & e.digits[0]) && (c = this.multiplyMod(c, d)), e = biShiftRight(e, 1), 0 == e.digits[0] && 0 == biHighIndex(e)) break;d = this.multiplyMod(d, d)}return c}function encryptedString(a, b) {for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e;) c[e] = b.charCodeAt(e), e++;for (; 0 != c.length % a.chunkSize;) c[e++] = 0;for (f = c.length, g = "", e = 0; f > e; e += a.chunkSize) {for (j = new BigInt, h = 0, i = e; i < e + a.chunkSize; ++h) j.digits[h] = c[i++], j.digits[h] += c[i++] << 8;k = a.barrett.powMod(j, a.e), l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix), g += l + " "}return g.substring(0, g.length - 1)}function decryptedString(a, b) {var e, f, g, h, c = b.split(" "), d = "";for (e = 0; e < c.length; ++e) for (h = 16 == a.radix ? biFromHex(c[e]) : biFromString(c[e], a.radix), g = a.barrett.powMod(h, a.d), f = 0; f <= biHighIndex(g); ++f) d += String.fromCharCode(255 & g.digits[f], g.digits[f] >> 8);return 0 == d.charCodeAt(d.length - 1) && (d = d.substring(0, d.length - 1)), d}function biMultiply(a, b) {var d, h, i, k, c = new BigInt, e = biHighIndex(a), f = biHighIndex(b);for (k = 0; f >= k; ++k) {for (d = 0, i = k, j = 0; e >= j; ++j, ++i) h = c.digits[i] + a.digits[j] * b.digits[k] + d, c.digits[i] = h & maxDigitVal, d = h >>> biRadixBits;c.digits[k + e + 1] = d}return c.isNeg = a.isNeg != b.isNeg, c}function biMultiplyDigit(a, b) {var c, d, e, f;for (result = new BigInt, c = biHighIndex(a), d = 0, f = 0; c >= f; ++f) e = result.digits[f] + a.digits[f] * b + d, result.digits[f] = e & maxDigitVal, d = e >>> biRadixBits;return result.digits[1 + c] = d, result}function biToHex(a) {var d, b = "";for (biHighIndex(a), d = biHighIndex(a); d > -1; --d) b += digitToHex(a.digits[d]);return b}function digitToHex(a) {var b = 15, c = "";for (i = 0; 4 > i; ++i) c += hexToChar[a & b], a >>>= 4;return reverseStr(c)}function reverseStr(a) {var c, b = "";for (c = a.length - 1; c > -1; --c) b += a.charAt(c);return b}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}const s1 = '{"ids":"[2095868608]","level":"standard","encodeType":"aac","csrf_token":""}'const s2 = "010001"const s3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"const s4 = "0CoJUm6Qyw8W8jud"console.log(d(s1, s2, s3, s4))
输出结果:
{encText: '2r5EJr1vZXnDnGpLKshUlqM8EA0pnIeVo8iVD0OmWDXz7eY2u5UND8iokynDdOiedX7OiYOKLCaaTKtA1v26dfPtU0cmc5b0U97R4UgkcROf1zylysKZC+26YLxD8h6KQrhVVksEoWZQ/wOKVgZr5w==',encSecKey: '5054917c52ea0cc59563831d6d4691da2b323e11509496023d612e8787d9b472cec3e0d4c74e2731f8c97f224b9e700560b3e745c81d1576b7e9f9eb796606bc9475a812f2de941fc34baccf7e854153ebd377486b718ab8c22cac8ca88e165a2a6e463804b0cec3a0b3e60e9939d0281f2cc48815489103b1ea00f5d2bfade2'}
虽然结果出来了,但是和刚才获取的不一样,这两个值是否有效呢?其实每次获取的都不会一样的,因为a函数中有个获取的获取的是随机值 。至于是否有效,可以将数据请求一下就知道是否可以了 。
3. 方式2、根据作用域补函数
首先补充几个知识点 。
匿名函数
中的匿名函数是指没有名字的函数 , 通常被用作一次性的、临时的函数 。匿名函数有两种主要的形式:函数表达式和箭头函数 。
函数表达式
函数表达式是指将函数赋值给一个变量或者常量,而不是声明一个具有名称的函数 。这个函数可以是匿名的 。
// 匿名函数表达式const add = function(x, y) {return x + y;};// 使用匿名函数console.log(add(2, 3)); // 输出 5
箭头函数
【爬虫实战】使用Python和JS两种方式逆向网易云音乐接口并下载歌曲

文章插图
箭头函数是 ES6 引入的一种更简洁的函数写法 。它可以更方便地定义匿名函数 。
// 匿名箭头函数const add = (x, y) => {return x + y;};// 如果函数体只有一行,可以省略大括号和 returnconst multiply = (x, y) => x * y;// 使用匿名箭头函数console.log(add(2, 3));// 输出 5console.log(multiply(2, 3)); // 输出 6
匿名函数通常在需要临时处理某些逻辑的情况下使用,或者作为函数的参数传递 。它们在创建回调函数、事件处理函数等场景中非常常见 。由于匿名函数没有名字,因此不能通过名字调用,必须通过变量或者直接在表达式中使用 。
在中,每个函数都创建了一个新的作用域 。函数内部的变量在函数外部是不可访问的,这被称为函数作用域 。而匿名函数也遵循这一规则 。
// 全局作用域var globalVar = 'I am global';function exampleFunction() {// 函数作用域var localVar = 'I am local';console.log(globalVar); // 可以访问全局变量console.log(localVar);// 可以访问本地变量}exampleFunction();// console.log(localVar); // 错误 , localVar 不在此作用域可见
匿名函数通常与闭包密切相关 。闭包是指一个函数能够记住并访问它被创建时所处的作用域 , 即使该函数在不同的作用域被调用 。匿名函数经常被用作闭包 。
function outerFunction() {var outerVar = 'I am outer';// 返回一个匿名函数 , 形成闭包return function() {console.log(outerVar); // 访问 outerFunction 的局部变量};}var closureFunction = outerFunction();closureFunction(); // 输出 'I am outer'
在上述例子中,是一个匿名函数,它被赋值为的返回值 。由于匿名函数被返回并在外部作用域执行,它形成了一个闭包,可以访问外部函数的局部变量。
匿名函数在闭包中经常用于保持某个函数内的状态,并在以后的调用中保留这个状态 。
总的来说,匿名函数和作用域之间的关系表现在匿名函数遵循函数作用域规则,并且匿名函数常常用于创建闭包,以保留函数创建时的作用域 。
整体思路 首先复制全局变量然后复制重点功能函数
那么具体怎么做呢?举个例子,刚才d函数嵌套使用了c函数,c函数又使用了函数 , 但是这个函数明显和c不在一个作用域里,所以就要把这个函数所在的作用域的代码全部复制过来 。
的顶级作用域是指代码中不包含在任何函数内的部分 , 即全局作用域 。在浏览器环境中,顶级作用域通常是整个页面 。在 Node.js 等环境中,顶级作用域可以是整个 Node.js 模块 。
在顶级作用域中声明的变量和函数具有全局作用域,可以被程序中的任何其他作用域访问 。这意味着在全局范围内声明的变量在整个程序中都是可见的 。
所以像这样类似的代码可以全部复制过来,如果运行报错就接着补 。补完全局变量后就再补具体的功能函数 。
代码如下:
const CryptoJS = require('crypto-js');// 全局变量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);!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}}();function RSAKeyPair(a, b, c) {this.e = biFromHex(a), this.d = biFromHex(b), this.m = biFromHex(c), this.chunkSize = 2 * biHighIndex(this.m), this.radix = 16, this.barrett = new BarrettMu(this.m)}function twoDigit(a) {return (10 > a ? "0" : "") + String(a)}function encryptedString(a, b) {for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e;) c[e] = b.charCodeAt(e), e++;for (; 0 != c.length % a.chunkSize;) c[e++] = 0;for (f = c.length, g = "", e = 0; f > e; e += a.chunkSize) {for (j = new BigInt, h = 0, i = e; i < e + a.chunkSize; ++h) j.digits[h] = c[i++], j.digits[h] += c[i++] << 8;k = a.barrett.powMod(j, a.e), l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix), g += l + " "}return g.substring(0, g.length - 1)}function decryptedString(a, b) {var e, f, g, h, c = b.split(" "), d = "";for (e = 0; e < c.length; ++e) for (h = 16 == a.radix ? biFromHex(c[e]) : biFromString(c[e], a.radix), g = a.barrett.powMod(h, a.d), f = 0; f <= biHighIndex(g); ++f) d += String.fromCharCode(255 & g.digits[f], g.digits[f] >> 8);return 0 == d.charCodeAt(d.length - 1) && (d = d.substring(0, d.length - 1)), d}function setMaxDigits(a) {maxDigits = a, ZERO_ARRAY = new Array(maxDigits);for (var b = 0; b < ZERO_ARRAY.length; b++) ZERO_ARRAY[b] = 0;bigZero = new BigInt, bigOne = new BigInt, bigOne.digits[0] = 1}function BigInt(a) {this.digits = "boolean" == typeof a && 1 == a ? null : ZERO_ARRAY.slice(0), this.isNeg = !1}function biFromDecimal(a) {for (var d, e, f, b = "-" == a.charAt(0), c = b ? 1 : 0; c < a.length && "0" == a.charAt(c);) ++c;if (c == a.length) d = new BigInt; else {for (e = a.length - c, f = e % dpl10, 0 == f && (f = dpl10), d = biFromNumber(Number(a.substr(c, f))), c += f; c < a.length;) d = biAdd(biMultiply(d, lr10), biFromNumber(Number(a.substr(c, dpl10)))), c += dpl10;d.isNeg = b}return d}function biCopy(a) {var b = new BigInt(!0);return b.digits = a.digits.slice(0), b.isNeg = a.isNeg, b}function biFromNumber(a) {var c, b = new BigInt;for (b.isNeg = 0 > a, a = Math.abs(a), c = 0; a > 0;) b.digits[c++] = a & maxDigitVal, a >>= biRadixBits;return b}function reverseStr(a) {var c, b = "";for (c = a.length - 1; c > -1; --c) b += a.charAt(c);return b}function biToString(a, b) {var d, e, c = new BigInt;for (c.digits[0] = b, d = biDivideModulo(a, c), e = hexatrigesimalToChar[d[1].digits[0]]; 1 == biCompare(d[0], bigZero);) d = biDivideModulo(d[0], c), digit = d[1].digits[0], e += hexatrigesimalToChar[d[1].digits[0]];return (a.isNeg ? "-" : "") + reverseStr(e)}function biToDecimal(a) {var c, d, b = new BigInt;for (b.digits[0] = 10, c = biDivideModulo(a, b), d = String(c[1].digits[0]); 1 == biCompare(c[0], bigZero);) c = biDivideModulo(c[0], b), d += String(c[1].digits[0]);return (a.isNeg ? "-" : "") + reverseStr(d)}function digitToHex(a) {var b = 15, c = "";for (i = 0; 4 > i; ++i) c += hexToChar[a & b], a >>>= 4;return reverseStr(c)}function biToHex(a) {var d, b = "";for (biHighIndex(a), d = biHighIndex(a); d > -1; --d) b += digitToHex(a.digits[d]);return b}function charToHex(a) {var h, b = 48, c = b + 9, d = 97, e = d + 25, f = 65, g = 90;return h = a >= b && c >= a ? a - b : a >= f && g >= a ? 10 + a - f : a >= d && e >= a ? 10 + a - d : 0}function hexToDigit(a) {var d, b = 0, c = Math.min(a.length, 4);for (d = 0; c > d; ++d) b <<= 4, b |= charToHex(a.charCodeAt(d));return b}function biFromHex(a) {var d, e, b = new BigInt, c = a.length;for (d = c, e = 0; d > 0; d -= 4, ++e) b.digits[e] = hexToDigit(a.substr(Math.max(d - 4, 0), Math.min(d, 4)));return b}function biFromString(a, b) {var g, h, i, j, c = "-" == a.charAt(0), d = c ? 1 : 0, e = new BigInt, f = new BigInt;for (f.digits[0] = 1, g = a.length - 1; g >= d; g--) h = a.charCodeAt(g), i = charToHex(h), j = biMultiplyDigit(f, i), e = biAdd(e, j), f = biMultiplyDigit(f, b);return e.isNeg = c, e}function biDump(a) {return (a.isNeg ? "-" : "") + a.digits.join(" ")}function biAdd(a, b) {var c, d, e, f;if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg, c = biSubtract(a, b), b.isNeg = !b.isNeg; else {for (c = new BigInt, d = 0, f = 0; f < a.digits.length; ++f) e = a.digits[f] + b.digits[f] + d, c.digits[f] = 65535 & e, d = Number(e >= biRadix);c.isNeg = a.isNeg}return c}function biSubtract(a, b) {var c, d, e, f;if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg, c = biAdd(a, b), b.isNeg = !b.isNeg; else {for (c = new BigInt, e = 0, f = 0; f < a.digits.length; ++f) d = a.digits[f] - b.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d);if (-1 == e) {for (e = 0, f = 0; f < a.digits.length; ++f) d = 0 - c.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d);c.isNeg = !a.isNeg} else c.isNeg = a.isNeg}return c}function biHighIndex(a) {for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];) --b;return b}function biNumBits(a) {var e, b = biHighIndex(a), c = a.digits[b], d = (b + 1) * bitsPerDigit;for (e = d; e > d - bitsPerDigit && 0 == (32768 & c); --e) c <<= 1;return e}function biMultiply(a, b) {var d, h, i, k, c = new BigInt, e = biHighIndex(a), f = biHighIndex(b);for (k = 0; f >= k; ++k) {for (d = 0, i = k, j = 0; e >= j; ++j, ++i) h = c.digits[i] + a.digits[j] * b.digits[k] + d, c.digits[i] = h & maxDigitVal, d = h >>> biRadixBits;c.digits[k + e + 1] = d}return c.isNeg = a.isNeg != b.isNeg, c}function biMultiplyDigit(a, b) {var c, d, e, f;for (result = new BigInt, c = biHighIndex(a), d = 0, f = 0; c >= f; ++f) e = result.digits[f] + a.digits[f] * b + d, result.digits[f] = e & maxDigitVal, d = e >>> biRadixBits;return result.digits[1 + c] = d, result}function arrayCopy(a, b, c, d, e) {var g, h, f = Math.min(b + e, a.length);for (g = b, h = d; f > g; ++g, ++h) c[h] = a[g]}function biShiftLeft(a, b) {var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;for (arrayCopy(a.digits, 0, d.digits, c, d.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = d.digits.length - 1, h = g - 1; g > 0; --g, --h) d.digits[g] = d.digits[g] << e & maxDigitVal | (d.digits[h] & highBitMasks[e]) >>> f;return d.digits[0] = d.digits[g] << e & maxDigitVal, d.isNeg = a.isNeg, d}function biShiftRight(a, b) {var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;for (arrayCopy(a.digits, c, d.digits, 0, a.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = 0, h = g + 1; g < d.digits.length - 1; ++g, ++h) d.digits[g] = d.digits[g] >>> e | (d.digits[h] & lowBitMasks[e]) << f;return d.digits[d.digits.length - 1] >>>= e, d.isNeg = a.isNeg, d}function biMultiplyByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, 0, c.digits, b, c.digits.length - b), c}function biDivideByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, b, c.digits, 0, c.digits.length - b), c}function biModuloByRadixPower(a, b) {var c = new BigInt;return arrayCopy(a.digits, 0, c.digits, 0, b), c}function biCompare(a, b) {if (a.isNeg != b.isNeg) return 1 - 2 * Number(a.isNeg);for (var c = a.digits.length - 1; c >= 0; --c) if (a.digits[c] != b.digits[c]) return a.isNeg ? 1 - 2 * Number(a.digits[c] > b.digits[c]) : 1 - 2 * Number(a.digits[c] < b.digits[c]);return 0}function biDivideModulo(a, b) {var f, g, h, i, j, k, l, m, n, o, p, q, r, s, c = biNumBits(a), d = biNumBits(b), e = b.isNeg;if (d > c) return a.isNeg ? (f = biCopy(bigOne), f.isNeg = !b.isNeg, a.isNeg = !1, b.isNeg = !1, g = biSubtract(b, a), a.isNeg = !0, b.isNeg = e) : (f = new BigInt, g = biCopy(a)), new Array(f, g);for (f = new BigInt, g = a, h = Math.ceil(d / bitsPerDigit) - 1, i = 0; b.digits[h] < biHalfRadix;) b = biShiftLeft(b, 1), ++i, ++d, h = Math.ceil(d / bitsPerDigit) - 1;for (g = biShiftLeft(g, i), c += i, j = Math.ceil(c / bitsPerDigit) - 1, k = biMultiplyByRadixPower(b, j - h); -1 != biCompare(g, k);) ++f.digits[j - h], g = biSubtract(g, k);for (l = j; l > h; --l) {for (m = l >= g.digits.length ? 0 : g.digits[l], n = l - 1 >= g.digits.length ? 0 : g.digits[l - 1], o = l - 2 >= g.digits.length ? 0 : g.digits[l - 2], p = h >= b.digits.length ? 0 : b.digits[h], q = h - 1 >= b.digits.length ? 0 : b.digits[h - 1], f.digits[l - h - 1] = m == p ? maxDigitVal : Math.floor((m * biRadix + n) / p), r = f.digits[l - h - 1] * (p * biRadix + q), s = m * biRadixSquared + (n * biRadix + o); r > s;) --f.digits[l - h - 1], r = f.digits[l - h - 1] * (p * biRadix | q), s = m * biRadix * biRadix + (n * biRadix + o);k = biMultiplyByRadixPower(b, l - h - 1), g = biSubtract(g, biMultiplyDigit(k, f.digits[l - h - 1])), g.isNeg && (g = biAdd(g, k), --f.digits[l - h - 1])}return g = biShiftRight(g, i), f.isNeg = a.isNeg != e, a.isNeg && (f = e ? biAdd(f, bigOne) : biSubtract(f, bigOne), b = biShiftRight(b, i), g = biSubtract(b, g)), 0 == g.digits[0] && 0 == biHighIndex(g) && (g.isNeg = !1), new Array(f, g)}function biDivide(a, b) {return biDivideModulo(a, b)[0]}function biModulo(a, b) {return biDivideModulo(a, b)[1]}function biMultiplyMod(a, b, c) {return biModulo(biMultiply(a, b), c)}function biPow(a, b) {for (var c = bigOne, d = a; ;) {if (0 != (1 & b) && (c = biMultiply(c, d)), b >>= 1, 0 == b) break;d = biMultiply(d, d)}return c}function biPowMod(a, b, c) {for (var d = bigOne, e = a, f = b; ;) {if (0 != (1 & f.digits[0]) && (d = biMultiplyMod(d, e, c)), f = biShiftRight(f, 1), 0 == f.digits[0] && 0 == biHighIndex(f)) break;e = biMultiplyMod(e, e, c)}return d}function BarrettMu(a) {this.modulus = biCopy(a), this.k = biHighIndex(this.modulus) + 1;var b = new BigInt;b.digits[2 * this.k] = 1, this.mu = biDivide(b, this.modulus), this.bkplus1 = new BigInt, this.bkplus1.digits[this.k + 1] = 1, this.modulo = BarrettMu_modulo, this.multiplyMod = BarrettMu_multiplyMod, this.powMod = BarrettMu_powMod}function BarrettMu_modulo(a) {var i, b = biDivideByRadixPower(a, this.k - 1), c = biMultiply(b, this.mu), d = biDivideByRadixPower(c, this.k + 1),e = biModuloByRadixPower(a, this.k + 1), f = biMultiply(d, this.modulus),g = biModuloByRadixPower(f, this.k + 1), h = biSubtract(e, g);for (h.isNeg && (h = biAdd(h, this.bkplus1)), i = biCompare(h, this.modulus) >= 0; i;) h = biSubtract(h, this.modulus), i = biCompare(h, this.modulus) >= 0;return h}function BarrettMu_multiplyMod(a, b) {var c = biMultiply(a, b);return this.modulo(c)}function BarrettMu_powMod(a, b) {var d, e, c = new BigInt;for (c.digits[0] = 1, d = a, e = b; ;) {if (0 != (1 & e.digits[0]) && (c = this.multiplyMod(c, d)), e = biShiftRight(e, 1), 0 == e.digits[0] && 0 == biHighIndex(e)) break;d = this.multiplyMod(d, d)}return c}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);// 具体功能函数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}ret = d("{\"ids\":\"[2095868608]\",\"level\":\"standard\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}", "010001", "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7", "0CoJUm6Qyw8W8jud")console.log(ret)
其实代码并不重要 , 重要的是根据作用域补函数的方法 。如果掌握了这个方法,整体时间绝对会比逐个补函数的方式快上N倍 。就算代码量大个几倍又如何,没人在乎你用多少代码来实现的 , 只关心是否实现了功能 。毕竟爬虫的逆向不能完全和企业的业务功能做比较 。
4. 代码调用
#!/usr/bin/python# -*- coding: UTF-8 -*-"""@time:2023/11/16@file:网易云音乐.py@author:medivh@mail:admin@econow.cn@IDE:PyCharm """import jsonimport execjsimport requestsfrom faker import Fakerdef start(song_id):headers = {"User-Agent": Faker().user_agent()}with open('网易云音乐_作用域补环境.js') as f:JSCode = f.read()js = execjs.compile(JSCode)song_info = {"ids": f"[{song_id}]", "level": "standard", "encodeType": "aac", "csrf_token": ""}data = http://www.kingceram.com/post/(json.dumps(song_info),"010001","00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7","0CoJUm6Qyw8W8jud")# data 中后面的三个值都是固定的,如果报错有可能是后面的三个值更新了 , 重新调试即可jiamiData = http://www.kingceram.com/post/js.call("jiami", *data)print(jiamiData)res = requests.post('https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=', headers=headers, data=http://www.kingceram.com/post/{"params": jiamiData.get('encText'),"encSecKey": jiamiData.get('encSecKey')})m4a_link = res.json().get('data')[0].get('url')print(m4a_link)if __name__ == '__main__':song_id = 2095868608start(song_id)
结果输出:
{'encText': 'wAcRjHaYgKYDoxymMZeo0ZOQLImyk+ZF8dfErBBqgkLeSnG5dQB8XcC1YWzeJ0TwlK2RskEJAXlNG9lpUVWAnifu7YN7dXI/X2YQ+68aKLsxt70vgV/+w2HxtIxZyxtTIajiNDg5CtOyFzz2xOB9dg/n1OX3AaMulaZU+xnaAF165V0SshyaeCgxj0TJKSrk', 'encSecKey': '0bd0c2cb8a35e2f5cc78bfb7a7d5502e70f304233d92de12bff5dddc6265ec8f0eaf9bb72803455fa8864a8ee60ad744b0d31ffef80f9bf0d2d13d906fe325ca30db47299176a045e1e16bd550b05098ba6472debab4b8a323aca5ca1a15ed47232d1a41e0203d9515ab738dfbcfab0eb32f0446d0c5ba4ce900ed0370c4926f'}http://m801.music.126.net/20231116181635/7120abb587b188af0e77fa3b783478e8/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/31248364361/113a/77e3/a0a5/c2a90b87a91199babf8ea42f08a0d8b8.m4a
调用的代码就很简单了,唯一的变量就是歌曲ID 。其实歌曲ID这个获取也很假单 , 比如有个歌单,是不是就可以批量获取ID了 。举个例子,就算在接口中没有找到合适的数据,但是也可以用xpath进行解析啊 。方法有很多,有效就行 。
四、总结回顾
通过两种JS补函数的方式实现了当前功能的逆向 。方法有多种,只要能实现即可 。简单的功能简单做 , 复杂的功能复杂做 。