diff --git a/mvt/android/cli.py b/mvt/android/cli.py index f4f5e97..a291539 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -28,7 +28,7 @@ log = logging.getLogger(__name__) # Help messages of repeating options. OUTPUT_HELP_MESSAGE = "Specify a path to a folder where you want to store JSON results" - +SERIAL_HELP_MESSAGE = "Specify a device serial number or HOST:PORT connection string" #============================================================================== # Main @@ -42,6 +42,7 @@ def cli(): # Download APKs #============================================================================== @cli.command("download-apks", help="Download all or non-safelisted installed APKs installed on the device") +@click.option("--serial", "-s", type=str, help=SERIAL_HELP_MESSAGE) @click.option("--all-apks", "-a", is_flag=True, help="Extract all packages installed on the phone, even those marked as safe") @click.option("--virustotal", "-v", is_flag=True, help="Check packages on VirusTotal") @@ -51,7 +52,7 @@ def cli(): help="Specify a path to a folder where you want to store the APKs") @click.option("--from-file", "-f", type=click.Path(exists=True), help="Instead of acquiring from phone, load an existing packages.json file for lookups (mainly for debug purposes)") -def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): +def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file, serial): try: if from_file: download = DownloadAPKs.from_json(from_file) @@ -64,6 +65,8 @@ def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): sys.exit(-1) download = DownloadAPKs(output_folder=output, all_apks=all_apks) + if serial: + download.serial = serial download.run() packages = download.packages @@ -85,12 +88,13 @@ def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): # Checks through ADB #============================================================================== @cli.command("check-adb", help="Check an Android device over adb") +@click.option("--serial", "-s", type=str, help=SERIAL_HELP_MESSAGE) @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") @click.option("--output", "-o", type=click.Path(exists=False), help="Specify a path to a folder where you want to store JSON results") @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") -def check_adb(iocs, output, list_modules, module): +def check_adb(iocs, output, list_modules, module, serial): if list_modules: log.info("Following is the list of available check-adb modules:") for adb_module in ADB_MODULES: @@ -119,6 +123,8 @@ def check_adb(iocs, output, list_modules, module): continue m = adb_module(output_folder=output, log=logging.getLogger(adb_module.__module__)) + if serial: + m.serial = serial if iocs: indicators.log = m.log @@ -138,10 +144,11 @@ def check_adb(iocs, output, list_modules, module): # Check ADB backup #============================================================================== @cli.command("check-backup", help="Check an Android Backup") +@click.option("--serial", "-s", type=str, help=SERIAL_HELP_MESSAGE) @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") @click.option("--output", "-o", type=click.Path(exists=False), help=OUTPUT_HELP_MESSAGE) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) -def check_backup(iocs, output, backup_path): +def check_backup(iocs, output, backup_path, serial): log.info("Checking ADB backup located at: %s", backup_path) if output and not os.path.exists(output): @@ -168,6 +175,9 @@ def check_backup(iocs, output, backup_path): m = module(base_folder=backup_path, output_folder=output, log=logging.getLogger(module.__module__)) + if serial: + m.serial = serial + if iocs: indicators.log = m.log m.indicators = indicators diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index 5c79729..8cfe6b8 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -40,15 +40,11 @@ class DownloadAPKs(AndroidExtraction): """DownloadAPKs is the main class operating the download of APKs from the device.""" - def __init__(self, output_folder=None, all_apks=False, packages=None): - """Initialize module. - :param output_folder: Path to the folder where data should be stored - :param all_apks: Boolean indicating whether to download all packages - or filter known-goods - :param packages: Provided list of packages, typically for JSON checks - """ - super().__init__(file_path=None, base_folder=None, - output_folder=output_folder) + def __init__(self, file_path=None, base_folder=None, output_folder=None, + serial=None, fast_mode=False, log=None, results=[]): + super().__init__(file_path=file_path, base_folder=base_folder, + output_folder=output_folder, fast_mode=fast_mode, + log=log, results=results) self.output_folder_apk = None self.packages = packages or [] diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 0c8019b..71832e5 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -10,8 +10,7 @@ import string import sys import tempfile import time - -from adb_shell.adb_device import AdbDeviceUsb +from adb_shell.adb_device import AdbDeviceUsb, AdbDeviceTcp from adb_shell.auth.keygen import keygen, write_public_keyfile from adb_shell.auth.sign_pythonrsa import PythonRSASigner from adb_shell.exceptions import AdbCommandFailureException, DeviceAuthError @@ -40,6 +39,7 @@ class AndroidExtraction(MVTModule): log=log, results=results) self.device = None + self.serial = None def _adb_check_keys(self): """Make sure Android adb keys exist. @@ -59,7 +59,19 @@ class AndroidExtraction(MVTModule): priv_key = handle.read() signer = PythonRSASigner("", priv_key) - self.device = AdbDeviceUsb() + + # If no serial was specified or if the serial does not seem to be + # a HOST:PORT definition, we use the USB transport. + if not self.serial or ":" not in self.serial: + self.device = AdbDeviceUsb(serial=self.serial) + # Otherwise we try to use the TCP transport. + else: + addr = self.serial.split(":") + if len(addr) < 2: + raise ValueError("TCP serial number must follow the format: `address:port`") + + self.device = AdbDeviceTcp(addr[0], int(addr[1]), + default_transport_timeout_s=30.) while True: try: diff --git a/mvt/android/modules/adb/chrome_history.py b/mvt/android/modules/adb/chrome_history.py index dc61f37..f163147 100644 --- a/mvt/android/modules/adb/chrome_history.py +++ b/mvt/android/modules/adb/chrome_history.py @@ -20,7 +20,7 @@ class ChromeHistory(AndroidExtraction): """This module extracts records from Android's Chrome browsing history.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/dumpsys_batterystats.py b/mvt/android/modules/adb/dumpsys_batterystats.py index c4b3cf4..b804c2f 100644 --- a/mvt/android/modules/adb/dumpsys_batterystats.py +++ b/mvt/android/modules/adb/dumpsys_batterystats.py @@ -14,7 +14,7 @@ class DumpsysBatterystats(AndroidExtraction): """This module extracts stats on battery consumption by processes.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/dumpsys_packages.py b/mvt/android/modules/adb/dumpsys_packages.py index becb5f3..3e87769 100644 --- a/mvt/android/modules/adb/dumpsys_packages.py +++ b/mvt/android/modules/adb/dumpsys_packages.py @@ -14,7 +14,7 @@ class DumpsysPackages(AndroidExtraction): """This module extracts stats on installed packages.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/dumpsys_procstats.py b/mvt/android/modules/adb/dumpsys_procstats.py index 44e2cd2..a2a69c7 100644 --- a/mvt/android/modules/adb/dumpsys_procstats.py +++ b/mvt/android/modules/adb/dumpsys_procstats.py @@ -14,7 +14,7 @@ class DumpsysProcstats(AndroidExtraction): """This module extracts stats on memory consumption by processes.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/packages.py b/mvt/android/modules/adb/packages.py index dcfe50f..54d1158 100644 --- a/mvt/android/modules/adb/packages.py +++ b/mvt/android/modules/adb/packages.py @@ -16,7 +16,7 @@ class Packages(AndroidExtraction): """This module extracts the list of installed packages.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/processes.py b/mvt/android/modules/adb/processes.py index 6bc1406..6350416 100644 --- a/mvt/android/modules/adb/processes.py +++ b/mvt/android/modules/adb/processes.py @@ -13,7 +13,7 @@ class Processes(AndroidExtraction): """This module extracts details on running processes.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/rootbinaries.py b/mvt/android/modules/adb/rootbinaries.py index 6b9258a..65e45dd 100644 --- a/mvt/android/modules/adb/rootbinaries.py +++ b/mvt/android/modules/adb/rootbinaries.py @@ -16,7 +16,7 @@ class RootBinaries(AndroidExtraction): """This module extracts the list of installed packages.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index 77e2634..5db5652 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -43,7 +43,7 @@ class SMS(AndroidExtraction): """This module extracts all SMS messages containing links.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/android/modules/adb/whatsapp.py b/mvt/android/modules/adb/whatsapp.py index e02f892..7a8fe37 100644 --- a/mvt/android/modules/adb/whatsapp.py +++ b/mvt/android/modules/adb/whatsapp.py @@ -20,7 +20,7 @@ class Whatsapp(AndroidExtraction): """This module extracts all WhatsApp messages containing links.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) diff --git a/mvt/common/module.py b/mvt/common/module.py index 74ed28a..e1c1d40 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -31,11 +31,12 @@ class MVTModule(object): slug = None def __init__(self, file_path=None, base_folder=None, output_folder=None, - fast_mode=False, log=None, results=[]): + serial=None, fast_mode=False, log=None, results=[]): """Initialize module. :param file_path: Path to the module's database file, if there is any. :param base_folder: Path to the base folder (backup or filesystem dump) :param output_folder: Folder where results will be stored + :param serial: The USB device serial ID :param fast_mode: Flag to enable or disable slow modules :param log: Handle to logger :param results: Provided list of results entries @@ -43,6 +44,7 @@ class MVTModule(object): self.file_path = file_path self.base_folder = base_folder self.output_folder = output_folder + self.serial= serial self.fast_mode = fast_mode self.log = log self.indicators = None