ichunqiu下载器

播放

ichunqiu视频解析分三个部分,按照时间加载顺序,先是m3u8文件,然后是key文件,然后是ts文件

m3u8

m3u8文件决定了加载视频的清晰度,链接比如https://www.ichunqiu.com/video/info/56743.m3u8?type=2,后面的type=2决定了当前请求加载的是普通画质,不同的type返回内容不同,相同type每次返回信息一致,block掉之后不加载key和ts文件

请求参数

1
curl "https://www.ichunqiu.com/video/info/56743.m3u8" -H "Cookie: ci_session=2edb2d3e0b39270896c7523b694bed64b6941012" -H "SIGN: e554e37d32cdd990d3f4a9622fa7b06deaffe45a" --data "rs=fc2fa685c8dbc5c9be48b9631ed0700e&type=2"

根据测试,写出伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
checkRs($_POST['rs']) OR die('{"code":2,"message":"ILLEGAL_REQUEST_0"}');
checkSign(getallheaders('SIGN')) OR die('{"code":2,"message":"ILLEGAL_REQUEST_2"}');

switch($_POST['type']){
case 1:
$type = 1;
break;
case 2:
$type = 2;
break;
case 3:
$type = 3;
break;
default:
die('{"code":2,"message":"ILLEGAL_REQUEST_2"}');
break;
}

checkType($type) AND check($_COOKIE['ci_session']) OR die('{"code":1,"message":"ok","data":{"status":1,"info":"未购买课程不支持观看"}}');

rs SIGN 和type绑定,和视频id无关,也就是说不同rs SIGN,返回m3u8不同,但应该都可以用(没试过 应该可以)

请求中有四个关键参数,按后台检查顺序排列,分别是body中的rs header中的SIGN body中的type,Cookie中的关键是ci_session,url上的type是可以忽略的,主要是前端使用,后端不在乎这个参数

m3u8和key文件各自独立,没有强制性关联,也就是课程id相同时,一个请求得到的m3u8文件和另一个请求得到的key是可以一起用的

按照这个情况,m3u8中很可能给出了应该加载哪个ts的信息,或者给出了其他信息,应该有一个原因使它必须位于前面,应该不单纯只是判断是否登录用的,因为返回数据挺多的

内容大致如下

1
{"code":1,"message":"ok","data":{"status":0,"info":"ok","data":"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"}}

key

其次是key,链接比如https://www.ichunqiu.com/video/key/56743,只要请求一次,后期可一直使用,切换清晰度的时候不再重复请求,不同次请求,key是会变化的,但都可以使用,block掉之后,会发出一个ts请求,接着马上cancel掉

请求参数

1
curl https://www.ichunqiu.com/video/key/57759 -H 'Cookie: ci_session=33fbc6a5fd137a8a86fe8eea01f31621b17be982;'

那么,按照key这个起名来说,很可能是ts视频解密密钥,那么也就是说,同一节课程使用同一个key,无论清晰度如何。
key是会变化的,但是ts m3u8都是不变化的

内容大致如下

1
{"code":1,"message":"ok","data":"UVIHBQtQAlUFNDkMBAUMBVVSDQANA1ZdX1wLCgNSVgwCVw==","key":"UwIBVVQGUQ9QNDlUA1cKBwwEAVADUAcHXAZWVFIFUgBWUA==","t":"10-04-39"}
1
{"code":1,"message":"ok","data":"VVZWVQBWUFACDgRRUlVZAQxWDAU1NAELBVwBXFFXUQ4CAw==","key":"VwZQBV8AAwpXVgMDVFcAVwAGAlY1NFBRBgZcAgAAVQJWBA==","t":"10-38-53"}

ts

一个是ts文件,是加密的视频,比如课程为56739,链接形如

1
2
3
https://mv.ichunqiu.com/56739/56741/56743_d/480/567430.ts
https://mv.ichunqiu.com/56739/56741/56743_d/480/567431.ts
……

测试

由于前后存在顺序关系,以此作为间断,开启xhr请求拦截,检查在m3u8和key两个请求之间js进行的操作,或许可以找到对m3u8的解析操作

m3u8请求生成

