之前我们已经成功拿到了wvd
现在可以直接写代码了
根据网络上的资料
大致是首先拿到pssh,这个一般在mpd文件里
![图片.png](data/attachment/forum/202406/24/122602kmnyce4lnwl006k6.png)
然后用cdm加载wvd
cdm会根据wvd和ppsh请求证书服务器
证书服务器会下发解密的密钥,然后用ffmpeg解密就可以了
为了方便大家动手实践我先以https://bitmovin.com/demos/drm为例
因为m3u8的ppsh不直观,就干脆后续下载mpd了
代码如下
const path = require('path');
const YTDlpWrap = require('yt-dlp-wrap').default;
const ytDlpWrap = new YTDlpWrap(path.join('./yt-dlp_x86.exe'));
let ytDlpEventEmitter = ytDlpWrap
.exec([
'https://cdn.bitmovin.com/content/assets/art-of-motion_drm/mpds/11331.mpd',
"-f",
"bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best[ext=m4a]",
"--allow-u",
"--no-part",
"--restrict-filenames",
"-N 4",
'-o',
'F:/vmware/output3.mp4',
])
.on('progress', (progress) =>
console.log(
progress.percent,
progress.totalSize,
progress.currentSpeed,
progress.eta
)
)
.on('ytDlpEvent', (eventType, eventData) =>
console.log(eventType, eventData)
)
.on('error', (error) => console.error(error))
.on('close', () => console.log('all done'));
console.log(ytDlpEventEmitter.ytDlpProcess.pid);
下载到的mp4无法正常播放
![图片.png](data/attachment/forum/202406/24/122755r5691x0xyt106x59.png)
接下来抓包看pssh,文件有多个ppsh
![图片.png](data/attachment/forum/202406/24/122853rgavtfairz5aicgo.png)
我们下载的目标文件名是output3.f1_stereo_192000.m4a
所以搜索192000
![图片.png](data/attachment/forum/202406/24/122949weadwcwsejegaq8s.png)
密钥为AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==
然后在网页抓包找到证书服务器,这个还是比较直观的
提交和返回都是乱码的就是证书服务器
![图片.png](data/attachment/forum/202406/24/123119flh4dlcqfhd1flhd.png)
![图片.png](data/attachment/forum/202406/24/123102su7chhnhyfshgdf2.png)
这里我们得到了https://cwip-shaka-proxy.appspot.com/no_auth
所以可以写出代码
import json
import re
import requests
from pywidevine.cdm import Cdm
from pywidevine.device import Device
from pywidevine.pssh import PSSH
def get_keys(pssh_value, license_url):
if pssh_value is None:
return []
try:
device = Device.load("aosp.wvd")
except:
return []
pssh_value = PSSH(pssh_value)
cdm = Cdm.from_device(device)
cdm_session_id = cdm.open()
challenge = cdm.get_license_challenge(cdm_session_id, pssh_value)
licence = requests.post(
license_url, data=challenge
)
licence.raise_for_status()
cdm.parse_license(cdm_session_id, licence.content)
keys = []
for key in cdm.get_keys(cdm_session_id):
if "CONTENT" in key.type:
keys += [f"{key.kid.hex}:{key.key.hex()}"]
cdm.close(cdm_session_id)
return keys
print(get_keys("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==","https://cwip-shaka-proxy.appspot.com/no_auth"))
请求可以看到返回了许多密钥
['ccbf5fb4c2965be7aa130ffb3ba9fd73:9cc0c92044cb1d69433f5f5839a159df', '9bf0e9cf0d7b55aeb4b289a63bab8610:90f52fd8ca48717b21d0c2fed7a12ae1', 'eb676abbcb345e96bbcf616630f1a3da:100b6c20940f779a4589152b57d2dacb', '0294b9599d755de2bbf0fdca3fa5eab7:3bda2f40344c7def614227b9c0f03e26', '639da80cf23b55f3b8cab3f64cfa5df6:229f5f29b643e203004b30c4eaf348f4']
前边是kid,后边是解密的密钥
我们返回之前看我们kid是eb676abb-cb34-5e96-bbcf-616630f1a3da
![图片.png](data/attachment/forum/202406/24/130829o6r6d7fcf1udrd4u.png)
刚好对应了eb676abbcb345e96bbcf616630f1a3da:100b6c20940f779a4589152b57d2dacb
可以知道我们的密钥是100b6c20940f779a4589152b57d2dacb
(一个小小的冷知识,其实全怼进去,ffmpeg自己也能识别出来正确的)
所以输入
ffmpeg -decryption_key 100b6c20940f779a4589152b57d2dacb -i output3.f1080_4800000.mp4 -codec copy decrypted_media.mp4
解密视频
使用同样的密钥
ffmpeg -decryption_key 100b6c20940f779a4589152b57d2dacb -i output3.f1_stereo_192000.m4a -codec copy decrypted_media.m4a
解密音频
输入ffmpeg -i decrypted_media.mp4 -i decrypted_media.m4a -vcodec copy -acodec copy video.mp4
合并视频音频
我们就得到了一个DRM视频,当然因为版权保护问题无法截图~
![图片.png](data/attachment/forum/202406/24/131121ukejel7mkplhmm4p.png)
待续....