播放
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 | checkRs($_POST['rs']) OR die('{"code":2,"message":"ILLEGAL_REQUEST_0"}'); |
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 | https://mv.ichunqiu.com/56739/56741/56743_d/480/567430.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解析后的资源了,往上翻翻解析过程,写出解析代码(有读者发现这个解析代码不太完整,直接执行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 | r:Uint32Array(4) [2578923527, 3055898187, 3519471670, 835375451] |
猜一下,99b74007b6254e4bd1c6e03631cad15b
长32位,分四组转dec
那么r就是iv,那么还原n,得到d318de6d2d064a2f870a4bd87089d412
,搞定
已经到了这一步了,我们找一下n的来源
再次,追到这里,能看到请求得到的data/key
给一个条件断点,方便下次追到这里
追了半天没追到,但是解密的key是放在某个变量的.key.bytes
里的,全局搜一下,在10205行找到生成情况,那就在这里断下,看一下如何生成的
追个p
1 | var p = new DataView(new Int8Array(hexAesStr(function (e, t) { |
再拦下来,找找调用栈,找找所需变量,然后整合出来
ok,随便找个视频试一下先,抓个包
然后用phantomjs跑一下,ffmpeg转一下
小事情,搞定。
发现一个通用key,应该是用于解免费视频的ad5192cbc9bd44a0849159f888be09d6