菜单

MasterXu
发布于 2021-06-21 / 1193 阅读 / 0 评论 / 0 点赞

记一次网课平台的加密视频下载

背景

最近正在学习 PMP ,在某个网课平台上学习,因为看视频的时候,视频表层会浮动一个含有个人信息的水印防止盗版。那就反而让我试试看能不能盗版你,嘿嘿

正题

首先,在播放器页面,先 F12 看看

image20210308005055066.png

这个看起来不是很对劲,在网上搜索一番以后,发现是 m3u8 形式,一般来说,可以在 Network 中看到 playlist(视频分块文件列表)

并且,在查找资料的过程中,发现 FFmpeg 是支持下载 m3u8 的视频并且转码的,所以接下来的问题就是,如何找到 playlist

在播放器页面调试工具中切到 Network 页面,仔细翻看一下

果不其然,看到了 playlist

image20210308005539396.png

查看 playlist 以后发现,视频是经过加密的,如下

image20210308005646122.png

Key 的地址在 Uri = 后面,查看 Network 以后,发现,播放器在获取了 playlist 后,请求了 Key,来康一康

image20210308005803811.png

简单,拿到 playlist 和 Key,我们把 playlist 和 Key 文件,单独成文件,按照 ffmpeg 的使用教程,把 m3u8 中的 Key uri 批量替换

image20210308010128512.png

然后在终端中使用命令

ffmpeg -allowed_extensions ALL -protocol_whitelist file,http,crypto,tcp -i m3u8.m3u8 test.mp4

本以为事情就是如此的简单,然而

image20210308010412436.png

继续搜索一番以后,发现,极有可能是 Key 错误,正常的 AES-128 Key,应该是 16 位,而在上面 Network 中请求获得的 Key 却是 20 位,显然是有问题的

推断极有可能是为了防止盗版,网课的播放器,对 Key 又进行了加密,拿到 Key 之后,经过内部解密以后,才用于解码视频流。

思索一番以后,觉得,既然要解密,那么解密的代码,肯定在 js 中。再次在浏览器中,翻看一下打开该页面时,加载的 js 代码(希望不要混淆。。。)

筛选了 js 之后,感觉只有这两个,是极有可能存在解密代码的

image20210308010929103.png

遗憾的是,果不其然,js 是经过混淆的,陷入僵局

思索一番以后,决定看看,如果抓包,将 Key 替换成错误的情况下,会出现什么情况,再通过错误的提示,或者控制台的错误,看能不能大致定位到解密部分的代码,干!

这里本来用的是 fiddler 抓包,然后在规则中,替换

image20210308012611077.png

结果,因为每次刷新页面,这个请求的 url 会不同,规则并不能生效,于是又是查找一番,找到一个 Python 的抓包库 https://github.com/qiyeboy/BaseProxy

根据教程稍微码一段

image20210308013658306.png

果然,出现了错误提示

image20210308013614739.png

根据提示在代码中查找一下这几个关键字

image20210308014004233.png

在 player.js 中搜索到 i18n,那么继续找英文这段

image20210308014106013.png

找到唯一一个,报这个错误的地方,但是,这也看不出啥啊,再次陷入僵局。

瞎看一段时间以后,发现

image20210308014302126.png

判断这个 player.js 应该就是一个公开的库,叫做 video.js ,一般来说,应该不会有开发者在这上面改代码,都直接拿来用,那么,推断,解密部分应该是在 index.js 中,解密玩以后,把参数给到 player.js 完成播放。

回到 index.js

查看一番以后,发现,很多类似 d.b.log() 一看就很像调试时 log 到控制台的代码,而且通过这些 log 应该可以推断出程序是如何运行的。

image20210308014739870.png

但是,实际查看控制台,确实空的

那么,我们搜索一下 log 这个函数

image20210308015004311.png

看到,虽然代码是被混淆的,但是这个 log 实际调用的应该就是 function a(),而 a 是空的,当然没有控制台输出。

那么如何才能让他输出呢,这个时候,fiddler 终于派上用场了,我们抓包,改掉 index.js ,在此处 a 的功能中,加入 console.error() 使用 error,只是为了更加明显一些。

image20210308015407518.png

保存,启用规则,刷新页面,此时需要注意,需要在浏览器开发者工具中,勾上 Disable Cache,防止浏览器加载本地缓存的 js

image20210308015551407.png

我们看到,代码已经注入了,再来 console 中看看

image20210308015713035.png

果然,已经输出了,此时,一看到 load Key 这部分,感觉不对劲,在代码中搜索一番

image20210308020005493.png

不管了,先把这里的 this 在控制台输出看看,判断应该会把解密以后的变量保存,在这里注入 d.b.log(this) ,依然使用 fiddler

image20210308020244734.png

注入以后,再到控制台查看 this 里面的东西,仔细查看一番以后,发现这个 this 中信息很全呀

image20210308020557849.png

看到了关键的 key,而且是 16 位,复制出来,转一下

image20210308020725298.png

这简直像极了 key,重新修改 key 之后,再次使用 ffmpeg 下载,嘿,成功

image20210308021127348.png

成功下载,并且可以播放

后续

咱们盗版也得提高效率,每个视频这么操作一波,岂不是很麻烦,不得完成一个批量操作,于是乎,自动化想法:

  • 继续注入 js 代码,在 function a 中,找到含有 key 的值,并且将 key post 出去
  • 用 Python 写一个小服务器,接收 js post 的 key
  • 使用 Python + baseproxy 完成 js 的注入,以及 m3u8 的 playlist 获取,也 post 到我们的小型服务器
  • 浏览器通过 Python + selenium 自动化操作,一个一个打开视频,完成批量 m3u8 以及对应 key 获取
  • 最后通过一个 Python 批量导入 m3u8 以及 key 完成下载

image20210308021823024.png

一番爆肝以后

image20210308022844117.png

运行一下

image20210308022320471.png

全部下载完成。搞定睡觉去


评论