1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 网易云音乐API分析

网易云音乐API分析

时间:2020-08-16 16:21:04

相关推荐

网易云音乐API分析

环境:Win10

工具:Fiddler 4,Chrome浏览器

想做一个网易云音乐的下载地址解析,于是有了这篇文章,记录下过程。

开Fiddler截包

抓取到如下的包

POST /weapi/song/enhance/player/url?csrf_token= HTTP/1.1Host: Connection: keep-aliveContent-Length: 412Origin: User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36Content-Type: application/x-www-form-urlencodedAccept: */*Referer: /Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.8Cookie: _ntes_nnid=3ddf2945dfda076e60b4ee0162f3c0cd,1488291659575; _ntes_nuid=3ddf2945dfda076e60b4ee0162f3c0cd; vjuids=76bd0b1bc.15acc1b713d.0.73360b94f7956; vjlast=1489483035.1489483035.30; vinfo_n_f_l_n3=e441d5bafac7c9b7.1.0.1489483034993.0.1489483124982; JSESSIONID-WYYY=m%2FO4x3rjCh4e1xfBjmOCZ52hbV7rD7M9U77ZX9qn%2BMw5WeKyU0vnw1zGmpbn%5Ci5ZWmdaRmVjQromGw%2BxForePwG3mBf6jOy27vj1IMOv%5ClM3%2BXkUrPOeM7qPP9HhgO%2F%2Fd%5C1nWUq3mDUIicmtDvWMsUbEeWS%5CbNAJyUO8t%5CbHCy731FHp%3A1489661205597; _iuqxldmzr_=32; __utma=94650624.1301580310.1488291660.1489644772.1489659406.9; __utmb=94650624.4.10.1489659406; __utmc=94650624; __utmz=94650624.1489581581.6.4.utmcsr=baidu|utmccn=(organic)|utmcmd=organicparams=TsOlwX6jVxTvkYi3mFgJlVqczMblRwfL6sWG2wcRkYr5I2pB1GSC3eH4tLquNlPiXrBotId2T%2FI4UEwjRvbh3L%2FrlBKQ9de1vohxzueBBv%2FM01D4Fa%2BXhJ7jrSIhj%2FD2&encSecKey=747e088d0d3867619c340da1bedba0e60265ece84c0c1ef5ae81eff317b4afdaeed3666a9e1c3628abcabc0c602dba7685e1dfcc7ef82b6be24c35588a39941b6fe8e2a6fa751fded9c9b61c1cbf604d21b1cde54f244ea25c4db6673ef76151b8069b0ed5323c9ec3d2bace24b073b53e408ebb445c76a044ac797fa7c13be0

再看返回数据

HTTP/1.1 200 OKServer: nginxDate: Thu, 16 Mar 10:19:35 GMTContent-Type: text/plain;charset=UTF-8Connection: keep-aliveVary: Accept-EncodingCache-Control: no-storePragrma: no-cacheExpires: Thu, 01 Jan 1970 00:00:00 GMTCache-Control: no-cacheContent-Length: 352{"data":[{"id":426881487,"url":"http://m10./0316184438/37a51d1b5257f895cd867ff2dbd40186/ymusic/cba9//eacb/50ab2466dcd90414959d84aa9c65dd9f.mp3","br":128000,"size":4575025,"md5":"50ab2466dcd90414959d84aa9c65dd9f","code":200,"expi":1200,"type":"mp3","gain":-3.1099,"fee":0,"uf":null,"payed":0,"flag":0,"canExtend":false}],"code":200}

返回的json中包含了mp3的真实地址

这里看看发包的数据

params=TsOlwX6jVxTvkYi3mFgJlVqczMblRwfL6sWG2wcRkYr5I2pB1GSC3eH4tLquNlPiXrBotId2T%2FI4UEwjRvbh3L%2FrlBKQ9de1vohxzueBBv%2FM01D4Fa%2BXhJ7jrSIhj%2FD2&encSecKey=747e088d0d3867619c340da1bedba0e60265ece84c0c1ef5ae81eff317b4afdaeed3666a9e1c3628abcabc0c602dba7685e1dfcc7ef82b6be24c35588a39941b6fe8e2a6fa751fded9c9b61c1cbf604d21b1cde54f244ea25c4db6673ef76151b8069b0ed5323c9ec3d2bace24b073b53e408ebb445c76a044ac797fa7c13be0

看不出任何的规律,故猜测是加密的数据,一般而言都是通过js进行加密的,那我们就去js里找一找

在浏览器中按F12调出开发者工具看到有一个core.js,打开看一看

通过搜索关键词params可以找到如下代码

var bua = window.asrsea(JSON.stringify(bl), bbZ(["流泪", "强"]), bbZ(cnb.md), bbZ(["爱心", "女孩", "惊恐", "大笑"]));bf.data = bm.eX({params: bua.encText,encSecKey: bua.encSecKey})

这里看到paramsencSecKey是通过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}

这里看到asrsea函数就是d函数,并且传入了4个参数

对四个参数进行分析

利用fiddler的AutoResponder可以方便我们本地调试js

在core.js中加入这段

window.console.info(bl);var bua = window.asrsea(JSON.stringify(bl), bbZ(["流泪", "强"]), bbZ(cnb.md), bbZ(["爱心", "女孩", "惊恐", "大笑"]));window.console.warn(bua.encText);bf.data = bm.eX({params: bua.encText,encSecKey: bua.encSecKey})

清除浏览器的缓存,刷新页面,可以看到开发者工具console里出现了信息

{“ids”:”[426881487]”,”br”:128000,”csrf_token”:”“}

这个是参数一

用同样的方法获取到四个参数

参数一{"ids":"[426881487]","br":128000,"csrf_token":""}参数二010001参数三00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7参数四0CoJUm6Qyw8W8jud

显然encTextparams是等值的,那看一下encText是如何计算的

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}

调用了b方法,再去看看b

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()}

这是一个CBC模式的AES加密,偏移量是 “0102030405060708”

第一个参数是被加密文本

第二个参数是定值0CoJUm6Qyw8W8jud

回到b方法

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}

encText进行了两次加密,第二次是把第一次加密的结果再加密一次

那么第二次加密的key值i是怎么获得的,可以看到调用了a方法,再去看看a

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}

这是一个取随机字符的方法,参数决定了取出多少位,既然是随机数我们就可以自己随便写,只要是16位即可

至此我们分析出了params的加密方式

再来看看encSecKey

h.encSecKey = c(i, e, f),

调用了c方法,但是c方法传递的参数是固定的(i由我们自己决定),那么这个encSecKey也就是固定的

至此两个参数是如何得到的我们都分析出来了

本篇参考了知乎,点此跳转

附Java计算params值代码

/*** Created by PVer on /3/19.*/import java.util.Scanner;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;import .URLEncoder;public class AES {// 加密public static String Encrypt(String sSrc, String sKey) throws Exception {if (sKey == null) {System.out.print("Key为空null");return null;}// 判断Key是否为16位if (sKey.length() != 16) {System.out.print("Key长度不是16位");return null;}byte[] raw = sKey.getBytes();SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);byte[] encrypted = cipher.doFinal(sSrc.getBytes());return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。}// 解密public static String Decrypt(String sSrc, String sKey) throws Exception {try {// 判断Key是否正确if (sKey == null) {System.out.print("Key为空null");return null;}// 判断Key是否为16位if (sKey.length() != 16) {System.out.print("Key长度不是16位");return null;}byte[] raw = sKey.getBytes("UTF-8");SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密try {byte[] original = cipher.doFinal(encrypted1);String originalString = new String(original);return originalString;} catch (Exception e) {System.out.println(e.toString());return null;}} catch (Exception ex) {System.out.println(ex.toString());return null;}}public static String get_params(String text) throws Exception {String first_key = "0CoJUm6Qyw8W8jud";String second_key = "FFFFFFFFFFFFFFFF";String h_encText = AES.Encrypt(text, first_key);h_encText = AES.Encrypt(h_encText, second_key);return h_encText;}public static String get_encSecKey() {String encSecKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c";return encSecKey;}public static void main(String[] args) throws Exception {/** 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符,虽然不会错,至于怎么裁决,个人看情况而定* 此处使用AES-128-CBC加密模式,key需要为16位。*/Scanner scanner = new Scanner(System.in);System.out.print("请输入要解析的音乐ID:");String first_param = "{\"ids\":\"[" + scanner.next() + "]\",\"br\":128000,\"csrf_token\":\"\"}";System.out.println("params=" + URLEncoder.encode(AES.get_params(first_param), "UTF-8") + "&encSecKey=" + AES.get_encSecKey());}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。