From 16df7440d6b3f3a6ede240b6d170c56fa5444b86 Mon Sep 17 00:00:00 2001 From: Diazole Date: Fri, 28 Jul 2023 18:56:42 +0100 Subject: [PATCH] Add support for Android 13 --- Helpers/Device.py | 12 +++++++++--- Helpers/script.js | 1 + README.md | 49 +++++++++++++++++++++++++++++++++++------------ dump_keys.py | 11 +++++++++-- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/Helpers/Device.py b/Helpers/Device.py index efd8d1e..1ce42e2 100644 --- a/Helpers/Device.py +++ b/Helpers/Device.py @@ -7,10 +7,10 @@ from Helpers.wv_proto2_pb2 import SignedLicenseRequest class Device: - def __init__(self, dynamic_function_name, cdm_version): + def __init__(self, dynamic_function_name, cdm_version, module_names): self.logger = logging.getLogger(__name__) self.saved_keys = {} - self.widevine_libraries = ['libwvhidl.so'] + self.widevine_libraries = module_names self.usb_device = frida.get_usb_device() self.name = self.usb_device.name @@ -74,7 +74,13 @@ class Device: loaded_modules = [] try: for lib in self.widevine_libraries: - loaded_modules.append(script.exports.getmodulebyname(lib)) + try: + loaded_modules.append(script.exports.getmodulebyname(lib)) + except frida.core.RPCException as e: + # Hide the cases where the module cannot be found + continue + except Exception as e: + raise(e) finally: process.detach() return loaded_modules diff --git a/Helpers/script.js b/Helpers/script.js index 4b02c62..9c9f6d5 100644 --- a/Helpers/script.js +++ b/Helpers/script.js @@ -95,6 +95,7 @@ function prepareKeyRequest(address) { this.ret = args[4]; break; case '16.1.0': + case '17.0.0': this.ret = args[5]; break; default: diff --git a/README.md b/README.md index c3d7bfd..f1d1445 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,14 @@ Dumper is a Frida script to dump L3 CDMs from any Android device. ## ** IMPORTANT ** -The function parameters can differ between CDM versions. The default is [4] but you may have to change this for your specific version. +The function parameters can differ between CDM versions. The default is [4] but you may have to change this for your specific version in the [script.js](./Helpers/script.js). -* `CDM_VERSION` can be retrieved using a DRM Info app. +## Prerequisites +- Rooted Android device +- [Installed Frida server on the Android device]((https://frida.re/docs/android/)) +- Installed [platform-tools ADB/Fastboot](https://developer.android.com/studio/releases/platform-tools) on the PC +- Installed [Python 3](https://www.python.org/downloads/) on the PC +- `CDM_VERSION` retrieved from the [DRM Info app](https://play.google.com/store/apps/details?id=com.androidfung.drminfo). ## Requirements: Use pip to install the dependencies: @@ -14,34 +19,52 @@ Use pip to install the dependencies: ## Usage: -* Enable USB debugging -* Start frida-server on the device -* Execute dump_keys.py -* Start streaming some DRM-protected content +* Enable USB debugging on the Android device and connect it to the PC +* [Start frida-server on the Android device](https://frida.re/docs/android/) +* Execute dump_keys.py on the PC +* Start streaming some DRM-protected content on the Android device e.g. [Bitmovin](https://bitmovin.com/demos/drm) -The script will hook every function in your 'libwvhidl.so' module by default, effectively brute forcing the private key function name. +The script will hook every function in your widevine 'libwvhidl.so'/'libwvaidl.so' modules by default, effectively brute forcing the private key function name. ``` -python3 .\dump_keys.py [OPTIONS] +python3 dump_keys.py ``` -You can pass the function name to hook using the `--function-name` argument. +You can pass the function name to hook using the `--function-name` argument. You can use [this post](https://forum.videohelp.com/threads/404219-How-To-Dump-L3-CDM-From-Android-Device-s-(ONLY-Talk-About-Dumping-L3-CDMS)/page6#post2646150) to retrive it by yourself. ``` -python3 .\dump_keys.py --function-name 'polorucp' +python3 dump_keys.py --function-name 'polorucp' ``` -The script defaults to `args[4]` if no `--cdm-version` is provided. This will only have an effect if your version is `16.1.0`. +The script defaults to `args[4]` if no `--cdm-version` is provided. This will only have an effect if your version is `16.1.0` or `17.0.0`. ``` -python3 .\dump_keys.py --cdm-version '16.1.0' +python3 dump_keys.py --cdm-version '16.1.0' ``` +You can pass the `.so` -module name using the `--module-name` argument. By default it looks in the `libwvhidl.so` and `libwvaidl.so` files. It can have multiple values. Its name can change depending on the version and SoC including but not limited to: `libwvaidl.so`, `libwvhidl.so`, `libwvdrmengine.so`, `libwvm.so`, `libdrmwvmplugin.so` [source](https://arxiv.org/abs/2204.09298). You can find your module name in the /vendor/lib64/ or /vendor/lib/ directories using an ADB shell. + +``` +python3 dump_keys.py --module-name 'libwvhidl.so' --module-name 'libwvaidl.so' +``` + + ## Options: ``` -h, --help Print this help text and exit. --cdm-version The CDM version of the device e.g. '16.1.0'. --function-name The name of the function to hook to retrieve the private key. + --module-name The name of the widevine `.so` modules. ``` +## Scenario: +1. You've got the function name +2. You've got the private key +3. Client ID extracted +4. Script closed + +The following files will be created after a successful dump: +- `client_id.bin` - Device identification +- `private_key.pem` - RSA private key + ## Known Working Versions: * Android 9 * CDM 14.0.0 @@ -51,6 +74,8 @@ python3 .\dump_keys.py --cdm-version '16.1.0' * CDM 16.0.0 * Android 12 * CDM 16.1.0 +* Android 13 + * CDM 17.0.0 ## Temporary disabling L1 to use L3 instead A few phone brands let us use the L1 keybox even after unlocking the bootloader (like Xiaomi). In this case, installation of a Magisk module called [liboemcrypto-disabler](https://github.com/umylive/liboemcrypto-disabler) is necessary. diff --git a/dump_keys.py b/dump_keys.py index 9ca9e54..dfecdf5 100644 --- a/dump_keys.py +++ b/dump_keys.py @@ -15,13 +15,20 @@ def main(): parser = argparse.ArgumentParser(description='Android Widevine L3 dumper.') parser.add_argument('--cdm-version', help='The CDM version of the device e.g. \'14.0.0\'', default='14.0.0') parser.add_argument('--function-name', help='The name of the function to hook to retrieve the private key.', default='') + parser.add_argument('--module-name', + nargs='+', + type=str, + help='The names of the widevine `.so` modules', + default=["libwvaidl.so", "libwvhidl.so"] + ) args = parser.parse_args() dynamic_function_name = args.function_name cdm_version = args.cdm_version + module_names = args.module_name logger = logging.getLogger("main") - device = Device(dynamic_function_name, cdm_version) + device = Device(dynamic_function_name, cdm_version, module_names) logger.info('Connected to %s', device.name) logger.info('Scanning all processes') @@ -29,7 +36,7 @@ def main(): if 'drm' in process.name: for library in device.find_widevine_process(process.name): device.hook_to_process(process.name, library) - logger.info('Functions Hooked, load the DRM stream test on Bitmovin!') + logger.info('Functions hooked, now open the DRM stream test on Bitmovin from your Android device! https://bitmovin.com/demos/drm') if __name__ == '__main__':