Netflix 视频抓取

Widevine L3 刚破解的时候就有 Netflix 的 web-dl 流出了,不过那个时候这还是一项很“高端”的技术,这个核心技术只掌握在少数破解小组手中。随着 Widevine 的神秘面纱被一层层揭开,现在网上已经能找到可用的 chrome 插件获取当前网页上使用 Widevine 加密的视频文件的密匙了,如果我们获取了完整的加密视频文件和密匙,那么就可以用 ffmpeg 等工具去 DRM 保护了。

插件 widevine-l3-decryptor

这是一个发布在 github 上破解 chrome Widevine L3 的插件,原版的仓库由于收到了 google 的投诉,只剩下个说明文件了。不过好在有很多 forks 还保留了完整的代码。我已经下到本地保存了一分。这个插件直接 hook 了浏览器的 EME (Encrypted Media Extensions),只要浏览器在播放使用 Widevine 加密的视频,插件就会在控制台输出如下信息

1
WidevineDecryptor: Found key: 709487bfa80678a4fd32fd26cbf42b60 (KID=00000000056167750000000000000000)

简单说一下破解 Widevine L3 的原理。Widevine 加密本质上是 AES 加密,理论上黑盒 AES 加密是十分安全的,以目前计算机的算力是无法破解的。但是 Widevine L3 并不是黑盒 AES,而是完全的白盒 AES。所谓白盒,就是破解者能调用 AES 加(解)密程序,这样可以通过一个差分分析方法破解出 AES 密匙。Widevine 其实是分三个等级的,L1 和 L2 要求硬件支持,一般是 cpu 里面有个单独的安全模块用来处理AES 加(解)密,这个过程对破解者是不可见的,用上面的方法也没法破解的,只有 L3 是白盒 AES。这个插件是直接把 chrome 在 windows 设备上用于 Widevine 的密匙破解出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// The public 2048-bit RSA key Widevine uses for Chrome devices in L3, on Windows
WidevineCrypto.chromeRSAPublicKey =
`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtdHcRBiDWWxdJyKDLTPO9OTapumVnW+9g6k3RSflM0CESFEufZUJGC73UKe9e+u789HVZT04pB5or3WB0XOx
aOibJklLBkd7Yfn1OndVrenMKTE1F4/6jg5rmwyv4qFQ1u8M/ThZUrAgb8pTmKfb9vrv1V8AApwVzcQg3s48eESnKjBU99Vk8alPTjPSfOgoTDluGxQONWiwCaMwftNs
YrOzlde+V3UOb5FVzPcrOmaERfyujV3h4sHGRbTCsqYVwMalO7hmNmtemwt0xBuf5Juia7t1scuJypQ8lI1iEsB+JZVo3Uovfa9nNX0gl5TAq1tAh6M55/ttpWAirWHv
CQIDAQAB
-----END PUBLIC KEY-----`;

