首页 > 编程知识 正文

网易云音乐API 分析,网易云音乐设计分析

时间:2023-05-06 11:47:33 阅读:272119 作者:260

这几天一直在做一个仿QQ音乐的APP,但是苦于资金问题,一直没能解决服务器问题,所以打算使用第三方的服务。我这里的第三方就是网易云。

在此之前,我有分析过多家的api,大部分都不太好实现。其技术难点就在于获取音乐文件的请求都已经使用了tcp进行实现,获取的难度太大。不过有一家除外,那就是网易云。

我们这里使用重放攻击来实现获取服务器信息。打开我们的charles(如果你还没有下载过,请参考我的这篇博客:http://blog.csdn.net/u013022222/article/details/51693702)

打开云音乐,第一个看到的请求就是这个

我们看下请求raw数据:

一次post请求,夹带了一个看起来什么都不像的字符串,显然是以某种方式encode过的。在cookie里,有一段appver的KV对。我尝试过各种组合,发现我们的请求里只需要夹带这个cookie就可以了。

我们开始尝试重放这次请求,为了方便我们使用的Okhttp这个库:

OkHttpClient okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); String url = "http://music.163.com/eapi/batch"; FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder(); formEncodingBuilder.add("params", "0BD8BB39A78692F1744DEFF63EBC30F7889FA0D28FD18C56783C7BF3AADA4C516E269DCEF72717031B0D0797563D21D74A80931032E90A0DBF772B7B86DAB7B29C47227066BA6859EF81B2BDC94960501592EFDBED2FA4BB612DD34C3BE69C1CB997189A2D14BE23FACD2D81694F87D7D86DD3F48F213C035A89EDEE2F6336478BEBA964633B3DB2A074EA2662FE8AEC18A167403EA0D465ED99F6E0BF1B58D64E2F6FAB87BFB382901FB3F8D753ABABE5361DD03E8767F3CC5BE299EDCBF8CEA82126579A7E11CD9A6B7A95AEB41CEC237356031206C2C94443360BB430F44D4CE1F78FE98FDF4468B40977A33CD3A7AD9A9F926C5E1B3979139277DBCDF27E7EB4BFC0C4996CD069835883475527C7D296034459225E90FC0FD45F259EDAD79318B200CCC01B51E4571EFD93F7E7EFE09D1169A86936C7C3D1E0EAAFE6955D2A72808C6F340B4388E57F4443C22DCB267E6BA157E3256F2924B9A2DD0B1F4C001E848DC9F85F05DE82FCCA50763549329EF9DF1BC9746B9CFB7308D72159C5A5DC242B76960F7E62827FD52B8F4BCF7A667EBDAD93E5D34CB68D92ECBCD7FEE9265DD359457ED508F38B088041E5BBFDB949F891FA490B48B24C2C754762F31DC4C0F0C8E3930D08A628D82D10C6CADDEA0BBDF8D9FF405C9FE9B2E5622BD99757F50109BF2BBE0B6804606EB5EF23E3D772D023013244905739680AC5801E039D02D768DDB47BE085BE698DFA91C29B13F34AFEC3DA8E69251F8EB21D1A11B85F89B6383089FEF4713C1C21972D09E2433FEDADBAB3B6ED239935E06E76AACA3A66B3F11E51EFD0F5AD0CE6A32783"); RequestBody requestBody = formEncodingBuilder.build(); builder.addHeader("Accept", "*/*"); builder.addHeader("Accept-Encoding", "gzip,deflate,sdch"); builder.addHeader("Accept-Language", "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4"); builder.addHeader("Connection", "keep-alive"); builder.addHeader("Host", "music.163.com"); builder.addHeader("Referer", "http://music.163.com/search/"); builder.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"); builder.addHeader("Cookie", "appver=1.5.2"); builder.post(requestBody); builder.url(url); Call call = okHttpClient.newCall(builder.build()); Response response = call.execute(); System.out.println(response.body().string());

运行结果如下:

哇,是一堆乱码,不过既然数据这么大,是服务器报错的可能还是很小的,我们在看看response的信息吧:

天,真的太简单了,返回的数据只是gzip压缩的而已,我们解压一下就可以了:

