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