通过分析代码 https://static2.ichunqiu.com/icq/resources/js/video/ichunqiu-media-play.min.js?v=f54073ca87 ,写出代码得到rs SIGN type,cookie由登录时获取,由此拿到四个凭证

生成SIGN的时候有这个字符串,因为资源是放在https://static2.ichunqiu.com域名下的,所以应该是写死的

m3u8解析

在dom中找到了这个blob

然后在网络标签里看到了类似的

最后面有这么一段

看到了"m3u8-parser": 31,,想起https://static2.ichunqiu.com/icq/resources/js/video/ichunqiu-media-play.min.js中也有类似的数字

应该是这里了,下个断点,把a.default换成a.default = function(){debugger;},运行,断在了这里,应该是猜对了,m3u8里存的是视频信息

m3u8.plain

应该就是m3u8解析后的资源了,往上翻翻解析过程,写出解析代码(有读者发现这个解析代码不太完整,直接执行ichunqiu-media-play.min.js吧)。

视频解析

按照这个情况来看,前面的猜想都是对的,那么key文件就是解析ts用的了。

然后Google一下m3u8的相关信息,看看这篇文章 http://www.voidcn.com/article/p-qiyqwfvs-bsc.html

很接近成功了,但是这边还是报错,感觉是key文件的问题,一般来说只有一个key,这里给了key还给了data,另外还是base64的,猜了一会没猜出来咋弄的,我再跟一下js,看看对key做了啥预处理

ffmpeg -protocol_whitelist "file,http,https,tcp,tls,crypto" -i m.m3u8 -c copy new.mp4

但是没有找到,找了篇文章参考 http://forum.toyoudo.com/d/7

这个就成功了,应该还是key的问题

然后在blob流中找到了ts数据

t是ts数据,另外两个是n和r,应该是iv和key,但不知道确切的对应和转换方式,已经知道iv出来是0x99b74007b6254e4bd1c6e03631cad15b,而

1
2
r:Uint32Array(4) [2578923527, 3055898187, 3519471670, 835375451]
n:Uint32Array(4) [3541622381, 755386927, 2265598936, 1888080914]

猜一下,99b74007b6254e4bd1c6e03631cad15b长32位,分四组转dec

那么r就是iv,那么还原n,得到d318de6d2d064a2f870a4bd87089d412,搞定

已经到了这一步了,我们找一下n的来源

再次,追到这里,能看到请求得到的data/key

给一个条件断点,方便下次追到这里

追了半天没追到,但是解密的key是放在某个变量的.key.bytes里的,全局搜一下,在10205行找到生成情况,那就在这里断下,看一下如何生成的

追个p

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var p = new DataView(new Int8Array(hexAesStr(function (e, t) {
var i,
n,
r,
a,
s = (i = parseInt(response.t.split("-")[0]),
n = parseInt(response.t.split("-")[1]),
r = parseInt(response.t.split("-")[2]),
((a = 3600 * i + 60 * n + r) - a % 180) / 180 % 128),
o = sprintf("%02x", s);
if (o != e.substr(s % 32, 2))
return $.fn_ajax({
url: base_url + "Common/ajaxErrorUpload",
options: {
os: "Invalid HLS key",
method: "video/key",
description: JSON.stringify({
date: new Date,
str: JSON.stringify(response)
})
},
callBack: function (e) {}
}),
"";
var l = e.replace("", e.substr(s % 32, 2)),
u = e.substr(0, s % 32),
c = e.substr(s % 32, e.length);
l = u + (c = c.replace(c.substr(0, 2), ""));
for (var d = "", h = "", p = 0; p < l.length; p += 2)
d += f(l.substr(p, 2), h = m("" == h ? o : h, t));
return d
}
(Base64.decode(response.data), o))).buffer);

再拦下来,找找调用栈,找找所需变量,然后整合出来

ok,随便找个视频试一下先,抓个包

然后用phantomjs跑一下,ffmpeg转一下

小事情,搞定。

发现一个通用key,应该是用于解免费视频的ad5192cbc9bd44a0849159f888be09d6

Author: hundan
Link: https://hundan.org/2019/01/09/ichunqiu下载器/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.