From c00a64a02f3eb4559ceb2d65a139ac575c76c79f Mon Sep 17 00:00:00 2001 From: n0stal6ic Date: Mon, 18 May 2026 12:15:01 -0500 Subject: [PATCH] Add files via upload --- kbxdecoder.py | 90 +++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/kbxdecoder.py b/kbxdecoder.py index 13fbcf8..34a9480 100644 --- a/kbxdecoder.py +++ b/kbxdecoder.py @@ -67,9 +67,8 @@ class ScanStats: wvd_generated: int = 0 def report(self): - log.info("\n─────────────────────────────────────────") - log.info(" Complete") - log.info("─────────────────────────────────────────") + log.info("") + log.info("Done.") log.info(f" Files scanned : {self.files_scanned}") log.info(f" Candidates found : {self.candidates_found}") log.info(f" Filter rejected : {self.filter_rejected}") @@ -79,7 +78,6 @@ class ScanStats: log.info(f" Decrypt failed : {self.decrypt_failed}") if self.wvd_generated: log.info(f" WVD files made : {self.wvd_generated}") - log.info("─────────────────────────────────────────") def clean_hex(s: str) -> bytes: @@ -194,7 +192,7 @@ def _save_cache(cache_path: str, keys: List[str], sources: List[str]) -> None: f.write("\n") for k in keys: f.write(k + "\n") - log.info(f" [i] Key cache updated -> {cache_path} ({len(keys)} keys)") + log.info(f" Key cache updated: {cache_path} ({len(keys)} keys)") @dataclass @@ -221,7 +219,7 @@ def build_key_database( file_sources.append(arg) else: log.warning( - f" [!] --key argument not recognised as hex, URL, or file: {arg!r}" + f" Warning: --key argument not recognised as hex, URL, or file: {arg!r}" ) seen_ecb = set() seen_cbc = set() @@ -249,18 +247,18 @@ def build_key_database( _add_ecb(k) for k, iv in _parse_cbc_keys_from_text(text): _add_cbc(k, iv) - log.info(f" [i] Loaded keys from file: {fpath}") + log.info(f" Loaded keys from file: {fpath}") except Exception as e: - log.warning(f" [!] Could not read key file {fpath}: {e}") + log.warning(f" Could not read key file {fpath}: {e}") if not url_sources: return db if not HAS_REQUESTS: - log.warning(" [!] 'requests' not installed, cannot download key URLs.") - log.warning(" Install with: pip install requests") + log.warning(" 'requests' not installed, cannot download key URLs.") + log.warning(" Install it with: pip install requests") return db cache_file = _cache_path(output_dir) if not force_update and _cache_is_fresh(cache_file): - log.info(f" [i] Using cached keys: {cache_file}") + log.info(f" Using cached keys from {cache_file}") for k in _load_cache(cache_file): _add_ecb(k) return db @@ -277,10 +275,10 @@ def build_key_database( for k, iv in new_cbc: _add_cbc(k, iv) log.info( - f" [i] Downloaded {len(new_keys)} ECB + {len(new_cbc)} CBC keys from {url}" + f" Downloaded {len(new_keys)} ECB + {len(new_cbc)} CBC keys from {url}" ) except Exception as e: - log.warning(f" [!] Failed to download {url}: {e}") + log.warning(f" Failed to download {url}: {e}") _save_cache(cache_file, downloaded, url_sources) return db @@ -663,9 +661,9 @@ def extract_from_file( raw_path = os.path.join(output_dir, raw_stem + ".bin") with open(raw_path, "wb") as out: out.write(block) - log.info(f"[{os.path.basename(path)} | Hit #{saved}]") - log.info(f" Raw keybox -> {raw_path}") - log.info(f" Offset : 0x{ppos:X} (postfix @ 0x{qpos:X})") + log.info(f"{os.path.basename(path)} hit #{saved}:") + log.info(f" Raw keybox saved to {raw_path}") + log.info(f" Offset 0x{ppos:X} (postfix at 0x{qpos:X})") if PRINT_HEXVIEW: log.info(hexview(block, base_offset=ppos, width=HEXVIEW_WIDTH)) log.info("") @@ -685,26 +683,26 @@ def extract_from_file( if not no_decrypt: if not HAS_CRYPTO: log.warning( - " [!] pycryptodome not installed, skipping decryption." + " pycryptodome not installed, skipping decryption." ) - log.warning(" Install with: pip install pycryptodome") + log.warning(" Install it with: pip install pycryptodome") elif not (db.ecb or db.cbc): log.warning( - " [!] No keys provided, use --key to supply keys." + " No keys provided. Use --key to supply keys." ) else: ecb_count = len(db.ecb) cbc_count = len(db.cbc) log.info( - f" [~] Trying {ecb_count} ECB + {cbc_count} CBC key(s) across 5 AES modes..." + f" Trying {ecb_count} ECB and {cbc_count} CBC key(s) across 5 AES modes..." ) results = decrypt_blob( block, db, stop_on_first=stop_on_first_key ) if not results: stats.decrypt_failed += 1 - log.info(" [-] No matching decryption key found.") - log.info(" Supply keys with --key ") + log.info(" No matching decryption key found.") + log.info(" Supply more keys with --key .") else: for r_idx, r in enumerate(results): stats.decrypted += 1 @@ -731,9 +729,9 @@ def extract_from_file( if r.iv_used else "" ) - log.info(f" [+] Decrypted -> {dec_path}") - log.info(f" [+] Key : {r.key_hex}") - log.info(f" [+] Mode : {r.mode_name}{iv_info}") + log.info(f" Decrypted payload saved to {dec_path}") + log.info(f" Key : {r.key_hex}") + log.info(f" Mode : {r.mode_name}{iv_info}") entry["decrypted"] = True entry["decrypt_key"] = r.key_hex entry["decrypt_mode"] = r.mode_name @@ -741,7 +739,7 @@ def extract_from_file( if wvkb: crc_status = "OK" if wvkb.crc_valid else "MISMATCH" log.info( - f" [+] Device ID : {dev_id_str} (CRC: {crc_status})" + f" Device ID : {dev_id_str} (CRC: {crc_status})" ) entry["wvkb_device_id"] = dev_id_str entry["wvkb_crc_valid"] = wvkb.crc_valid @@ -750,14 +748,14 @@ def extract_from_file( ) with open(wvkb_path, "wb") as wf: wf.write(wvkb.raw) - log.info(f" [+] WV Keybox -> {wvkb_path}") + log.info(f" Widevine keybox saved to {wvkb_path}") if extract_wvd: if not HAS_PYWIDEVINE: log.info( - " [i] pywidevine not installed, cannot generate WVD." + " pywidevine not installed, cannot generate WVD." ) log.info( - " Install with: pip install pywidevine" + " Install it with: pip install pywidevine" ) else: client_id_path = os.path.join( @@ -784,22 +782,22 @@ def extract_from_file( stats.wvd_generated += 1 entry["wvd_file"] = wvd_path log.info( - f" [+] WVD -> {wvd_path}" + f" WVD saved to {wvd_path}" ) else: - log.info(" [!] WVD generation failed.") + log.info(" WVD generation failed.") else: log.info( - " [i] WVD generation requires provisioned client_id.bin" + " WVD generation requires a provisioned client_id.bin" ) log.info( - " + private_key.pem in the same directory as this script." + " and private_key.pem in the same directory as this script." ) log.info( - " The raw keybox has been saved, use it to provision" + " The raw keybox has been saved. Use it to provision" ) log.info( - " the device and obtain these files first." + " the device and obtain these two files first." ) manifest_entries.append(entry) log.info("") @@ -814,13 +812,13 @@ def write_manifest(entries: list, output_dir: str): path = os.path.join(output_dir, "manifest.json") with open(path, "w") as f: json.dump(entries, f, indent=2) - log.info(f"Manifest written -> {path}") + log.info(f"Manifest written to {path}") def main() -> int: parser = argparse.ArgumentParser( prog="kbxdecoder", - description="Extract and decode MStar/MediaTek Widevine keyboxes from eMMC dumps and firmware images. Decryption from ECB, CBC, CFB, OFB, and CTR modes using supplied keys (hex string, local file, or URL).", + description="Extract and decode MStar/MediaTek Widevine keyboxes from eMMC dumps and firmware images. Decryption using supplied keys (hex string, local file, or URL).", ) parser.add_argument("files", nargs="*", help="One or more binary files to scan.") parser.add_argument( @@ -896,26 +894,26 @@ def main() -> int: os.makedirs(args.output_dir, exist_ok=True) if not args.quiet and (not args.no_decrypt): if not HAS_CRYPTO: - log.info("[!] pycryptodome not found, decryption disabled.") - log.info(" Install with: pip install pycryptodome") + log.info("pycryptodome not found, decryption disabled.") + log.info("Install it with: pip install pycryptodome") if args.extract_wvd and (not HAS_PYWIDEVINE): - log.info("[i] pywidevine not found, WVD generation disabled.") - log.info(" Install with: pip install pywidevine") + log.info("pywidevine not found, WVD generation disabled.") + log.info("Install it with: pip install pywidevine") log.info("") db = KeyDatabase() if not args.no_decrypt and args.key: - log.info("[*] Resolving key sources...") + log.info("Resolving key sources...") db = build_key_database( key_args=args.key, output_dir=args.output_dir, force_update=args.force_update_keys, only_custom=args.only_custom, ) - log.info(f" ECB candidates : {len(db.ecb)}") - log.info(f" CBC pairs : {len(db.cbc)}") + log.info(f" ECB candidates : {len(db.ecb)}") + log.info(f" CBC pairs : {len(db.cbc)}") log.info("") elif not args.no_decrypt and (not args.key): - log.info("[!] No keys supplied. Use --key to enable decryption.") + log.info("No keys supplied. Use --key to enable decryption.") log.info("") stats = ScanStats() manifest_entries = [] @@ -956,4 +954,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file