From d23d66d623a2bd198f839dcd6a8bf2f72d7e6a8d Mon Sep 17 00:00:00 2001 From: FoxRefire <155989196+FoxRefire@users.noreply.github.com> Date: Mon, 20 May 2024 12:12:24 +0900 Subject: [PATCH 1/3] Open Bitmovin's demo automatically --- README.md | 1 + keydive.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index d9deee7..0ae0608 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Extract Widevine L3 keys from an Android device. options: -h, --help show this help message and exit + -a, --auto Open Bitmovin\'s demo automatically -d DEVICE, --device DEVICE Target Android device ID. -f FUNCTIONS, --functions FUNCTIONS diff --git a/keydive.py b/keydive.py index 92591de..fc8cac1 100644 --- a/keydive.py +++ b/keydive.py @@ -19,6 +19,7 @@ if __name__ == '__main__': # Parse command line arguments for device ID parser = argparse.ArgumentParser(description='Extract Widevine L3 keys from an Android device.') + parser.add_argument('-a', '--auto', required=False, action='store_true', help='Open Bitmovin\'s demo automatically.') parser.add_argument('-d', '--device', required=False, type=str, help='Target Android device ID.') parser.add_argument('-f', '--functions', required=False, type=Path, help='Path to Ghidra XML functions file.') parser.add_argument('--force', required=False, action='store_true', help='Force using the default vendor (skipping analysis).') @@ -47,6 +48,9 @@ if __name__ == '__main__': raise Exception('Failed to hook into the Widevine process') logger.info('Successfully hooked. To test, play a DRM-protected video: https://bitmovin.com/demos/drm') + if args.auto: + subprocess.run(['adb', 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', 'https://bitmovin.com/demos/drm']) + # Keep script running while extracting keys while cdm.running: time.sleep(1) From 2e79463ee86cbdd72c1fd5418135f694c157f34b Mon Sep 17 00:00:00 2001 From: FoxRefire <155989196+FoxRefire@users.noreply.github.com> Date: Mon, 20 May 2024 12:12:24 +0900 Subject: [PATCH 2/3] Open Bitmovin's demo automatically --- README.md | 3 ++- keydive.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9deee7..54a8317 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,13 @@ For a detailed step-by-step guide on setting up and executing KeyDive without in ### Command-Line Options ```shell -usage: keydive.py [-h] [-d DEVICE] [-f FUNCTIONS] [--force] +usage: keydive.py [-h] [-a] [-d DEVICE] [-f FUNCTIONS] [--force] Extract Widevine L3 keys from an Android device. options: -h, --help show this help message and exit + -a, --auto Open Bitmovin’s demo automatically -d DEVICE, --device DEVICE Target Android device ID. -f FUNCTIONS, --functions FUNCTIONS diff --git a/keydive.py b/keydive.py index 92591de..fc8cac1 100644 --- a/keydive.py +++ b/keydive.py @@ -19,6 +19,7 @@ if __name__ == '__main__': # Parse command line arguments for device ID parser = argparse.ArgumentParser(description='Extract Widevine L3 keys from an Android device.') + parser.add_argument('-a', '--auto', required=False, action='store_true', help='Open Bitmovin\'s demo automatically.') parser.add_argument('-d', '--device', required=False, type=str, help='Target Android device ID.') parser.add_argument('-f', '--functions', required=False, type=Path, help='Path to Ghidra XML functions file.') parser.add_argument('--force', required=False, action='store_true', help='Force using the default vendor (skipping analysis).') @@ -47,6 +48,9 @@ if __name__ == '__main__': raise Exception('Failed to hook into the Widevine process') logger.info('Successfully hooked. To test, play a DRM-protected video: https://bitmovin.com/demos/drm') + if args.auto: + subprocess.run(['adb', 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', 'https://bitmovin.com/demos/drm']) + # Keep script running while extracting keys while cdm.running: time.sleep(1) From daaab4e6aa4a218f388981ba68c176fc3f0ec908 Mon Sep 17 00:00:00 2001 From: FoxRefire <155989196+FoxRefire@users.noreply.github.com> Date: Tue, 21 May 2024 21:26:31 +0900 Subject: [PATCH 3/3] Generate WVD --- README.md | 3 ++- extractor/cdm.py | 18 +++++++++++++++++- keydive.py | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54a8317..55d6eb3 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ For a detailed step-by-step guide on setting up and executing KeyDive without in ### Command-Line Options ```shell -usage: keydive.py [-h] [-a] [-d DEVICE] [-f FUNCTIONS] [--force] +usage: keydive.py [-h] [-a] [-d DEVICE] [-f FUNCTIONS] [-w] [--force] Extract Widevine L3 keys from an Android device. @@ -68,6 +68,7 @@ options: Target Android device ID. -f FUNCTIONS, --functions FUNCTIONS Path to Ghidra XML functions file. + -w, --wvd Generate WVD --force Force using the default vendor (skipping analysis). ``` diff --git a/extractor/cdm.py b/extractor/cdm.py index 61dca10..bbc1c87 100644 --- a/extractor/cdm.py +++ b/extractor/cdm.py @@ -11,6 +11,7 @@ from Cryptodome.PublicKey import RSA from extractor.license_protocol_pb2 import SignedMessage, LicenseRequest, ClientIdentification, DrmCertificate, SignedDrmCertificate from extractor.vendor import Vendor +from pywidevine.device import Device, DeviceTypes SCRIPT_PATH = Path(__file__).parent / 'script.js' @@ -28,7 +29,7 @@ class Cdm: # Add more as needed for different versions. } - def __init__(self, device: str = None, functions: Path = None, force: bool = False): + def __init__(self, device: str = None, functions: Path = None, force: bool = False, wvd: bool = False): self.logger = logging.getLogger('Cdm') self.functions = functions self.running = True @@ -37,6 +38,9 @@ class Cdm: self.device: Device = frida.get_device(id=device, timeout=5) if device else frida.get_usb_device(timeout=5) self.logger.info('Device: %s (%s)', self.device.name, self.device.id) + # Select if create WVD or not + self.wvd = wvd + # Obtain device properties self.properties = self._fetch_device_properties() @@ -252,6 +256,18 @@ class Cdm: self.logger.info('Dumped client ID: %s', path_client_id) self.logger.info('Dumped private key: %s', path_private_key) + + if self.wvd: + path_wvd = path / 'device.wvd' + Device( + client_id=client_id.SerializeToString(), + private_key=private_key.exportKey('PEM'), + type_=DeviceTypes['ANDROID'], + security_level=3, + flags=None + ).dump(path_wvd) + self.logger.info('Created WVD: %s', path_wvd) + self.running = False else: self.logger.warning('Failed to intercept the private key') diff --git a/keydive.py b/keydive.py index fc8cac1..19717af 100644 --- a/keydive.py +++ b/keydive.py @@ -22,6 +22,7 @@ if __name__ == '__main__': parser.add_argument('-a', '--auto', required=False, action='store_true', help='Open Bitmovin\'s demo automatically.') parser.add_argument('-d', '--device', required=False, type=str, help='Target Android device ID.') parser.add_argument('-f', '--functions', required=False, type=Path, help='Path to Ghidra XML functions file.') + parser.add_argument('-w', '--wvd', required=False, action='store_true', help='Generate WVD') parser.add_argument('--force', required=False, action='store_true', help='Force using the default vendor (skipping analysis).') args = parser.parse_args() @@ -35,7 +36,7 @@ if __name__ == '__main__': raise EnvironmentError('ADB is not recognized as an environment variable, see https://github.com/hyugogirubato/KeyDive/blob/main/docs/PACKAGE.md#adb-android-debug-bridge') # Initialize the CDM handler with the specified or default device - cdm = Cdm(device=args.device, functions=args.functions, force=args.force) + cdm = Cdm(device=args.device, functions=args.functions, force=args.force, wvd=args.wvd) # Attempt to locate and identify the Widevine process on the target device pid = cdm.enumerate_processes().get(cdm.vendor.process)