diff --git a/Whatsapp_Chat_Exporter/__main__.py b/Whatsapp_Chat_Exporter/__main__.py index 1784723..bf130be 100644 --- a/Whatsapp_Chat_Exporter/__main__.py +++ b/Whatsapp_Chat_Exporter/__main__.py @@ -229,6 +229,10 @@ def setup_argument_parser() -> ArgumentParser: "--decrypt-chunk-size", dest="decrypt_chunk_size", default=1 * 1024 * 1024, type=int, help="Specify the chunk size for decrypting iOS backup, which may affect the decryption speed." ) + misc_group.add_argument( + "--max-bruteforce-worker", dest="max_bruteforce_worker", default=10, type=int, + help="Specify the maximum number of worker for bruteforce decryption." + ) return parser @@ -398,7 +402,8 @@ def decrypt_android_backup(args) -> int: crypt, args.showkey, DbType.CONTACT, - keyfile_stream=keyfile_stream + keyfile_stream=keyfile_stream, + max_worker=args.max_bruteforce_worker ) if isinstance(key, io.IOBase): key.seek(0) @@ -411,7 +416,8 @@ def decrypt_android_backup(args) -> int: crypt, args.showkey, DbType.MESSAGE, - keyfile_stream=keyfile_stream + keyfile_stream=keyfile_stream, + max_worker=args.max_bruteforce_worker ) # Handle errors diff --git a/Whatsapp_Chat_Exporter/android_crypt.py b/Whatsapp_Chat_Exporter/android_crypt.py index 8e83ed5..2f764dd 100644 --- a/Whatsapp_Chat_Exporter/android_crypt.py +++ b/Whatsapp_Chat_Exporter/android_crypt.py @@ -114,12 +114,13 @@ def _decrypt_database(db_ciphertext: bytes, main_key: bytes, iv: bytes) -> bytes ) return db -def _decrypt_crypt14(database: bytes, main_key: bytes) -> bytes: +def _decrypt_crypt14(database: bytes, main_key: bytes, max_worker: int = 10) -> bytes: """Decrypt a crypt14 database using multithreading for brute-force offset detection. Args: database (bytes): The encrypted database. main_key (bytes): The decryption key. + max_worker (int, optional): The maximum number of threads to use for brute force. Defaults to 10. Returns: bytes: The decrypted database. @@ -164,7 +165,7 @@ def _decrypt_crypt14(database: bytes, main_key: bytes) -> bytes: except (zlib.error, ValueError): return None # Decryption failed, move to next - with concurrent.futures.ThreadPoolExecutor(10) as executor: + with concurrent.futures.ThreadPoolExecutor(max_worker) as executor: future_to_offset = {executor.submit(attempt_decrypt, offset): offset for offset in offset_combinations} try: @@ -247,7 +248,8 @@ def decrypt_backup( db_type: DbType = DbType.MESSAGE, *, dry_run: bool = False, - keyfile_stream: bool = False + keyfile_stream: bool = False, + max_worker: int = 10 ) -> int: """ Decrypt the WhatsApp backup database. @@ -308,7 +310,7 @@ def decrypt_backup( try: if crypt == Crypt.CRYPT14: - db = _decrypt_crypt14(database, main_key) + db = _decrypt_crypt14(database, main_key, max_worker) elif crypt == Crypt.CRYPT12: db = _decrypt_crypt12(database, main_key) elif crypt == Crypt.CRYPT15: