通过抓包下载微信小程序中的视频

有这个需求是因为《无界·线上艺术》在春节上架了单雯和施夏明的传承版昆曲《牡丹亭》,是我一直在寻找的演出视频。她/他们是我眼中这个时代最好的杜丽娘和柳梦梅,一定要下载收入资源库。

单雯 施夏明 牡丹亭

我常用 You-Get 和 Downie 下载视频,它们支持几乎所有主流视频网站,而且 Downie 还可用 User-Guided Extraction 模式手动嗅探不支持网站的内容。但它们都需要视频页面的 URL 链接才能解析,而这个小程序播放器并非公开网页,没有暴露任何 URL,所以此路不通。

只剩下一种方式,抓包,无论软件如何隐藏都必须通过网络把服务器的源视频下载到本地才能播放,会产生大量 TCP 数据包,通过 Wireshark 追踪一定能找到源视频地址。主流视频平台一般采用分段、加密等方式增加解析难度,但我不觉得一个冷门的小公司产品会这么做,试试看。

Wireshark

首先解决如何用 Mac 端 Wireshark 捕捉手机端数据包的问题,一种方式是用连接有线网络的 Mac 开启网络共享创建 Wi-Fi 热点,手机连接此热点后产生的所有网络数据都会通过 Mac,即可被捕捉。另一种方式是先用 Charles 之类的软件在 Mac 上创建代理服务,然后同局域网内手机设置代理指向它实现转发数据包。但 iPhone 内置代理只有 HTTP Proxy,不借助第三方软件只能转发 HTTP 报文,而有些视频流并非 HTTP 协议,所以前者连接 Mac 热点更好些。

只有连接有线网络的 Mac 可以开启 Wi-Fi 共享:

mac sharing

手机播放视频发现 Wireshark 捕捉到大量与同一个 IP 交互的 TCP 数据包,上层是 HTTP 协议:

wireshark

Follow 它的 TCP Stream 发现明文 HTTP 请求:

wireshark

把 Host 和 GET 组合为视频 URL 地址:

http://130*********.vod2.myqcloud.com/32a5c501vodcq130*********/9111a3b75285890814193748493/************UA.mp4

从 HTTP 的 Rang 参数和 206 响应码看出客户端是在分段请求数据,视频总长度 1,313,589,669 字节,本次请求 0~262,143 字节。分段请求是播放器加载网络媒体的一种实现方式,根据进度条位置按需下载对应的数据段而非从头开始把整个视频下载下来,这种方式要求源视频必须是特殊的 Fragmented MP4。另一种常见方式是 HLS,先分割原视频生成作为索引的 M3U8 文件,客户端再按需请求这些小文件实现渐进式加载播放。

用浏览器打开 URL,运气不错,无须授权并且是未分割的完整视频,可以直接保存到本地。

单雯 牡丹亭

惊鸿一瞥,单雯和施夏明的【山桃红】后半段,我真的是第一次看到✨。

HTTPS

不过大部分情况从 Wireshark 看到的并不是 HTTP 明文,而是密密麻麻的 TLS,即加密 HTTPS:

wireshark

Follow 它的 TCP Stream 会看到加密后的 HTTP 报文,理所当然一片乱码,分辨不出有效信息:

wireshark

本以为 Wireshark 能像 Charles 那样把根证书导入系统再以中间人方式监听 HTTPS 通信,但搜索一番发现并不可以,它比 Charles 更底层而不能这样做。

仅有的两种方案在下载微信视频时都不可行:

  1. 要求提供服务端的 HTTPS 私钥(不可能拿到🤷🏻)
  2. 要求从浏览器导出 HTTPS 使用的实际通信密钥(微信小程序视频不可能用浏览器打开🤷🏻)

车到山前自有路,既然已经知道客户端是使用 HTTPS 分段下载视频,用 Charles 看看:

charles

成功解密,完整 URL 映入眼帘,左边一列是分段请求,Charles 用媒体标记证明它们就是下载的视频片段。

尾声

之前以为网络视频都是先在服务端被分割再供客户端点播,即 HLS 和 M3U8,原来也可以不分割,让客户端使用 HTTP 分段请求特性按需下载片段。这并非从书上看到的结论,而是在需求驱动下一步步剥茧抽丝发现的,探索过程十分有趣。

arrow_upward