Breaking Google's Widevine DRM
Post written: 10 Nov 2024
The following article describes methods that must only be used for educational purposes.
Introduction
Google Widevine is the most widely used DRM (Digital Right Management) technology right now. It is used among the biggest platforms, like Netflix. What does DRM do? It restricts you from copying (downloading locally) the content that you are watching. But I don’t like that! So let’s break it!
Widevine Levels
Google divides Widevine’s operation security in three levels: - L1: All operations are done on the TEE (Trusted Execution Environment) - L2: Some operations are done on the CPU and some on the TEE (rarely used) - L3: All operations are done on the CPU. The TEE is a dedicated chip in the computer that does cryptographic operations. (The TPM is one)
Unfortunately, we can’t crack L1 and L2 because they are more secure. Luckily, we can crack L3. The only problem is that some sites require L1 for higher quality video (1080p+).
What are we doing?
The Content Decryption Module (CDM) does the licensing and decryption of the content. To get one, we need to dump it from an Android phone. This way, we can spoof a browser and decrypt the content.
To do this, we will start an Android emulator and dump the Widevine CDM from it. I you already have installed Android Studio, you can create an AVD (API 28) using the GUI tools. If you don’t, I will show you how to do it without using Android Studio.
Creating the emulator
I am using Linux to do this. If you are using Windows, please download Android Studio and set up an AVD from there. This should only be done once.
Steps:
- Go to your home directory and create the root of your Android SDK:
cd ~ mkdir -p Android/Sdk
- Download cmdline-tools from here and extract them to
Android/Sdk/cmdline-tools/latest
. Your directory tree should now look like this:cmdline-tools/ └── latest ├── bin ├── lib ├── NOTICE.txt └── source.properties
- Accept the licenses of sdkmanager and download the necessary files, so we can create an Android 9 device:
cd Android/Sdk/cmdline-tools/latest/bin ./sdkmanager --licenses ./sdkmanager "emulator" "platform-tools" "platforms;android-28" "system-images;android-28;google_apis;x86_64"
- Create an AVD with the name “wvdump”:
./avdmanager create avd -n "wvdump" -k "system-images;android-28;google_apis;x86_64"
- We are done!
Preparing to dump the CDM
Now we need to install something called the “Frida Server” to our emulator, so that the program that dumps the CDM can communicate with the emulator.
Steps:
-
Firstly, go there, click “Show all assets” and download the file called “frida-server-X.X.X-android-x86_64.xz” (replace the X’s with the version number). Then extract the file (preferably to
Android/Sdk/platform-tools/
) - For this step you need to have Python installed. If you don’t have it installed, please install it. Run:
pip3 install frida pip3 install frida-tools
- Now we need to finally start the emulator. To do this, run:
cd ~/Android/Sdk/emulator ./emulator -avd wvdump
And then wait for the emulator to start. If you have a slow computer, this will take a while.
- Now, we will push Frida Server to the emulator and run it. Run:
cd ~/Android/Sdk/platform-tools ./adb push /path/to/frida-server-X.X.X-android-x86_64 /sdcard ./adb shell mv /sdcard/frida-server.X.X.X-android-x86_64 /data/local/tmp chmod +x /data/local/tmp/frida-server.X.X.X-android-x86_64 /data/local/tmp/frida-server.X.X.X-android-x86_64
You will see that it will wait indefinitely. This is good and it means that the Frida Server is running.
Start another terminal so you can proceed to the next step.
Dumping the CDM
Now we will finally dump the CDM from the device. This should only be done once (you don’t have to get a new CDM every time).
- First create a directory which we will be using for now on.
Clone this repository and start the dumper:
mkdir widevine && cd widevine git clone https://github.com/wvdumper/dumper.git cd dumper pip3 install -r requirements.txt pip3 install protobuf==3.20.0 # We downgrade the library version to avoid a crash python3 dump_keys.py
You will see that it will wait for activity.
- Go to the Android emulator and open Chrome. Using Chrome in the emulator navigate to https://bitmovin.com/demos/drm. Try to play the video. If a popup shows up while trying to play the video (not the cookies one), select Allow.
Why this page? Because it contains a video protected with Widevine DRM. When you try to play it, dumper will
intercept the keys and save them.
Now you should see some activity in the dumper terminal window. If you don’t
see anything, please retry. Type Ctrl-C to terminate the dumper
and go to the folder key_dumps/Android Emulator 5554/private_keys/<some numbers>/<some numbers>
. You should find
two files in this folder named client_id.bin
and private_key.pem
. That is your CDM!
If however you cannot find this folder, please check you’ve done all the steps correctly and retry the procedure.
Getting encrypted video
Now we should choose a video to decrypt. The site tg4.ie is a pretty good site for you to practice, because it provides video encrypted with Widevine and a relatively easy procedure to follow. However, you should know that you must delete the decrypted video afterwards, since pirating copyrighted media this way is illegal in some countries.
To get the keys required for the decryption we need three things:
- The CDM (we already have that one)
- The PSSH, a long base64-encoded string
- The license server URL
After we have those, we need to send a license request to the license server, accompanied with our CDM and the PSSH, and then the license server should return the keys to us.
To find the PSSH and the license server URL, follow these steps: (The steps are for Firefox users, but for Chrome it should be quite similiar)
- Go to the site above and hit Ctrl-Shift-I to open Inspect Element, go the Network tab, and filter for requests containing the text ‘mpd’.
For Chrome users, it is already enabled and you can't disable it because Google.
-
Click a video to play. You will see some requests in the Network tab. Click one that it has Type ‘dash’. Go to the Response tab, and you should see some XML data.
-
Right click on the request, click Copy Value and then Copy URL. Then go to the terminal and download the file using this command:
cd /path/to/widevine # Go to our directory wget "<the URL you copied>"
You will now have a file named ‘manifest.mpd’. It contains the PSSH and (probably) the license server URL.
The PSSH resides inside the cenc:pssh
tags. You can find the license server URL either by
going back to the Network tab and filtering the requests by ‘license’ or ‘widevine’, or looking in the
bc:licenseAcquisitionUrl
in the ContentProtection
tag.
You should save both to a file for later use.
Now the only thing left is to download the encrypted video. You can do that either with:
- N_m3u8DL-RE:
./N_m3u8DL-RE "<the manifest.mpd url you copied from the network tab>"
- or yt-dlp:
yt-dlp --allow-unplayable "<the manifest.mpd url you copied from the network tab>"
(Select other qualities with-f <quality id>
and list available ones with-F
)
Getting decryption keys
We will do this with the help of a library/tool named pywidevine.
Steps:
- Install pywidevine:
pip3 install pywidevine
- Merge our two CDM files into one “Widevine device” file:
pywidevine create-device -t ANDROID -l3 -k /path/to/private_key.pem -c /path/to/client_id.bin -o device.wvd
Replace the paths to your CDM files appropriately.
- Finally, send out license request to get the decryption keys:
pywidevine license device.wvd <PSSH> <License URL>
Replace the PSSH and License URL appropriately. The output should be similiar to this:
INFO:root:pywidevine version 1.8.0 Copyright (c) 2022-2024 rlaphoenix INFO:root:https://github.com/devine-dl/pywidevine INFO:license:[+] Loaded Device (4464 L3) INFO:license:[+] Loaded CDM INFO:license:[+] Opened CDM Session: 9a5a570efca927a4db506d40e5deb27b INFO:license:[+] Created License Request Message (Challenge) INFO:license:[+] Got License Message INFO:license:[+] License Parsed Successfully INFO:license:[SIGNING] 00000000000000000000000000000000:56398c5cb0287ba52085be71c2aeb5b0d61d9b6cd6eec441c442c455832862cbad123c97fed82139156a755c79c4331843379140ca9ea0c37547e693e4827092 INFO:license:[CONTENT] 6c8dae40ec3547fa9f6c78e16af85987:f05de262d9b1b3db398d153edf0bf80b
The pair of big hexadecimal numbers after
[CONTENT]
is our key (and key ID)! Copy it and save it to a file.
Decrypting
Finally, to decrypt the audio and video (as you may have noticed there two separate files) we will need a copy of the Bento4 SDK. Download it and extract it.
To decrypt do:
/path/to/Bento4-SDK/bin/mp4decrypt --key <The key we got earlier> video.mp4 video_decrypted.mp4
/path/to/Bento4-SDK/bin/mp4decrypt --key <The key we got earlier> audio.m4a audio_decrypted.m4a
Replace the key with the full key and key id pair we got before (including the semicolon , e.g 6c8dae40ec3547fa9f6c78e16af85987:f05de262d9b1b3db398d153edf0bf80b
)
and the video.mp4
and audio.m4a
with the names of the files you downloaded.
Now, if you play one of them they should play normally! This means they’re decrypted!! Now the only thing left is to merge them:
ffmpeg -i video_decrypted.mp4 -i audio_decrypted.m4a -vcodec copy -acodec copy final_decrypted.mp4
And there you have a playable decrypted video!
Epilogue
And we are done! We have converted a video that was playable only in your browser to a normal video file that can be distributed! I hope you learned something from this article. If so, share it with your friends.