加密M3U8下载解密流程分析

加密M3U8下载解密流程分析

记录一下获取某在线课堂网站的视频课程资源(M3U8)全过程。

参考资料:某网址视频下载解密流程分析及脚本实现 – 作者:6FyY2p-安全小百科

一、背景介绍

公司要求所有新员工进行在线课程学习,并配有在线考试。为达到更好的查资料学习效果,打算把视频课程下载到本地浏览;

已知非大型视频类网站的视频资源往往较容易获取,常见资源嗅探方式有:

  1. IDM浏览器扩展自带嗅探浮窗;
  2. 安装CocoCut等资源嗅探扩展,获取视频链接,再导入其他软件下载;
  3. 使用视频网站自带的下载功能;

其中IDM在非加密资源的下载体验是最好的(毕竟是下载器起家),但遇到带价密的M3U8文件则提示由于版权原因不支持下载,因此对于加密M3U8常用的下载软件有:

nilaoda大佬的N_m3u8DL-CLI:https://github.com/nilaoda/N_m3u8DL-CLI

ps:作者表示由于该项目耦合过多,难以扩展,因此项目已进入维护阶段,后续新的跨平台下载器的开发转至另一仓库:

https://github.com/nilaoda/N_m3u8DL-RE

二、实际操作

1. 分析视频格式

根据 CocoCut 的嗅探结果,该视频为 m3u8 格式,由结尾的_enc猜测带有加密;

2. 尝试常规方法下载

使用 N_m3u8DL_CLI 下载,下载窗口直接闪退,工作目录只留下raw.m3u8文件:

文件中的METHOD=AES-128IV=***KEYFORMAT=media-drm-token确定视频使用了 AES-128-CBC 加密

3. 寻找其他下载方式

1. 寻找 BaseUrl

BaseUrl 指 M3U8 流媒体各个视频分片(扩展名.ts)的前缀 URL,后续的下载需要使用 BaseUrl+URI 进行拼接(有的不需要);

在浏览器使用F12打开开发者工具,切换到“网络”选项卡,刷新页面并加载一小段,搜索ts

可以发现视频分片的Get请求地址和M3U8文件里的URI并不同,查看多个ts文件的请求地址前缀均为此,所以提取文件名之前的部分作为BaseUrl;

2. 构造请求Headers

从以上截图中还可以发现该视频网站的引用站点策略(Referrer Policy)设置为strict-origin-when-cross-origin用于防盗链,查看多个ts文件的请求Headers,在构造请求时添加额外值:

以N_m3u8DL_CLI为例,添加命令行参数:--headers ":authority:***|origin:***|referer:***"

3. 寻找视频解密Key

该部分为重点内容

根据以往的经验,加密M3U8文件一般在头部会注明获取key的URL,但从截图中发现此M3U8文件并未标注,因此在浏览器开发者工具中搜索videokey,果然发现一条记录:

此时如果直接使用该密钥进行解密,会提示失败,且解密后的文件仍然无法播放。查看该值对应的键名“encryptedVideoKey”即加密后的视频密钥,推测视频的真实密钥也被做了加密。

经过搜索,查找到相关博客(如开头所述),根据文中指示进行后续操作

在开发者工具-源代码标签页内右键顶级目录,选择“在所有文件中搜索”,查找encryptedVideoKey字样,得到以下结果:

此时不确定具体是哪个js脚本负责此页面的视频播放逻辑,因此全部代码格式化后在相应位置下断点:

1
2
3
4
5
6
7
8
9
10
11
12
if ("media-drm-token" === e.playlist.keyFormat || "media-drm-player-binding" === e.playlist.keyFormat) {
for (var o = JSON.parse(r), f = o.encryptedVideoKey, c = u["default"].utils.utf8.toBytes("72Fhskjglp8qjpqx"), h = new u["default"].ModeOfOperation.ecb(c), p = [], m = 0; m < f.length; m += 2)
p.push(parseInt(f[m] + f[m + 1], 16));
var y = h.decrypt(p)
, _ = u["default"].utils.utf8.fromBytes(y)
, v = function(e) {
for (var t = new ArrayBuffer(e.length), i = new Uint8Array(t), n = 0, r = e.length; n < r; n++)
i[n] = e.charCodeAt(n);
return t
}(_);
l = s = new DataView(v)
}

这段代码大致逻辑为:

  • 密钥c:硬编码字符串72Fhskjglp8qjpqx
  • 解密器h:AES-ECB模式;
  • f:加密后的视频密钥;

循环将 f 赋值到 p 中,解密器 h 使用 c 解密 p,解密结果为参数 y,即 y 为真正的视频密钥。

因此下断点后刷新页面,y 执行结束后在控制台输出该值:

从代码输出中可以证明,p 确实为加密后的密钥,而 y 则为解密后的视频密钥。

使用010Editor创建解密文档dec.key并写入密钥:

下载前的准备工作基本完成。

4. 下载视频

根据 N_m3u8DL_CLI 的文档,使用如下参数:

1
.\N_m3u8DL-CLI_v3.0.2.exe "https://***/f3cb79e1ac654fccae295a612b00be2e_1030324010_480p_enc.m3u8" --workDir "C:\Users\***\Downloads" --baseUrl "https://***/" --headers ":authority:***|origin:***|referer:***/" --useKeyFile "C:\Users\***\Desktop\dec.key" --useKeyIV "a3c41e5794ee05de82bf48e4654584e2"

运行后视频下载完成,并成功解密,可供本地观看。

作者

Luan Blesson

发布于

2022-09-14

更新于

2023-02-22

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×