一个学习网站的视频下载流程解析

前言

昨晚在群里看到一个下载网站限时视频的需求,要来网站稍微分析了一下,发现有点意思,不能直接 ffmpeg 下载,需要在请求头添加加密参数才可以,而且有时间限制,稍微慢一下就报 403,下面开始分析。

寻找加密请求来源

首先打开控制台抓包过滤 XHR 请求,如图:

image-20201112195600408

根据 URL 后缀的 m3u8 和 .ts 初步分析视频是用了 m3u8 格式,m3u8 是一个文本文件,是一个存储了所有的 ts 视频片段信息的索引,长这样:

image-20201112195952254

后续浏览器会根据这个索引文件自动在合适的时间下载合适的 ts 文件,然后就可以不间断的播放视频了。

扯远了。。。回到网站上来。

大概浏览了下,发现好多个请求都带有这几个请求头,初步判断请求头中的这几个参数是用来加密和校验的。

image-20201112200218233

所以问题就变成了找到这几个请求头的计算方法。

寻找请求头的计算方法

寻找x-time

老样子,全局搜索,只有三处,大概结构都一样,全部打上断点,如图:

image-20201112200637462

刷新网页,断点断下,正好这几个参数都在一起,不用挨个找了,如图:

image-20201112200836259

首选看最短的 x-time 参数,也就是 r=Math.round((new Date).getTime() / 1e3).toString(16),由一个时间戳转成十六进制而来。

寻找x-token

再来看 x-token,也就是 n 参数,由两个字符串拼接在一起后通过 u 函数计算之后全部转为小写后得到。目前不知道这个加密参数 t 从何而来,先看下 u 函数是怎么计算的,单步进入,如图:

image-20201112201327338

到了这个位置,发现 e 就是前面拼接的字符串,t 未定义,经过 s 函数计算后得到转码为字符串后返回。

image-20201112201634573

看一眼 s 函数长什么样:

image-20201112201817605

逻辑很复杂,而且涉及到大量的位运算,一般这种我都会第一时间试下 md5,SHA1 这种哈希运算,结果如图:

image-20201112202017990

image-20201112202120713

结果是一致的,所以这里就不用跟进去调试代码了,直接跳出函数即可,到这里我们知道 n 参数也就是 x-token 参数的值是由一个加密的 token 和时间戳的十六进制形式拼接在一起后做 MD5 运算得来的。

接下来寻找加密的 token,也就是 t = h.a.tokenKey 是怎么来的。

往上翻代码,翻到本代码段的开头,可以看到 h 是在这里初始化的,如图:

image-20201112202903514

下断点,刷新网页,断点断下,进入 r 函数查看,在函数开头打一个条件断点,如图:

image-20201112203542141

因为 h 的值是执行了 r 函数并且传入 23 这个参数得到的,所以这里只需要看 t 为 23 时的函数返回值。在右侧可以看到 r 为一个数组,索引值为 23 的数据是一个 json 格式的数据,里面包含了好多数据,其中就有我们需要的 tokenKey。

image-20201112203905595

到了这里问题又来了,这个巨大的 r 数组是从何而来的呢?目前还没有头绪,我选择随意翻翻之前的请求和 HTML 源代码找找有没有线索。顺便查了查函数调用栈,发现好多地方都调用了这个 r 数组,根据不同的索引值从里面获取了不同的数据。

调试了一番,发现在代码初始化时这个 r 数组是空的,随着代码的运行每次调用这个函数都会判断 r 数组中是否有对应的索引数据,如果没有的话会往下进行,调用 e 数组中对应的函数将 r 数组进行初始化,之后再调用此函数时就可以直接返回不需要计算了,如图:

image-20201112210331179

由于我们需要索引为 23 时函数的返回值,那么我们只需要知道在 t 等于 23 时会调用什么函数然后去对应函数中下断点即可。

e 这个数组是提前生成好的,在 e 数组中找到索引值为 23 的函数点击跳转过去就是我们要找的函数了,如图:

image-20201112211854671

跳转过来了,发现这个 tokenKey 竟然是硬编码进去的,这就比较坑了,如图:

image-20201112212029643

到此我们已经成功的得到了 x-token 的加密方法。

寻找x-sa

还差最后一个 x-sa 参数未找到,继续下断点,如图:

image-20201112212508763

断下后可以看到 Object(p.u) 为一个函数调用,传入的参数为 i 和 a。i 和 a 都是通过调用函数获取到的,实测两个参数获取到的值是一样的,并且多次请求不会改变,再结合当前分析的这个 JS 文件应该是一个埋点 SDK,所以猜测这两个参数应该是浏览器指纹信息,就不深究它的生成过程了。

下面来分析 encrypt 函数的加密流程,如图:

image-20201112215426435

看到了熟悉的 AES 加密,按照加密模式和密钥将数据加密试试,如图:

image-20201112215723511

找一个 AES 加密的网站,计算结果,对比,如图:

image-20201112215806925

OK,计算结果是一致的,那么 x-sa 参数就是使用浏览器指纹构造的 json 字符串通过 AES 加密得到的结果。

代码验证

根据以上逻辑写出请求代码,可以正常获取所有的视频信息或者某个视频的详细播放链接,如图:

image-20201112222729111

视频下载

通过遍历视频链接,实测可以获取所有视频信息,如图:

image-20201113123352610

通过分段下载并且拼接的方式可以下载全部视频,如图:

image-20201113140724363

总结

以上分析流程仅限于研究和学习 JS 逆向。

注:以上分析流程及结果仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果与作者无关。

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接https://blog.d77.xyz/archives/f8095adb.html