网易云音乐API 分析

版权声明:本文为博主原创文章,未经博主允许不得转载。转载请注明来自:http://blog.csdn.net/u013022222 https://blog.csdn.net/u013022222/article/details/51707352

这几天一直在做一个仿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,敬请期待!

没有更多推荐了,返回首页