看雪学院 09月12日
DRM 视频解密流程解析
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了 DRM 视频解密密钥的获取与整体流程,主要涉及浏览器中的 EME 和 CDM 模块,以及 MSE 动态加载媒体数据的解密过程。内容不含具体实现与算法分析,旨在帮助读者理解 DRM 视频解密的原理和步骤。

🔑 EME 和 CDM 模块:现代浏览器内置 EME (Encrypted Media Extensions) 标准 API,允许网页与 CDM (Content Decryption Module) 通信,而 CDM 负责实际解密。不同浏览器或操作系统使用不同的 CDM,例如 Chrome 使用 Google Widevine,Safari 使用 Apple FairPlay,Edge 使用 Microsoft PlayReady。

📈 MSE 动态加载:对于 DASH 等自适应流媒体,需要手动加载和解析 MPD 文件,获取视频编码信息、码率、初始化片段和媒体片段 URL。然后通过 MSE (Media Source Extensions) 动态提供媒体数据,将加密片段按顺序 append 到 SourceBuffer 中。

🔑 EME 手动解密流程:选择 DRM 系统,创建 MediaKeys 对象并与 Video 元素关联。监听 'encrypted' 事件,生成许可证请求并发送到许可证服务器。接收许可证并更新 MediaKeySession,CDM 解密媒体片段,视频即可播放。

🔍 接口/方法/事件:包括 navigator.requestMediaKeySystemAccess()、MediaKeySystemAccess.createMediaKeys()、video.setMediaKeys()、mediaKeys.createSession()、video.addEventListener('encrypted', ...)、session.generateRequest()、session.addEventListener('message', ...)、fetch() 和 session.update() 等,用于实现 EME 解密流程。

mb_jepgtozh 2025-09-05 18:08 上海

看雪论坛作者ID:mb_jepgtozh

本文仅讲述 DRM 视频解密密钥的获取与整体流程简介,不包含任何实现与算法分析。
DRM 在浏览器上的实现

01

该部分内容均为 AI 编写浏览器角色: 现代浏览器 (如 Chrome, Firefox, Edge) 都内置了“加密媒体扩展” (Encrypted Media Extensions, EME)。EME 是一个 W3C 标准的 API,它允许网页通过 JavaScript 与浏览器的内容解密模块 (Content Decryption Module, CDM) 进行通信,而无需了解解密过程本身。

CDM (内容解密模块): 这是浏览器中真正负责解密的部分。每个浏览器或操作系统都有自己的 CDM,用于处理特定的 DRM 系统。

例如:

Google Widevine: 通常用于 Chrome 浏览器和 Android 设备。

Apple FairPlay: 用于 Safari 浏览器和 iOS/macOS 设备。

Microsoft PlayReady: 用于 Edge 浏览器和 Windows 设备。

这里我们只需要了解 DRM 用到了 EME 和 CDM 模块即可。

原生播放的两个核心步骤对于 DASH 这种自适应流媒体,你不能像播放普通 MP4 一样直接设置 video.src = "video.mp4"。你必须:

1.手动加载和解析 MPD 文件:用 fetch 获取 .mpd 清单文件,然后用 JavaScript 解析这个 XML 文件,找出视频的编码信息、码率、初始化片段和媒体片段的 URL。

2.通过 MSE 动态提供媒体数据

◆创建一个 MediaSource 对象。

◆将这个对象的 URL 赋给 <video> 元素的 src。

◆在 MediaSource 对象上创建一个 SourceBuffer

◆手动下载视频的初始化片段和后续的媒体片段,然后按顺序将它们 appendBuffer() 到 SourceBuffer 中。

3.通过 EME 处理加密:在向 SourceBuffer 推送加密片段时,浏览器会触发事件,此时你需要启动 EME 流程来获取解密密钥。

EME 手动解密流程详解这是不使用库时最复杂的部分。整个过程是一个精确的“握手”协议:

1.选择 DRM 系统:

◆使用 navigator.requestMediaKeySystemAccess('com.widevine.alpha', ...) 来询问浏览器是否支持 Widevine。

