网站有两种 DRM:Widevine 和 biliDRM(clearkey)
1. 获取 Key
单纯拿 key 都很简单,下个插件,随便找个 L3 就能拿到 key。
项目地址:
https://github.com/DevLARLEY/WidevineProxy2

2. 分析过程
2.1 关闭 Widevine DRM
在 edge://flags/ 中关闭 Widevine DRM:

JS 不过多分析了,扣代码也很容易,基本直接用就行。
2.2 定位加密位置
很容易定位到加密位置,请求和处理响应分别是:

2.3 biliDRMGenSPC 分析
根据提示,传入分别是:
说明:
传入函数 _biliDRMGenSPC:

2.3.1 wasm 层函数
WASM 层对应函数是 x,利用 jeb 分析 导出全部函数。
jeb 交叉引用不起作用,导出来用其他软件分析。

从后往前分析:
函数 f44 多次调用:


2.3.2 函数 f131
函数 f131 多次调用,并带有日志。
稍微分析变化 → 得出是内存拷贝。



在 f131 下断点,打印每次传入参数大小,可以看出大致分为几段:

2.3.3 各段数据分析
0–8
固定的 8 字节 "bilibili"
8–24


24–40
随机 16 字节
某处调用是循环,很明显是随机生成。

40–168
长度 128 + 公钥 → RSA 加密后的内容
加密前的内容可直接修改传入公钥,利用对应私钥解密。
调用 f619 → 对 JS 随机生成的 aeskey 进行 RSA 加密。


168–188
对公钥进行 SHA1


188–192
后续数据长度


192–240
倒推,最后调用 f226,明显是 AES 加密:


密文由几部分组成,同样调用 f131 组合:
第一部分(16字节):目前固定值

第二部分(16字节):调用 f226(ECB 模式),加密 kid 前 16 字节
第三部分(16字节):kid 后 16 字节

总的组成如下结构
SPC = Struct(
"bilibili" / Bytes(8), # 固定 bilibili
"header" / Bytes(12), # 固定
"time" / Int32ub, # 时间戳
"iv" / Bytes(16), # 随机生成的 IV
"cipher_key" / Bytes(128), # RSA 加密后的 AES 密钥
"sha" / Bytes(20), # RSA 公钥的 SHA1 哈希值
"contentKeyCtxSize" / Int32ub, # AES 加密后的 KID 上下文长度
"contentKeyCtx" / Bytes(this.contentKeyCtxSize), # AES 加密后的 KID 上下文
)
KID = Struct(
"salt" / Bytes(16),
"enc_kid" / Bytes(16), # 加密后的 kid 前 16 字节
"kid" / Bytes(16), # kid 后 16 字节
)
2.3 biliDRMParseCKC 分析
biliDRMParseCKC搞定了上面就很简单了
主要就是aes解密
CKC = Struct(
"header" / Bytes(12),
"time" / Int32ub,
"iv" / Bytes(16),
"data_len" / Int32ub,
"data" / Bytes(this.data_len),#第一次解密全部,第二次解密后16字节,转为hex就是key
)

3. 总结
总的来说并不难,很多符号和提示都给了,还用的openssl里面的库。