// The private 2048-bit RSA key Widevine uses for authenticating Chrome devices in L3, on Windows
// Extracted by applying some mathematical tricks to Arxan's white-box algorithm
WidevineCrypto.chromeRSAPrivateKey =
`-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC10dxEGINZbF0nIoMtM8705Nqm6ZWdb72DqTdFJ+UzQIRIUS59lQkYLvdQp71767vz0dVlPTikHmiv
dYHRc7Fo6JsmSUsGR3th+fU6d1Wt6cwpMTUXj/qODmubDK/ioVDW7wz9OFlSsCBvylOYp9v2+u/VXwACnBXNxCDezjx4RKcqMFT31WTxqU9OM9J86ChMOW4bFA41aLAJ
ozB+02xis7OV175XdQ5vkVXM9ys6ZoRF/K6NXeHiwcZFtMKyphXAxqU7uGY2a16bC3TEG5/km6Jru3Wxy4nKlDyUjWISwH4llWjdSi99r2c1fSCXlMCrW0CHoznn+22l
YCKtYe8JAgMBAAECggEAGOPDJvFCHd43PFG9qlTyylR/2CSWzigLRfhGsClfd24oDaxLVHav+YcIZRqpVkr1flGlyEeittjQ1OAdptoTGbzp7EpRQmlLqyRoHRpT+MxO
Hf91+KVFk+fGdEG+3CPgKKQt34Y0uByTPCpy2i10b7F3Xnq0Sicq1vG33DhYT9A/DRIjYr8Y0AVovq0VDjWqA1FW5OO9p7vky6e+PDMjSHucQ+uaLzVZSc7vWOh0tH5M
0GVk17YpBiB/iTpw4zBUIcaneQX3eaIfSCDHK0SCD6IRF7kl+uORzvWqiWlGzpdG2B96uyP4hd3WoPcZntM79PKm4dAotdgmalbueFJfpwKBgQDUy0EyA9Fq0aPF4LID
HqDPduIm4hEAZf6sQLd8Fe6ywM4p9KOEVx7YPaFxQHFSgIiWXswildPJl8Cg5cM2EyMU1tdn5xaR4VIDk8e2JEDfhPtaWskpJp2rU2wHvAXOeAES7UFMrkhKVqqVOdbo
IhlLdcYp5KxiJ3mwINSSO94ShwKBgQDavJvF+c8AINfCaMocUX0knXz+xCwdP430GoPQCHa1rUj5bZ3qn3XMwSWa57J4x3pVhYmgJv4jpEK+LBULFezNLV5N4C7vH63a
Zo4OF7IUedFBS5B508yAq7RiPhN2VOC8LRdDh5oqnFufjafF82y9d+/czCrVIG43D+KO2j4F7wKBgDg/HZWF0tYEYeDNGuCeOO19xBt5B/tt+lo3pQhkl7qiIhyO8KXr
jVilOcZAvXOMTA5LMnQ13ExeE2m0MdxaRJyeiUOKnrmisFYHuvNXM9qhQPtKIgABmA2QOG728SX5LHd/RRJqwur7a42UQ00Krlr235F1Q2eSfaTjmKyqrHGDAoGAOTrd
2ueoZFUzfnciYlRj1L+r45B6JlDpmDOTx0tfm9sx26j1h1yfWqoyZ5w1kupGNLgSsSdimPqyR8WK3/KlmW1EXkXIoeH8/8aTZlaGzlqtCFN4ApgKyqOiN44cU3qTrkhx
7MY+7OUqB83tVpqBGfWWeYOltUud6qQqV8v8LFsCgYEAnOq+Ls83CaHIWCjpVfiWC+R7mqW+ql1OGtoaajtA4AzhXzX8HIXpYjupPBlXlQ1FFfPem6jwa1UTZf8CpIb8
pPULAN9ZRrxG8V+bvkZWVREPTZj7xPCwPaZHNKoAmi3Dbv7S5SEYDbBX/NyPCLE4sj/AgTPbUsUtaiw5TvrPsFE=
-----END PRIVATE KEY-----`;

Widevine L3 的破解是 Netflix web-dl 的基石,理论上所有使用 Widevine DRM 技术的视频网站都可以用它来获取密匙,剩下的工作就是解析出加密视频的下载地址。

解析 Netflix 视频下载地址

Netflix 的网页 App 和一般的视频网站还不一样,它使用的一个 MSL (Message Security Layer) 控制客户端与服务器的通讯。如果像往常一样用 http 抓包工具来分析 Netflix 的工作流程的话,你会发现几乎每个 response 内容都是一串 BASE64 编码的字符串,完全没有 JSON 这样可读的内容。好在 Netflix 的 MSL 也被大家研究透了(更何况它还是 Netflix 在 github 上的一个开源项目), 最简单的一个办法,是我在一个下载 Netflix 字幕的油猴插件上看到的,直接 hook javascript 内置的 JSON 的两个方法,代码片段如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const injection = () => {
const WEBVTT = 'webvtt-lssdh-ios8';
const MANIFEST_URL = "manifest";
const forceSubs = localStorage.getItem('NSD_force-all-lang') !== 'false';

// hijack JSON.parse and JSON.stringify functions
((parse, stringify) => {
JSON.parse = function (text) {
const data = parse(text);
if (data && data.result && data.result.timedtexttracks && data.result.movieId) {
window.dispatchEvent(new CustomEvent('netflix_sub_downloader_data', {detail: data.result}));
console.log('manifest:')
console.log(data)
console.log(stringify(data))
}
return data;
};
JSON.stringify = function (data) {
if (data && typeof data.url === 'string' && data.url.indexOf(MANIFEST_URL) > -1) {
for (let v of Object.values(data)) {
try {
if (v.profiles)
v.profiles.unshift(WEBVTT);
if (v.showAllSubDubTracks != null && forceSubs)
v.showAllSubDubTracks = true;
}
catch (e) {
if (e instanceof TypeError)
continue;
else
throw e;
}
}
console.log('manifest_req:')
console.log(data)
}
if(data && typeof data.movieId === 'number') {
try {
let videoId = data.params.sessionParams.uiplaycontext.video_id;
if(typeof videoId === 'number' && videoId !== data.movieId)
window.dispatchEvent(new CustomEvent('netflix_sub_downloader_data', {detail: {id_override: [videoId, data.movieId]}}));
}
catch(ignore) {}
}
return stringify(data);
};
})(JSON.parse, JSON.stringify);
}