2.创建 MediaKeys:

◆如果支持,就调用 createMediaKeys() 来创建一个 MediaKeys 对象。这个对象代表了浏览器中的 CDM(内容解密模块)。

3.关联到 Video 元素:

◆调用 video.setMediaKeys(mediaKeys),将这个 MediaKeys 对象与你的 <video> 元素关联起来。

4.监听加密事件:

◆当你通过 MSE 的 appendBuffer() 向 SourceBuffer 推送了一个加密的媒体片段后,MediaSource 会触发一个 'encrypted' 事件。

5.生成许可证请求:

◆在这个事件的处理器中,你会得到 initData(初始化数据,通常来自 MPD 或媒体片段的'moov'/'moof'盒子)。

◆使用 mediaKeys.createSession() 创建一个许可证会话 (MediaKeySession)。

◆调用 session.generateRequest('cenc', initData) 来生成一个许可证请求。

6.发送请求到许可证服务器:

generateRequest 是一个Promise,完成后,session 对象会触发一个 'message' 事件。

◆这个事件的 message 属性就是需要发送给Widevine许可证服务器的请求体 (Challenge)

◆使用 fetch 将这个请求体(通常是二进制的 ArrayBuffer)POST 到你的许可证服务器 URL。

7.提供许可证给 CDM:

◆fetch 请求会收到许可证服务器返回的许可证 (License)

◆调用 session.update(license),将这个许可证交给 MediaKeySession。

8.解密和播放:

◆一旦 CDM 成功处理了你提供的许可证,它就拥有了解密密钥。

◆现在,当后续的加密媒体片段被推送到 SourceBuffer 时,CDM 就可以在后台自动解密它们,然后视频就能正常播放了。

接口/方法/事件作用navigator.requestMediaKeySystemAccess()入口函数

。询问浏览器是否支持特定的 DRM 系统(如 'com.widevine.alpha')和视频格式。这是所有操作的第一步。

MediaKeySystemAccess.createMediaKeys()

如果上一步成功,调用此方法来创建一个 MediaKeys 对象,这个对象就代表了 CDM 的一个实例。

video.setMediaKeys()

将创建好的 MediaKeys 对象与你的 <video> 元素进行关联,告诉浏览器“这个视频的解密工作由这个 CDM 负责”。

mediaKeys.createSession()

创建一个 MediaKeySession 对象。一个会话代表一次完整的许可证获取流程。

video.addEventListener('encrypted', ...)关键事件监听器

。当 MSE 收到加密数据时,<video> 元素会触发这个事件。这是从“播放”转向“请求解密”的信号。

session.generateRequest()

在 'encrypted' 事件处理器中调用。它告诉 CDM:“根据我给你的初始化数据 (initData),请生成一个许可证请求(Challenge)。”

session.addEventListener('message', ...)关键事件监听器

。当 CDM 成功生成许可证请求后,会触发这个事件。事件的 message 属性就是需要发送给许可证服务器的二进制数据。

fetch()

使用标准的 fetch API,将从 'message' 事件中获取的请求体 POST 到你的许可证服务器 URL。

session.update()关键方法

。当你从许可证服务器那里 fetch 到了许可证 (License) 之后,调用这个方法,将许可证交给 CDM。CDM 处理后,就拥有了真正的解密密钥。握手至此成功

请求列表

02

https://api.***.com/pugv/player/web/playurl?avid=…&drm_tech_type=2

https://bvc-drm.bilivideo.com/cer/***_certificate.bin

https://bvc-drm.bilivideo.com/bili_widevine

https://api.***i.com/pugv/player/web/playurl?avid=…

https://s1.hdslb.com/bfs/static/player/main/widgets/npd.drm_sdk.7d8e1e5f.js

https://bvc-drm.bilivideo.com/cer/bilidrm_pub.key

https://bvc-drm.bilivideo.com/bilidrm


playurl的请求里可以发现多出了一些奇怪的字段drm_typewidevine_pssh

分析

03

