我不理解,为什么像Netflix、Spotify流媒体服务提供商,包括苹果、谷歌等公司,会使用数字版权管理。他们在我买的设备上,浪费我的硬件算力,加密我买的数字内容,以阻止我下载。君子也防?[doge]
本文尝试欺骗DRM License Server获取秘钥,来解密被DRM加密的多媒体内容。
了解Google Widevine 通过阅读文章大致了解了widevine的工作原理,以及DRM(Digital Rights Management,数字版权管理)系统的执行过程:
获取CDM Device验证文件 CDM(内容解密模块)是嵌入在Chrome浏览器或Android操作系统中用来加解密流媒体的闭源模块。因此,我们可以模拟一个安卓设备。
使用pywidevine
创建CDM Device。创建设备需要两个关键文件,client_id.bin
和private_key.pem
。
按照帖子Dumping Your own L3 CDM with Android Studio 的操作,下载并安装Android Studio,再使用frida-server
获取关键文件。
在Android Studio的Device Manager中新建设备,选一台更帖子描述一样的设备,Pixel 6
、Pie
,生成了Pixel 6 API 28
设备,启动后显示emulator-5554
。(我的Andriod Studio装好时,Device Manager中已经有了一部虚拟手机Pixel_3A_API_34_extension_level_7_x86_64
(注意这是一台x86_64的设备),这个设备浏览器解析CDM时貌似有问题)
在Windows下的conda
中新开一个环境安装pip install frida frida-tools
,并确定frida
版本(我使用了16.2.1
),frida
与frida-server
版本需一致。
将下载的frida-server-16.2.1-android-x86
放到C:\Users\<YourUserName>\AppData\Local\Android\Sdk\platform-tools\
目录中,命令行进入该目录,执行:
1 2 3 > .\adb.exe devices List of devices attached emulator-5554 device
可以看到虚拟设备已被识别。
将frida-server
上传到虚拟设备:
1 2 > .\adb.exe push .\frida-server -16 .2.1 -android -x86 /sdcard .\frida-server -16 .2.1 -android -x86 : 1 file pushed, 0 skipped. 152.8 MB/s (51807740 bytes in 0.323 s)
连接虚拟设备,将frida-server
放在正确的位置,赋予执行权限并启动:
1 2 3 4 5 > .\adb.exe shell su mv /sdcard/frida-server -16 .2.1 -android -x86 /data /local/tmp/chmod +x /data /local/tmp/frida-server -16 .2.1 -android -x86 /data /local/tmp/frida-server -16 .2.1 -android -x86
此时server启动,保持终端运行。
下载dumper ,进入目录并安装依赖pip install -r requirements.txt
。
运行时报错提示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 python .\dump_keys.py Traceback (most recent call last): File "C:\Users\qinzi\pyws\wdevof\dumper\dump_keys.py" , line 6 , in <module> from Helpers.Scanner import Scan File "C:\Users\qinzi\pyws\wdevof\dumper\Helpers\Scanner.py" , line 7 , in <module> from Helpers.wv_proto2_pb2 import SignedLicenseRequest File "C:\Users\qinzi\pyws\wdevof\dumper\Helpers\wv_proto2_pb2.py" , line 33 , in <module> _descriptor.EnumValueDescriptor( File "C:\Users\qinzi\anaconda3\envs\wdev\Lib\site-packages\google\protobuf\descriptor.py" , line 789 , in __new__ _message.Message._CheckCalledFromGeneratedFile() TypeError: Descriptors cannot be created directly. If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc >= 3.19 .0 .If you cannot immediately regenerate your protos, some other possible workarounds are: 1 . Downgrade the protobuf package to 3.20 .x or lower. 2 . Set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python (but this will use pure-Python parsing and will be much slower).
因此安装建议为PowerShell添加环境变量,再次运行python .\dump_keys.py
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 > $env:PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION = 'python' > $env:PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION python > python .\dump_keys.py 2024 -03 -02 12 :22 :36 PM - root - 16 - INFO - Connected to Android Emulator 5554 2024 -03 -02 12 :22 :36 PM - root - 17 - INFO - scanning all processes for the following libraries... 2024 -03 -02 02 :17 :06 PM - Helpers.Scanner - 82 - INFO - Running libwvhidl.so at 0 xec7170002024 -03 -02 02 :17 :06 PM - Helpers.Scanner - 75 - DEBUG - { "from" : "Dynamic Function" , "message" : "L3 RSA Key export function found: cwkfcplc" } ... 2024 -03 -02 12 :22 :37 PM - root - 25 - INFO - Hooks completed
此时dumper启动,保持终端运行。
创建CDM设备 此时可以使用client_id.bin
和private_key.pem
,通过pywidevine
命令行工具来生成wvd
设备文件了:
1 pywidevine create-device -k ./private_key.pem -c ./client_id.bin -t "ANDROID" -l 3
得到一个名为google_aosp_on_ia_emulator_14.0.0_67016d30_4464_l3.wvd
的设备文件。
获取秘钥 使用pywidevine
的例程即可获取秘钥:
pssh
在希望下载的视频描述文件中,可能是浏览器请求的一个https://a.com/b/c.mpd
xml文件或是一个https://a.com/b/c.m3u8
list文件。
device
的路径即为刚才创建的设备文件路径。
licence
请求的地址,是浏览器加载视频时发出的一次或两次(aduio和video各一次)秘钥请求,URL特征应当非常明显,通常为POST方式,带有二进制负载。
另外需要注意的是,CDM秘钥的请求原理上是独立于媒体提供平台的,或是与媒体服务API不同的一套API,因此要使得licence
请求成功,可能还需要从浏览器中复制秘钥请求headers
一并加入requests.post
(如使用浏览器**复制为cURL (bash)**)。
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 from pywidevine.cdm import Cdmfrom pywidevine.device import Devicefrom pywidevine.pssh import PSSHimport requestspssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa" "7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==" ) device = Device.load("C:/Path/To/A/Provision.wvd" ) cdm = Cdm.from_device(device) session_id = cdm.open () challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post("https://..." , data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) for key in cdm.get_keys(session_id): print (f"[{key.type } ] {key.kid.hex } :{key.key.hex ()} " ) cdm.close(session_id)
请求成功后会打印SIGNING
和CONTENT
,这个CONTENT
就是我们需要用来解密音视频的秘钥对kid:key
,我的请求输出大致格式为:
1 2 [SIGNING] <32bit-hex>:<128bit-hex> [CONTENT] <32bit-hex-kid>:<32bit-hex-key>
解密多媒体文件 解密工具有现成的,比如mp4decrypt 、shaka ,我用的是mp4decrypt,使用刚才拿到的`kid:key。
1 mp4decrypt --key <kid>:<key> my_encrypted_file.mp4 my_decrypted_file.mp4
也可以直接使用支持DRM解密的下载工具,如使用rust编写的dash-mpd-cli ,只需要在参数中指定解密工具和秘钥即可:
1 2 3 4 5 6 dash-mpd-cli https://url_of_.mpd_file.mpd --output my_decrypted_file.mp4 -H cURL_format_headers -H ... --decryption-application mp4decrypt \ --mp4decrypt-location /home/wsl/devof/Bento4/cmakebuild/mp4decrypt \ --key d2afe67c226cf2e932ac9b4c0c13432d:c5cf130ddf1087ca2f134725e82f1c1e