Generate WVD

This commit is contained in:
FoxRefire 2024-05-21 21:26:31 +09:00
parent 2e79463ee8
commit daaab4e6aa
3 changed files with 21 additions and 3 deletions

View File

@ -57,7 +57,7 @@ For a detailed step-by-step guide on setting up and executing KeyDive without in
### Command-Line Options ### Command-Line Options
```shell ```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. Extract Widevine L3 keys from an Android device.
@ -68,6 +68,7 @@ options:
Target Android device ID. Target Android device ID.
-f FUNCTIONS, --functions FUNCTIONS -f FUNCTIONS, --functions FUNCTIONS
Path to Ghidra XML functions file. Path to Ghidra XML functions file.
-w, --wvd Generate WVD
--force Force using the default vendor (skipping analysis). --force Force using the default vendor (skipping analysis).
``` ```

View File

@ -11,6 +11,7 @@ from Cryptodome.PublicKey import RSA
from extractor.license_protocol_pb2 import SignedMessage, LicenseRequest, ClientIdentification, DrmCertificate, SignedDrmCertificate from extractor.license_protocol_pb2 import SignedMessage, LicenseRequest, ClientIdentification, DrmCertificate, SignedDrmCertificate
from extractor.vendor import Vendor from extractor.vendor import Vendor
from pywidevine.device import Device, DeviceTypes
SCRIPT_PATH = Path(__file__).parent / 'script.js' SCRIPT_PATH = Path(__file__).parent / 'script.js'
@ -28,7 +29,7 @@ class Cdm:
# Add more as needed for different versions. # 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.logger = logging.getLogger('Cdm')
self.functions = functions self.functions = functions
self.running = True 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.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) self.logger.info('Device: %s (%s)', self.device.name, self.device.id)
# Select if create WVD or not
self.wvd = wvd
# Obtain device properties # Obtain device properties
self.properties = self._fetch_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 client ID: %s', path_client_id)
self.logger.info('Dumped private key: %s', path_private_key) 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 self.running = False
else: else:
self.logger.warning('Failed to intercept the private key') self.logger.warning('Failed to intercept the private key')

View File

@ -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('-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('-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('-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).') parser.add_argument('--force', required=False, action='store_true', help='Force using the default vendor (skipping analysis).')
args = parser.parse_args() 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') 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 # 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 # Attempt to locate and identify the Widevine process on the target device
pid = cdm.enumerate_processes().get(cdm.vendor.process) pid = cdm.enumerate_processes().get(cdm.vendor.process)