From be511dcb515b74f0f6973852b811a052ccf9bdaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Fri, 4 Mar 2022 17:06:10 +0100 Subject: [PATCH] Refactor SMS ADB code to use backup functions --- mvt/android/modules/adb/base.py | 28 ++++++++++++++++++++++++++++ mvt/android/modules/adb/sms.py | 25 +++---------------------- mvt/android/parsers/backup.py | 8 -------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 2ad9b87..6d4bfb8 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -19,6 +19,7 @@ from adb_shell.exceptions import (AdbCommandFailureException, DeviceAuthError, from usb1 import USBErrorAccess, USBErrorBusy from mvt.common.module import InsufficientPrivileges, MVTModule +from mvt.android.parsers.backup import parse_ab_header, parse_backup_file log = logging.getLogger(__name__) @@ -243,6 +244,33 @@ class AndroidExtraction(MVTModule): # Disconnect from the device. self._adb_disconnect() + def _generate_backup(package_name): + # Run ADB command to create a backup of SMS app + self.log.warning("Please check phone and accept Android backup prompt. You may need to set a backup password. \a") + + # Run ADB command to create a backup of SMS app + # TODO: Base64 encoding as temporary fix to avoid byte-mangling over the shell transport... + backup_output_b64 = self._adb_command("/system/bin/bu backup -nocompress '{}' | base64".format(package_name)) + backup_output = base64.b64decode(backup_output_b64) + header = parse_ab_header(backup_output) + if not header["backup"]: + self.log.error("Extracting SMS via Android backup failed. No valid backup data found.") + return + + if header["encryption"] == "none": + return parse_backup_file(backup_output, password=None) + + # Backup encrypted. Request password from user. + while password_retry in range(0, 3): + backup_password = getpass.getpass(prompt="Backup Password: ", stream=None) + try: + decrypted_backup_tar = parse_backup_file(backup_output, backup_password) + return decrypted_backup_tar + except InvalidBackupPassword: + self.log.info("Invalid backup password") + + self.log.warn("All attempts to decrypt backup with password failed!") + def run(self): """Run the main procedure.""" raise NotImplementedError diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index 10000db..d323cd1 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -10,7 +10,7 @@ import base64 import getpass from mvt.common.utils import check_for_links, convert_timestamp_to_iso -from mvt.android.parsers.backup import parse_ab_header, parse_sms_backup, InvalidBackupPassword, AndroidBackupParsingError +from mvt.android.parsers.backup import parse_tar_for_sms, AndroidBackupParsingError from mvt.common.module import InsufficientPrivileges from .base import AndroidExtraction @@ -116,28 +116,9 @@ class SMS(AndroidExtraction): It is crucial to use the under-documented "-nocompress" flag to disable the non-standard Java compression algorithim. This module only supports an unencrypted ADB backup. """ - # Run ADB command to create a backup of SMS app - self.log.warning("Please check phone and accept Android backup prompt. Do not set an encryption password. \a") - - # Run ADB command to create a backup of SMS app - # TODO: Base64 encoding as temporary fix to avoid byte-mangling over the shell transport... - backup_output_b64 = self._adb_command("/system/bin/bu backup -nocompress com.android.providers.telephony | base64") - backup_output = base64.b64decode(backup_output_b64) - header = parse_ab_header(backup_output) - if not header["backup"]: - self.log.error("Extracting SMS via Android backup failed. No valid backup data found.") - return - - pwd = None - if header["encryption"] != "none": - pwd = getpass.getpass(prompt="Backup Password: ", stream=None) - - + backup_tar = self._generate_backup("com.android.providers.telephony") try: - self.results = parse_sms_backup(backup_output, password=pwd) - except InvalidBackupPassword: - self.log.info("Invalid backup password") - return + self.results = parse_tar_for_sms(backup_tar) except AndroidBackupParsingError: self.log.info("Impossible to read SMS from the Android Backup, please extract the SMS and try extracting it with Android Backup Extractor") return diff --git a/mvt/android/parsers/backup.py b/mvt/android/parsers/backup.py index c9c2a1d..85b1865 100644 --- a/mvt/android/parsers/backup.py +++ b/mvt/android/parsers/backup.py @@ -42,14 +42,6 @@ def to_utf8_bytes(input_bytes): return bytes(output) -def parse_sms_backup(data, password=None): - """ - Parse a backup file and returns SMS in it - """ - tardata = parse_backup_file(data, password=password) - return parse_tar_for_sms(tardata) - - def parse_ab_header(data): """ Parse the header of an Android Backup file