widevine_pssh(playurl&drm_tech_type=2)加载请求中的 widevine_pssh 数据到每个音频 / 视频流数据上

该数据将作为KeySystemInitData

其中KeySystem可能是其中的一个(根据环境变化)

this.CLEARKEY_KEYSTEM_STRING = "org.w3.clearkey",this.WIDEVINE_KEYSTEM_STRING = "com.widevine.alpha",this.PLAYREADY_KEYSTEM_STRING = "com.microsoft.playready"

通过以下方式解码,注意该处会先调用所有可用的 KeySystem 进行InitData(测试哪些可用)

这里可以把所有的 KeySystem 都断点

***_certificate.bin在请求结果处理函数中,我们注意到有一个 setMpdBody 的函数,很明显是用于设定 Mpd 元信息的函数。

跟踪进去

attachExternal调用attachSourceProxy

attachSourceProxy内,判断视频流的格式,然后第一次进入该函数会请求***_certificate.bin也就是证书文件。

获取到证书文件后 base64 作为serverCertificate

bili_widevine,playurl然后 POST 请求bili_widevine接口得到一个奇怪的字符串(请求数据 TODO)

在请求完成回调处断点,我们会得到这样的东西

接着他会进入updateKeySession函数,该函数就是更新 kid 与 key 的地方。

只不过传入以上数据它并不是合法的,所以我们会等到第二次调用这个函数,第二次调用才会传入正确值。

抛出错误后他会再进行一次请求,不过这次请求的接口是playurl,请求函数见上。

接着它就能获取到kid(从playurl接口得到),注意对比两次的 playurl 请求会发现第一次请求是没有bilidrm_uri而只有widevine_pssh的,第二次则相反,因为第二次请求没有加&drm_tech_type=2的参数。

该值是获取bilidrm_uri的最后一个//后的文本得出的,而该值从得出:

npd.drm_sdk.7d8e1e5f.js,bilidrm_pub.key,bilidrm最后获取 key 的函数均在getKeyDetail内,比较简单,不做分析。

最后我们能够得到

{"osStatus":0,"iv":"pIEmSahBQLWILQEUa+yFEw==","key":"c6xChuWnTweKvL8/j0Cm8A==","protectionData":{"org.w3.clearkey":{"clearkeys":{"2PZrk9soSYS05_xQ1xJ4_w":"c6xChuWnTweKvL8_j0Cm8A"},"priority":0}}}

使用 clearkeys 解密即可。

测试

04

使用 bbdown 下载视频流和音频流(注意不要合并)

BBDown.exe https://www.***.com/cheese/play/ep1302284 --skip-mux

kid 和 key 解码 base64 (注意是 url safe )

d8f66b93db284984b4e7fc50d71278ff:73ac4286e5a74f078abcbf3f8f40a6f0

mp4decrypt.exe --key d8f66b93db284984b4e7fc50d71278ff:73ac4286e5a74f078abcbf3f8f40a6f0 "voice.m4a" "voice_.m4a"mp4decrypt.exe --key d8f66b93db284984b4e7fc50d71278ff:73ac4286e5a74f078abcbf3f8f40a6f0 "video.mp4" "video_.mp4"

ffmpeg.exe -i voice_.m4a -i video_.mp4 -c:v copy -c:a copy output.mp4


ref

05

https://www.bento4.com/developers/dash/encryption_and_drm/

https://github.com/nilaoda/BBDown

看雪ID:mb_jepgtozh

https://bbs.kanxue.com/user-home-1022681.htm

*本文为看雪论坛优秀文章,由 mb_jepgtozh 原创,转载请注明来自看雪社区

议题征集倒计时!看雪·第九届安全开发者峰会(SDC 2025)

# 往期推荐

实现简易ARK工具:遍历进程和内核模块

Gepys木马分析与还原C代码

UNC2891银行攻防战:ATM物理后门与Linux反取证揭秘

CVE-2014-0038内核漏洞exp分析

APP登录请求之协议分析

球分享

球点赞

球在看

点击阅读原文查看更多

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

DRM 视频解密 EME CDM MSE
相关文章