OkHttpClient okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); String url = "http://music.163.com/eapi/batch"; FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder(); formEncodingBuilder.add("params", "0BD8BB39A78692F1744DEFF63EBC30F7889FA0D28FD18C56783C7BF3AADA4C516E269DCEF72717031B0D0797563D21D74A80931032E90A0DBF772B7B86DAB7B29C47227066BA6859EF81B2BDC94960501592EFDBED2FA4BB612DD34C3BE69C1CB997189A2D14BE23FACD2D81694F87D7D86DD3F48F213C035A89EDEE2F6336478BEBA964633B3DB2A074EA2662FE8AEC18A167403EA0D465ED99F6E0BF1B58D64E2F6FAB87BFB382901FB3F8D753ABABE5361DD03E8767F3CC5BE299EDCBF8CEA82126579A7E11CD9A6B7A95AEB41CEC237356031206C2C94443360BB430F44D4CE1F78FE98FDF4468B40977A33CD3A7AD9A9F926C5E1B3979139277DBCDF27E7EB4BFC0C4996CD069835883475527C7D296034459225E90FC0FD45F259EDAD79318B200CCC01B51E4571EFD93F7E7EFE09D1169A86936C7C3D1E0EAAFE6955D2A72808C6F340B4388E57F4443C22DCB267E6BA157E3256F2924B9A2DD0B1F4C001E848DC9F85F05DE82FCCA50763549329EF9DF1BC9746B9CFB7308D72159C5A5DC242B76960F7E62827FD52B8F4BCF7A667EBDAD93E5D34CB68D92ECBCD7FEE9265DD359457ED508F38B088041E5BBFDB949F891FA490B48B24C2C754762F31DC4C0F0C8E3930D08A628D82D10C6CADDEA0BBDF8D9FF405C9FE9B2E5622BD99757F50109BF2BBE0B6804606EB5EF23E3D772D023013244905739680AC5801E039D02D768DDB47BE085BE698DFA91C29B13F34AFEC3DA8E69251F8EB21D1A11B85F89B6383089FEF4713C1C21972D09E2433FEDADBAB3B6ED239935E06E76AACA3A66B3F11E51EFD0F5AD0CE6A32783"); RequestBody requestBody = formEncodingBuilder.build(); builder.addHeader("Accept", "*/*"); builder.addHeader("Accept-Encoding", "gzip,deflate,sdch"); builder.addHeader("Accept-Language", "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4"); builder.addHeader("Connection", "keep-alive"); builder.addHeader("Host", "music.163.com"); builder.addHeader("Referer", "http://music.163.com/search/"); builder.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"); builder.addHeader("Cookie", "appver=1.5.2"); builder.post(requestBody); builder.url(url); Call call = okHttpClient.newCall(builder.build()); Response response = call.execute(); GZIPInputStream gzipInputStream = new GZIPInputStream(response.body().byteStream()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int length = -1; byte[] cache = new byte[1024]; while ((length = gzipInputStream.read(cache)) != -1) { byteArrayOutputStream.write(cache, 0, length); } System.out.println(new String(byteArrayOutputStream.toByteArray()));

看下运行结果:

没毛病。

我们把返回的数据拷贝,贴进brackets,点击uri和云音乐界面进行对比:

这样依次找出json内容的含义。

最后我们可以找到新歌推荐的一些信息:

我们找到了歌曲uri和低质量音乐的信息。我们观察,可以发现上面Uri的后缀竟然和lMusic的id惊人的相似,我们不得不怀疑这个mp3地址是可以计算出来的。

我在分析一段时间后得出的结论就是,这个uri的确是计算出来的,方式如下:
1:前半段是固定字符串 http://m2.music.126.net/
2:中间部分是一段base64编码的字符串,别问为什么,很容易看出来。其内容以歌曲id对应的byte和key 异或之后得出的摘要
3:后段是歌曲id

实现方式如下:

String input = "2946691177993133"; String key = "3go8&$8*3*3h0k(2)2"; byte[] keyBytes = key.getBytes(); byte[] searchBytes = input.getBytes(); for (int i = 0; i < searchBytes.length; ++i) { searchBytes[i] ^= keyBytes[i % keyBytes.length]; } MessageDigest mdInst = MessageDigest.getInstance("MD5"); mdInst.update(searchBytes); byte[] result = Base64.getEncoder().encode(mdInst.digest()); String params = new String(result); params = params.replace("+", "-"); params = params.replace("/", "_"); System.out.println("http://m2.music.126.net/" + params + "/" + input + ".mp3");

run一遍,是不是已经成功了呢? 之后的博文我会陆续讲解如何使用查询api,敬请期待!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。