Add files via upload

Added a script to extract device_private_key and device_client_id_blob from a WDV File, also added support for https://cdrm-project.com api
This commit is contained in:
Sasuke-Duck UwU 2023-11-16 01:32:18 -05:00 committed by GitHub
parent 4b985d7d71
commit 8aa81bddf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 154 additions and 1 deletions

View File

@ -26,6 +26,7 @@ from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import pss from Cryptodome.Signature import pss
from Cryptodome.Util import Padding from Cryptodome.Util import Padding
import logging import logging
from bs4 import BeautifulSoup
_sym_db = _symbol_database.Default() _sym_db = _symbol_database.Default()
@ -855,4 +856,29 @@ def parse_manifest_ism(manifest_url):
encoded_string = base64.b64encode(bytes.fromhex(array_of_bytes.hex())).decode("utf-8") encoded_string = base64.b64encode(bytes.fromhex(array_of_bytes.hex())).decode("utf-8")
return kid, stream_info_list, encoded_string return kid, stream_info_list, encoded_string
def get_keys_license_cdrm_project(license_url, headers_license, pssh_value):
formatted_headers = '\n'.join([f'{key}: "{value}"' for key, value in headers_license.items()])
json_data = {
'license': license_url,
'headers': formatted_headers,
'pssh': pssh_value,
'buildInfo': '',
'proxy': '',
'cache': False,
}
response = requests.post('https://cdrm-project.com/wv', json=json_data)
return response
def print_keys_cdrm_project(response):
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
li_elements = soup.find('ol').find_all('li')
for li in li_elements:
key = li.get_text(strip=True)
print(f'KEY: {key}')
else:
print(f"Error: {response.status_code}")

35
cdrm.py Normal file
View File

@ -0,0 +1,35 @@
import argparse
import requests
from cdm.wks import PsshExtractor, get_keys_license_cdrm_project, print_keys_cdrm_project
token = ""
def main():
parser = argparse.ArgumentParser(description="Decrypt Widevine content using MPD URL and License URL")
parser.add_argument("-mpd", required=True, help="URL of the MPD manifest")
parser.add_argument("-lic", required=True, help="URL of the license server")
args = parser.parse_args()
mpd_url = args.mpd
license_url = args.lic
headers_mpd = {
'origin': 'https://play.hbomax.com',
'referer': 'https://play.hbomax.com/',
}
response = requests.get(mpd_url, headers=headers_mpd)
pssh_extractor = PsshExtractor(response.text)
pssh_value = pssh_extractor.extract_pssh()
print("PSSH value:", pssh_value)
headers_license = {
'authorization': f'Bearer {token}',
}
response = get_keys_license_cdrm_project(license_url, headers_license, pssh_value)
print_keys_cdrm_project(response)
if __name__ == "__main__":
main()

92
extractwvd.py Normal file
View File

@ -0,0 +1,92 @@
import argparse
import json
from enum import Enum
from pathlib import Path
from construct import BitStruct, Bytes, Const
from construct import Enum as CEnum
from construct import Flag, If, Int8ub, Int16ub, Optional, Padded, Padding, Struct, this
from Cryptodome.PublicKey import RSA
from cdm.wks import ClientIdentification
class DeviceTypes(Enum):
CHROME = 1
ANDROID = 2
WidevineDeviceStruct = Struct(
'signature' / Const(b'WVD'),
'version' / Int8ub,
'type' / CEnum(
Int8ub,
**{t.name: t.value for t in DeviceTypes}
),
'security_level' / Int8ub,
'flags' / Padded(1, Optional(BitStruct(
Padding(7),
'send_key_control_nonce' / Flag
))),
'private_key_len' / Int16ub,
'private_key' / Bytes(this.private_key_len),
'client_id_len' / Int16ub,
'client_id' / Bytes(this.client_id_len),
'vmp_len' / Optional(Int16ub),
'vmp' / If(this.vmp_len, Optional(Bytes(this.vmp_len)))
)
WidevineDeviceStructVersion = 1
def parse_args():
parser = argparse.ArgumentParser(description='Widevine Device Information Parser')
parser.add_argument('file', type=Path, help='Path to WVD file')
return parser.parse_args()
def write_key_and_blob_files(out_dir, device):
private_key_file = out_dir / 'device_private_key'
print(f'\n[INFO] Writing private key to: {private_key_file}')
private_key = RSA.import_key(device.private_key)
private_key_file.write_text(private_key.export_key('PEM').decode())
client_id_blob_file = out_dir / 'device_client_id_blob'
print(f'[INFO] Writing client ID blob to: {client_id_blob_file}')
client_id_blob_file.write_bytes(device.client_id)
if device.vmp:
vmp_blob_file = out_dir / 'device_vmp_blob'
print(f'[INFO] Writing VMP blob to: {vmp_blob_file}')
vmp_blob_file.write_bytes(device.vmp)
def write_json_file(out_dir, name, client_id, device):
wv_json_file = out_dir / 'wv.json'
description = f'{name} ({client_id.Token._DeviceCertificate.SystemId})'
print(f'[INFO] Writing JSON file to: {wv_json_file}')
wv_json_file.write_text(json.dumps({
'name': name,
'description': description,
'security_level': device.security_level,
'session_id_type': device.type.lower(),
'private_key_available': True,
'vmp': bool(device.vmp),
'send_key_control_nonce': device.type == DeviceTypes.ANDROID
}, indent=2))
def main():
args = parse_args()
name = args.file.with_suffix('').name
out_dir = Path.cwd() / 'cdm' / 'devices' / 'android_generic'
out_dir.mkdir(parents=True, exist_ok=True)
with args.file.open('rb') as fd:
device = WidevineDeviceStruct.parse_stream(fd)
print(f'\n[INFO] Starting Widevine Device Information Parsing')
write_key_and_blob_files(out_dir, device)
client_id = ClientIdentification()
client_id.ParseFromString(device.client_id)
write_json_file(out_dir, name, client_id, device)
print('[INFO] Done')
if __name__ == '__main__':
main()