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:

  1. L1: All operations are done on the TEE (Trusted Execution Environment)
  2. L2: Some operations are done on the CPU and some on the TEE (rarely used)
  3. 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. 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. Steps:

  1. Go to your home directory and create the root of your Android SDK:
    cd ~
    mkdir -p Android/Sdk
                    
  2. 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
                    
  3. 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"
                    
  4. Create an AVD with the name "wvdump":
    ./avdmanager create avd -n "wvdump" -k "system-images;android-28;google_apis;x86_64"
                    
  5. We are done with this step!


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:

  1. 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 version). Then extract the file (preferably to Android/Sdk/platform-tools/)
  2. 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
                    
  3. 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.
  4. In this step, 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.

  1. 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 weird crash
    python3 dump_keys.py
                    
    You will see that it will wait for activity.
  2. Go to the 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 please retry. Tap 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.
To get the keys required for the decryption we need three things:


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)
  1. 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'.

  2. If you are using Firefox, you need to enable the 'Play DRM-controlled content' option to play the video. If you don't want to, create a new profile and enable it there.
    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.
  3. The mpd file contents on the Network tab
  4. 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.
PSSH and License Server URL location in the XML code
Now the only thing left is to download the encrypted video. You can do that either with:


Getting decryption keys

We will do this with the help of a library/tool named pywidevine. Steps:

  1. Install pywidevine:
    pip3 install pywidevine
                    
  2. 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.
  3. 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 (with the : , 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
        


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 to your friends.