任你 MSL 多复杂,它在发送数据之前会调用 JSON.stringify,接收数据之后会调用 JSON.parse,我们直接把这两个函数给替换了,让它在接收到关键信息之后向 console 输出就行了。在这里关键信息就是 Netflix 播放器用来播放视频的 manifest, 这个 manifest 包含了所有我们需要的视频、音频、字幕信息(下载地址)。在这里就不细说 manifest 内容的结构了,都是人类可读的信息,找出视频下载地址没什么难度。另外说明下,Netflix 只对视频加密了,音频和字幕都是直接可用的。

高品质音视频的获取

本来到这里应该是万事大吉了,视频地址也解析出来了,解密密匙也获取了。用 ffmpeg 或者 bento4 这类工具就能去 DRM 了。但是,我要告诉你的是,用 chrome 浏览器看 Netflix 最高只有 720p,AAC 2.0 音质。但实际上我们用点小技巧,也能让 chrome 也获得 1080p 的视频以及高规格的音频。这个插件就是 netflix-1080p,托管在 github 上。原仓库很久不更新了,用不了。但是你多做一些搜索,就会发现能用的。更重要的是这个仓库还提供了手动的 MSL requester,可以发自己构造的请求,然后解析出 manifest。原仓库的虽然不能用,但是给我们提供了一个很好的样板。

获取高品质的音视频 manifest 的关键在于改写 Netflix 原版的 cadmium player。Netflix 会根据我们所用的平台(在这里是 chrome)生成 MSL request 发送给服务器,然后服务器返回 manifest信息。原版的 player 看见我们用的是 chrome,于是就只请求 720p 视频和 AAC 2.0 音频了,典型的是瞧不起我们 chrome 用户。但实际上 Netflix 准备很多规格的视频 (avc 1080p / hevc 1080p / 部分 hevc 2160p)和音频 (AAC 5.1 / DDP 5.1 / 部分 DDP ATOMS),而其中有一部分是可以通过改写 cadmium player 就能获取的,并且在 chrome 上就能播放。另外一些能获取,但是 chrome 不能播放,但是这不妨碍我们下载这些内容。改写片段(112507行左右)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
viewableId: n,
// profiles: g,
profiles: [
"playready-h264mpl30-dash",
"playready-h264mpl31-dash",
"playready-h264mpl40-dash",

"playready-h264hpl30-dash",
"playready-h264hpl31-dash",
"playready-h264hpl40-dash",

// "vp9-profile0-L21-dash-cenc",
// "vp9-profile0-L30-dash-cenc",
// "vp9-profile0-L31-dash-cenc",
// "vp9-profile0-L40-dash-cenc",

// "vp9-profile2-L30-dash-cenc-prk",
// "vp9-profile2-L31-dash-cenc-prk",
// "vp9-profile2-L40-dash-cenc-prk",
// "vp9-profile2-L50-dash-cenc-prk",
// "vp9-profile2-L51-dash-cenc-prk",

// "ddplus-5.1hq-dash",
// "ddplus-atoms-dash",
"heaac-2-dash",
"heaac-2hq-dash",
"heaac-5.1-dash",

"simplesdh",
"nflx-cmisc",
"BIF240",
"BIF320"
],

目前我测试的结果是 avc 1080p 能获取,且能播放; aac 音频能获取且能播放;DDP 音频能获取但是不能播放。同样字幕的获取也可以通过这种方法,不过前面已经介绍了一个油猴插件,用那个插件更简单(原理是一样的)。

MKV 封装

相关链接

r/Piracy 帖子 Widevine-L3-Decryptor - A Chrome Extension That Demonstrates Bypassing Widevine L3 DRM
github netflix-1080p 插件1
github netflix-1080p 插件2
github widevine-l3-decryptor 插件1
github widevine-l3-decryptor 插件2(可用)
油猴脚本 netflix-subtitle-downloader