某牛m3u8 key解密算法分析及实现

查看 99|回复 9
作者:superTian   
目标:学习交流 就不放目标地址了
播放任意一个视频,F12找到m3u8的请求 发现其中没有Key的信息


图片1.png (27.85 KB, 下载次数: 0)
下载附件
2023-11-16 09:42 上传

这种我们从ts的启动器下手


图片2.png (45.67 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

点进去然后搜decryptdata.key、aes-128、buffer 等关键字
搜decryptdata.key 有5个结果 全部打断点 刷新


图片3.png (49.41 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

断住了 这个this.decryptkey 非常像解密时用的key
可以使用m3u8下载工具 验证一下想法是不是对的
首先将this.decryptkey转为十六进制字符串,代码如下
[Python] 纯文本查看 复制代码decryptkey = [53, 98, 97, 53, 97, 100, 50, 53, 51, 54, 99, 57, 50, 55, 100, 54]
hex_string = bytes(decryptkey).hex() # 35626135616432353336633932376436
使用工具下载(这里十六进制key会被自动Base64编码)


图片4.png (59.25 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

结果可正常下载 正常播放
至此 key已经找到 接下来需要找key生成的地方
上面得知this.decryptkey = t.decryptdata.key = new Uint8Array(this.qiniuDRMKey)
文件内搜qiniuDRMKey = 只有一个结果


图片5.png (39.86 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

往上跟找e.config


图片6.png (165.15 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

t = this
t.config = e
e里面有qiniuDRMKey
往上跟找e


图片7.png (65.5 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

又一个this.config  往上找this.config.DRMKey


图片8.png (70.85 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

r = this
r.config = t
t 里面有DRMKey  t是传过来的 继续往上跟...


图片9.png (93.59 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

一路跟 跟到这 这个e里面有两个关键字段 一个是key  一个m3u8的url(意外惊喜)
继续往上跟 要换js文件了 不行了来感觉了


图片10.png (82.31 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

成功找到了key、url的生成位置
这里o 和 v 分别是加密后的url 和key
进入关键函数X看一下 是个AES加解密函数。


图片11.png (21.86 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

X接收三个参数 e=要解密的内容 t=固定字符串 n控制加密还是解密
接下来就是找被加密的url 和key字符串 也就是o 和v
一般这两个参数都是通过api返回的,可以用抓包工具搜索一下(这里用的Charles)


图片12.png (33.23 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

这里Search In 只勾选 Response Body


图片13.png (57.51 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

这里有两个搜索结果 一个html 一个api 两个里面都包含加密内容


图片14.png (24.7 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

点开api的Response Body 可以看到 o、v 都是该api返回
至此整个分析结束 接下来我们来实现一下解密算法
JS实现:
[JavaScript] 纯文本查看 复制代码const CryptoJS = require('crypto-js');
function decrypt(e) {
    var t = CryptoJS.MD5("固定字符串").toString();
    var i = CryptoJS.enc.Utf8.parse(t.substring(0, 16));
    var r = r = CryptoJS.enc.Utf8.parse(t.substring(16));
    return CryptoJS.AES.decrypt(e, r, {
        iv: i,
        padding: CryptoJS.pad.Pkcs7,
      }).toString(CryptoJS.enc.Utf8);
}
var annex_secret = '';
var key = JSON.parse(decrypt(annex_secret));
console.log(key)


图片15.png (19.24 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

对比控制台执行结果 完全一致
Python 实现:
[Python] 纯文本查看 复制代码from utils import AESCrypto
import hashlib
import json
def get_md5_string(s: str):
    return hashlib.md5(s.encode('utf-8')).hexdigest()
def decrypt(e: str):
    t = get_md5_string("固定字符串")
    i, r = t[:16], t[16:]
    cipher = AESCrypto(key=r, mode='CBC', padding='Pkcs7')
    return cipher.decrypt(e, iv=i)
if __name__ == '__main__':
    annex_secret = ''
    key = json.loads(decrypt(annex_secret))
    print(key)
对比下结果也是一样的


图片16.png (58.08 KB, 下载次数: 0)
下载附件
2023-11-16 09:51 上传

最后:
其实刚开始的时候就发现了在控制台里面有key的log信息...
本着学习的态度 还是从头到尾撸了一遍...

下载次数, 下载附件

OVVO   

大佬好强
superTian
OP
  


paoxueyuan 发表于 2023-11-18 16:14
from utils import AESCrypto
想问下下这个AESCrypto 是大佬您的本地库么,网上安装utils的库中文件夹 ...
pip install pycryptodome
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES
import hashlib
import base64
import json
def decrypt(e):
    t = hashlib.md5('固定字符串'.encode()).hexdigest()
    i = t[:16].encode()
    r = t[16:].encode()
    cipher = AES.new(r, AES.MODE_CBC, iv=i)
    decrypted = cipher.decrypt(e)
    return unpad(decrypted, AES.block_size)
annex_secret = ''
key = json.loads(decrypt(base64.b64decode(annex_secret)))
print(key)
chg1a   

好教程!
superTian
OP
  


OVVO 发表于 2023-11-16 18:35
大佬好强

主要是看了巨佬的帖子 学到了很多!
feifei1255   

高端贴,学习
sunzhw   

学习学习大佬
xxggla666   

感谢大佬分享,学习了。
pjyang   

大佬好强
stoptime77   

过来学习高端技术
您需要登录后才可以回帖 登录 | 立即注册

返回顶部