diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index c98d9f7..e6f4d9f 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -5,7 +5,6 @@ import logging import os -import sys import tarfile import click @@ -53,28 +52,33 @@ def cli(): help="File containing raw encryption key to use to decrypt the backup", mutually_exclusive=["password"]) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) -def decrypt_backup(destination, password, key_file, backup_path): +@click.pass_context +def decrypt_backup(ctx, destination, password, key_file, backup_path): backup = DecryptBackup(backup_path, destination) if key_file: if PASSWD_ENV in os.environ: - log.info(f"Ignoring {PASSWD_ENV} environment variable, using --key-file '{key_file}' instead") + log.info("Ignoring environment variable, using --key-file '%s' instead", + PASSWD_ENV, key_file) backup.decrypt_with_key_file(key_file) elif password: log.info("Your password may be visible in the process table because it was supplied on the command line!") if PASSWD_ENV in os.environ: - log.info(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + log.info("Ignoring %s environment variable, using --password argument instead", + PASSWD_ENV) backup.decrypt_with_password(password) elif PASSWD_ENV in os.environ: - log.info(f"Using password from {PASSWD_ENV} environment variable") + log.info("Using password from %s environment variable", PASSWD_ENV) backup.decrypt_with_password(os.environ[PASSWD_ENV]) else: sekrit = Prompt.ask("Enter backup password", password=True) backup.decrypt_with_password(sekrit) + if not backup.can_process(): + ctx.exit(1) backup.process_backup() @@ -96,9 +100,10 @@ def extract_key(password, backup_path, key_file): log.info("Your password may be visible in the process table because it was supplied on the command line!") if PASSWD_ENV in os.environ: - log.info(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + log.info("Ignoring %s environment variable, using --password argument instead", + PASSWD_ENV) elif PASSWD_ENV in os.environ: - log.info(f"Using password from {PASSWD_ENV} environment variable") + log.info("Using password from %s environment variable", PASSWD_ENV) password = os.environ[PASSWD_ENV] else: password = Prompt.ask("Enter backup password", password=True) @@ -120,7 +125,8 @@ def extract_key(password, backup_path, key_file): @click.option("--list-modules", "-l", is_flag=True, help="Print list of available modules and exit") @click.option("--module", "-m", help="Name of a single module you would like to run instead of all") @click.argument("BACKUP_PATH", type=click.Path(exists=True)) -def check_backup(iocs, output, fast, backup_path, list_modules, module): +@click.pass_context +def check_backup(ctx, iocs, output, fast, backup_path, list_modules, module): if list_modules: log.info("Following is the list of available check-backup modules:") for backup_module in BACKUP_MODULES: @@ -135,7 +141,7 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): os.makedirs(output) except Exception as e: log.critical("Unable to create output folder %s: %s", output, e) - sys.exit(-1) + ctx.exit(1) if iocs: # Pre-load indicators for performance reasons. @@ -177,7 +183,8 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): @click.option("--list-modules", "-l", is_flag=True, help="Print list of available modules and exit") @click.option("--module", "-m", help="Name of a single module you would like to run instead of all") @click.argument("DUMP_PATH", type=click.Path(exists=True)) -def check_fs(iocs, output, fast, dump_path, list_modules, module): +@click.pass_context +def check_fs(ctx, iocs, output, fast, dump_path, list_modules, module): if list_modules: log.info("Following is the list of available check-fs modules:") for fs_module in FS_MODULES: @@ -192,7 +199,7 @@ def check_fs(iocs, output, fast, dump_path, list_modules, module): os.makedirs(output) except Exception as e: log.critical("Unable to create output folder %s: %s", output, e) - sys.exit(-1) + ctx.exit(1) if iocs: # Pre-load indicators for performance reasons. diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index fe50af5..6393767 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -8,6 +8,7 @@ import logging import os import shutil import sqlite3 +import glob from iOSbackup import iOSbackup @@ -28,6 +29,9 @@ class DecryptBackup: self._backup = None self._decryption_key = None + def can_process(self) -> bool: + return self._backup is not None + def process_backup(self): if not os.path.exists(self.dest_path): os.makedirs(self.dest_path) @@ -73,6 +77,17 @@ class DecryptBackup: """ log.info("Decrypting iOS backup at path %s with password", self.backup_path) + if not os.path.exists(os.path.join(self.backup_path, "Manifest.plist")): + possible = glob.glob(os.path.join(self.backup_path, "*", "Manifest.plist")) + if len(possible) == 1: + newpath = os.path.dirname(possible[0]) + log.warning("No Manifest.plist in %s, using %s instead.", + self.backup_path, newpath) + self.backup_path = newpath + elif len(possible) > 1: + log.critical("No Manifest.plist in %s, and %d Manifest.plist files in subdirs. Please choose one!", + self.backup_path, len(possible)) + return try: self._backup = iOSbackup(udid=os.path.basename(self.backup_path), cleartextpassword=password, @@ -81,7 +96,8 @@ class DecryptBackup: if isinstance(e, KeyError) and len(e.args) > 0 and e.args[0] == b"KEY": log.critical("Failed to decrypt backup. Password is probably wrong.") elif isinstance(e, FileNotFoundError) and os.path.basename(e.filename) == "Manifest.plist": - log.critical(f"Failed to find a valid backup at {self.backup_path}. Did you point to the right backup path?") + log.critical("Failed to find a valid backup at %s. Did you point to the right backup path?", + self.backup_path) else: log.exception(e) log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?")