From 3ec3b86a45d7d20ffbcde9c6b0bfe6061e430bd7 Mon Sep 17 00:00:00 2001 From: Tek Date: Wed, 26 Jul 2023 13:53:54 +0200 Subject: [PATCH] Adds support for zip files in check-androidqf command (#372) --- mvt/android/cli.py | 4 +- mvt/android/cmd_check_androidqf.py | 30 ++++++++++++++- mvt/android/cmd_check_backup.py | 2 +- mvt/android/modules/adb/base.py | 2 +- mvt/android/modules/androidqf/base.py | 30 +++++++++++---- .../androidqf/dumpsys_accessibility.py | 24 ++++++------ .../modules/androidqf/dumpsys_activities.py | 24 ++++++------ .../modules/androidqf/dumpsys_appops.py | 22 +++++------ .../modules/androidqf/dumpsys_packages.py | 5 +-- .../modules/androidqf/dumpsys_receivers.py | 24 ++++++------ mvt/android/modules/androidqf/getprop.py | 3 +- mvt/android/modules/androidqf/processes.py | 4 +- mvt/android/modules/androidqf/settings.py | 23 ++++++------ mvt/android/modules/androidqf/sms.py | 7 +--- mvt/android/modules/backup/helpers.py | 1 - .../test_dumpsysaccessbility.py | 7 +++- tests/android_androidqf/test_dumpsysappops.py | 7 +++- .../android_androidqf/test_dumpsyspackages.py | 9 ++++- .../test_dumpsysreceivers.py | 7 +++- tests/android_androidqf/test_getprop.py | 33 +++++++++++++---- tests/android_androidqf/test_processes.py | 12 +++--- tests/android_androidqf/test_settings.py | 7 +++- tests/android_androidqf/test_sms.py | 35 ++++++++++++++---- tests/artifacts/androidqf.zip | Bin 0 -> 492 bytes tests/test_check_android_androidqf.py | 1 - tests/test_check_android_backup.py | 3 +- tests/utils.py | 13 +++++++ 27 files changed, 225 insertions(+), 114 deletions(-) create mode 100644 tests/artifacts/androidqf.zip diff --git a/mvt/android/cli.py b/mvt/android/cli.py index a7d3ba7..b1546c2 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -9,13 +9,13 @@ import click from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import ( + HELP_MSG_ANDROID_BACKUP_PASSWORD, HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, HELP_MSG_NONINTERACTIVE, - HELP_MSG_ANDROID_BACKUP_PASSWORD, HELP_MSG_OUTPUT, HELP_MSG_SERIAL, HELP_MSG_VERBOSE, @@ -32,8 +32,8 @@ from .cmd_download_apks import DownloadAPKs from .modules.adb import ADB_MODULES from .modules.adb.packages import Packages from .modules.backup import BACKUP_MODULES -from .modules.bugreport import BUGREPORT_MODULES from .modules.backup.helpers import cli_load_android_backup_password +from .modules.bugreport import BUGREPORT_MODULES init_logging() log = logging.getLogger("mvt") diff --git a/mvt/android/cmd_check_androidqf.py b/mvt/android/cmd_check_androidqf.py index ed04c7a..279c323 100644 --- a/mvt/android/cmd_check_androidqf.py +++ b/mvt/android/cmd_check_androidqf.py @@ -4,7 +4,10 @@ # https://license.mvt.re/1.1/ import logging -from typing import Optional +import os +import zipfile +from pathlib import Path +from typing import List, Optional from mvt.common.command import Command @@ -37,3 +40,28 @@ class CmdAndroidCheckAndroidQF(Command): self.name = "check-androidqf" self.modules = ANDROIDQF_MODULES + + self.format: Optional[str] = None + self.archive: Optional[zipfile.ZipFile] = None + self.files: List[str] = [] + + def init(self): + if os.path.isdir(self.target_path): + self.format = "dir" + parent_path = Path(self.target_path).absolute().parent.as_posix() + target_abs_path = os.path.abspath(self.target_path) + for root, subdirs, subfiles in os.walk(target_abs_path): + for fname in subfiles: + file_path = os.path.relpath(os.path.join(root, fname), parent_path) + self.files.append(file_path) + elif os.path.isfile(self.target_path): + self.format = "zip" + self.archive = zipfile.ZipFile(self.target_path) + self.files = self.archive.namelist() + + def module_init(self, module): + if self.format == "zip": + module.from_zip_file(self.archive, self.files) + else: + parent_path = Path(self.target_path).absolute().parent.as_posix() + module.from_folder(parent_path, self.files) diff --git a/mvt/android/cmd_check_backup.py b/mvt/android/cmd_check_backup.py index 22fcd6a..76c8c1c 100644 --- a/mvt/android/cmd_check_backup.py +++ b/mvt/android/cmd_check_backup.py @@ -12,13 +12,13 @@ from pathlib import Path from typing import List, Optional from mvt.android.modules.backup.base import BackupExtraction +from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from mvt.android.parsers.backup import ( AndroidBackupParsingError, InvalidBackupPassword, parse_ab_header, parse_backup_file, ) -from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from mvt.common.command import Command from .modules.backup import BACKUP_MODULES diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 78fff01..418f12d 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -24,12 +24,12 @@ from adb_shell.exceptions import ( ) from usb1 import USBErrorAccess, USBErrorBusy +from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from mvt.android.parsers.backup import ( InvalidBackupPassword, parse_ab_header, parse_backup_file, ) -from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from mvt.common.module import InsufficientPrivileges, MVTModule ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey") diff --git a/mvt/android/modules/androidqf/base.py b/mvt/android/modules/androidqf/base.py index b0f08c9..ae851fa 100644 --- a/mvt/android/modules/androidqf/base.py +++ b/mvt/android/modules/androidqf/base.py @@ -6,6 +6,7 @@ import fnmatch import logging import os +import zipfile from typing import Any, Dict, List, Optional, Union from mvt.common.module import MVTModule @@ -31,13 +32,28 @@ class AndroidQFModule(MVTModule): log=log, results=results, ) + self._path: str = target_path + self.files: List[str] = [] + self.archive: Optional[zipfile.ZipFile] = None - self._path = target_path - self._files = [] + def from_folder(self, parent_path: str, files: List[str]): + self.parent_path = parent_path + self.files = files - for root, dirs, files in os.walk(target_path): - for name in files: - self._files.append(os.path.join(root, name)) + def from_zip_file(self, archive: zipfile.ZipFile, files: List[str]): + self.archive = archive + self.files = files - def _get_files_by_pattern(self, pattern): - return fnmatch.filter(self._files, pattern) + def _get_files_by_pattern(self, pattern: str): + return fnmatch.filter(self.files, pattern) + + def _get_file_content(self, file_path): + if self.archive: + handle = self.archive.open(file_path) + else: + handle = open(os.path.join(self.parent_path, file_path), "rb") + + data = handle.read() + handle.close() + + return data diff --git a/mvt/android/modules/androidqf/dumpsys_accessibility.py b/mvt/android/modules/androidqf/dumpsys_accessibility.py index efd0790..a7f15c7 100644 --- a/mvt/android/modules/androidqf/dumpsys_accessibility.py +++ b/mvt/android/modules/androidqf/dumpsys_accessibility.py @@ -49,21 +49,21 @@ class DumpsysAccessibility(AndroidQFModule): lines = [] in_accessibility = False - with open(dumpsys_file[0]) as handle: - for line in handle: - if line.strip().startswith("DUMP OF SERVICE accessibility:"): - in_accessibility = True - continue + data = self._get_file_content(dumpsys_file[0]) + for line in data.decode("utf-8").split("\n"): + if line.strip().startswith("DUMP OF SERVICE accessibility:"): + in_accessibility = True + continue - if not in_accessibility: - continue + if not in_accessibility: + continue - if line.strip().startswith( - "-------------------------------------------------------------------------------" - ): # pylint: disable=line-too-long - break + if line.strip().startswith( + "-------------------------------------------------------------------------------" + ): # pylint: disable=line-too-long + break - lines.append(line.rstrip()) + lines.append(line.rstrip()) self.results = parse_dumpsys_accessibility("\n".join(lines)) diff --git a/mvt/android/modules/androidqf/dumpsys_activities.py b/mvt/android/modules/androidqf/dumpsys_activities.py index 7abd167..08b9f64 100644 --- a/mvt/android/modules/androidqf/dumpsys_activities.py +++ b/mvt/android/modules/androidqf/dumpsys_activities.py @@ -52,21 +52,21 @@ class DumpsysActivities(AndroidQFModule): lines = [] in_package = False - with open(dumpsys_file[0]) as handle: - for line in handle: - if line.strip() == "DUMP OF SERVICE package:": - in_package = True - continue + data = self._get_file_content(dumpsys_file[0]) + for line in data.decode("utf-8").split("\n"): + if line.strip() == "DUMP OF SERVICE package:": + in_package = True + continue - if not in_package: - continue + if not in_package: + continue - if line.strip().startswith( - "------------------------------------------------------------------------------" - ): # pylint: disable=line-too-long - break + if line.strip().startswith( + "------------------------------------------------------------------------------" + ): # pylint: disable=line-too-long + break - lines.append(line.rstrip()) + lines.append(line.rstrip()) self.results = parse_dumpsys_activity_resolver_table("\n".join(lines)) diff --git a/mvt/android/modules/androidqf/dumpsys_appops.py b/mvt/android/modules/androidqf/dumpsys_appops.py index c89e135..3f5efc9 100644 --- a/mvt/android/modules/androidqf/dumpsys_appops.py +++ b/mvt/android/modules/androidqf/dumpsys_appops.py @@ -76,19 +76,19 @@ class DumpsysAppops(AndroidQFModule): lines = [] in_package = False - with open(dumpsys_file[0]) as handle: - for line in handle: - if line.startswith("DUMP OF SERVICE appops:"): - in_package = True - continue + data = self._get_file_content(dumpsys_file[0]) + for line in data.decode("utf-8").split("\n"): + if line.startswith("DUMP OF SERVICE appops:"): + in_package = True + continue - if in_package: - if line.startswith( - "-------------------------------------------------------------------------------" - ): # pylint: disable=line-too-long - break + if in_package: + if line.startswith( + "-------------------------------------------------------------------------------" + ): # pylint: disable=line-too-long + break - lines.append(line.rstrip()) + lines.append(line.rstrip()) self.results = parse_dumpsys_appops("\n".join(lines)) self.log.info("Identified %d applications in AppOps Manager", len(self.results)) diff --git a/mvt/android/modules/androidqf/dumpsys_packages.py b/mvt/android/modules/androidqf/dumpsys_packages.py index b531d1d..cb9d5bb 100644 --- a/mvt/android/modules/androidqf/dumpsys_packages.py +++ b/mvt/android/modules/androidqf/dumpsys_packages.py @@ -78,13 +78,12 @@ class DumpsysPackages(AndroidQFModule): self.log.info("Dumpsys file not found") return - with open(dumpsys_file[0]) as handle: - data = handle.read().split("\n") + data = self._get_file_content(dumpsys_file[0]) package = [] in_service = False in_package_list = False - for line in data: + for line in data.decode("utf-8").split("\n"): if line.strip().startswith("DUMP OF SERVICE package:"): in_service = True continue diff --git a/mvt/android/modules/androidqf/dumpsys_receivers.py b/mvt/android/modules/androidqf/dumpsys_receivers.py index ceea87c..d60c6d7 100644 --- a/mvt/android/modules/androidqf/dumpsys_receivers.py +++ b/mvt/android/modules/androidqf/dumpsys_receivers.py @@ -86,21 +86,21 @@ class DumpsysReceivers(AndroidQFModule): in_receivers = False lines = [] - with open(dumpsys_file[0]) as handle: - for line in handle: - if line.strip() == "DUMP OF SERVICE package:": - in_receivers = True - continue + data = self._get_file_content(dumpsys_file[0]) + for line in data.decode("utf-8").split("\n"): + if line.strip() == "DUMP OF SERVICE package:": + in_receivers = True + continue - if not in_receivers: - continue + if not in_receivers: + continue - if line.strip().startswith( - "------------------------------------------------------------------------------" - ): # pylint: disable=line-too-long - break + if line.strip().startswith( + "------------------------------------------------------------------------------" + ): # pylint: disable=line-too-long + break - lines.append(line.rstrip()) + lines.append(line.rstrip()) self.results = parse_dumpsys_receiver_resolver_table("\n".join(lines)) diff --git a/mvt/android/modules/androidqf/getprop.py b/mvt/android/modules/androidqf/getprop.py index b048001..3725a8d 100644 --- a/mvt/android/modules/androidqf/getprop.py +++ b/mvt/android/modules/androidqf/getprop.py @@ -39,8 +39,7 @@ class Getprop(GetPropArtifact, AndroidQFModule): self.log.info("getprop.txt file not found") return - with open(getprop_files[0]) as f: - data = f.read() + data = self._get_file_content(getprop_files[0]).decode("utf-8") self.parse(data) self.log.info("Extracted a total of %d properties", len(self.results)) diff --git a/mvt/android/modules/androidqf/processes.py b/mvt/android/modules/androidqf/processes.py index b743250..b7fcd46 100644 --- a/mvt/android/modules/androidqf/processes.py +++ b/mvt/android/modules/androidqf/processes.py @@ -37,7 +37,5 @@ class Processes(ProcessesArtifact, AndroidQFModule): if not ps_files: return - with open(ps_files[0]) as handle: - self.parse(handle.read()) - + self.parse(self._get_file_content(ps_files[0]).decode("utf-8")) self.log.info("Identified %d running processes", len(self.results)) diff --git a/mvt/android/modules/androidqf/settings.py b/mvt/android/modules/androidqf/settings.py index f398dc3..36978ad 100644 --- a/mvt/android/modules/androidqf/settings.py +++ b/mvt/android/modules/androidqf/settings.py @@ -38,19 +38,18 @@ class Settings(SettingsArtifact, AndroidQFModule): namespace = setting_file[setting_file.rfind("_") + 1 : -4] self.results[namespace] = {} + data = self._get_file_content(setting_file) + for line in data.decode("utf-8").split("\n"): + line = line.strip() + try: + key, value = line.split("=", 1) + except ValueError: + continue - with open(setting_file) as handle: - for line in handle: - line = line.strip() - try: - key, value = line.split("=", 1) - except ValueError: - continue - - try: - self.results[namespace][key] = value - except IndexError: - continue + try: + self.results[namespace][key] = value + except IndexError: + continue self.log.info( "Identified %d settings", sum([len(val) for val in self.results.values()]) diff --git a/mvt/android/modules/androidqf/sms.py b/mvt/android/modules/androidqf/sms.py index a8ac512..57ef570 100644 --- a/mvt/android/modules/androidqf/sms.py +++ b/mvt/android/modules/androidqf/sms.py @@ -6,6 +6,7 @@ import logging from typing import Optional +from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from mvt.android.parsers.backup import ( AndroidBackupParsingError, InvalidBackupPassword, @@ -13,7 +14,6 @@ from mvt.android.parsers.backup import ( parse_backup_file, parse_tar_for_sms, ) -from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password from .base import AndroidQFModule @@ -96,8 +96,5 @@ class SMS(AndroidQFModule): self.log.info("No backup data found") return - with open(files[0], "rb") as handle: - data = handle.read() - - self.parse_backup(data) + self.parse_backup(self._get_file_content(files[0])) self.log.info("Identified %d SMS in backup data", len(self.results)) diff --git a/mvt/android/modules/backup/helpers.py b/mvt/android/modules/backup/helpers.py index ee98823..d736381 100644 --- a/mvt/android/modules/backup/helpers.py +++ b/mvt/android/modules/backup/helpers.py @@ -7,7 +7,6 @@ import os from rich.prompt import Prompt - MVT_ANDROID_BACKUP_PASSWORD = "MVT_ANDROID_BACKUP_PASSWORD" diff --git a/tests/android_androidqf/test_dumpsysaccessbility.py b/tests/android_androidqf/test_dumpsysaccessbility.py index 918e02e..91d0ba2 100644 --- a/tests/android_androidqf/test_dumpsysaccessbility.py +++ b/tests/android_androidqf/test_dumpsysaccessbility.py @@ -3,16 +3,21 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ +from pathlib import Path + from mvt.android.modules.androidqf.dumpsys_accessibility import DumpsysAccessibility from mvt.common.module import run_module -from ..utils import get_android_androidqf +from ..utils import get_android_androidqf, list_files class TestDumpsysAccessibilityModule: def test_parsing(self): data_path = get_android_androidqf() m = DumpsysAccessibility(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 4 assert len(m.detected) == 0 diff --git a/tests/android_androidqf/test_dumpsysappops.py b/tests/android_androidqf/test_dumpsysappops.py index c1914b5..80aa98e 100644 --- a/tests/android_androidqf/test_dumpsysappops.py +++ b/tests/android_androidqf/test_dumpsysappops.py @@ -3,16 +3,21 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ +from pathlib import Path + from mvt.android.modules.androidqf.dumpsys_appops import DumpsysAppops from mvt.common.module import run_module -from ..utils import get_android_androidqf +from ..utils import get_android_androidqf, list_files class TestDumpsysAppOpsModule: def test_parsing(self): data_path = get_android_androidqf() m = DumpsysAppops(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 12 assert len(m.timeline) == 16 diff --git a/tests/android_androidqf/test_dumpsyspackages.py b/tests/android_androidqf/test_dumpsyspackages.py index a91520a..2bfb293 100644 --- a/tests/android_androidqf/test_dumpsyspackages.py +++ b/tests/android_androidqf/test_dumpsyspackages.py @@ -4,18 +4,22 @@ # https://license.mvt.re/1.1/ import logging +from pathlib import Path from mvt.android.modules.androidqf.dumpsys_packages import DumpsysPackages from mvt.common.indicators import Indicators from mvt.common.module import run_module -from ..utils import get_android_androidqf +from ..utils import get_android_androidqf, list_files class TestDumpsysPackagesModule: def test_parsing(self): data_path = get_android_androidqf() m = DumpsysPackages(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 2 assert len(m.detected) == 0 @@ -28,6 +32,9 @@ class TestDumpsysPackagesModule: def test_detection_pkgname(self, indicator_file): data_path = get_android_androidqf() m = DumpsysPackages(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) ind = Indicators(log=logging.getLogger()) ind.parse_stix2(indicator_file) ind.ioc_collections[0]["app_ids"].append("com.sec.android.app.DataCreate") diff --git a/tests/android_androidqf/test_dumpsysreceivers.py b/tests/android_androidqf/test_dumpsysreceivers.py index 45b4aca..916f7a1 100644 --- a/tests/android_androidqf/test_dumpsysreceivers.py +++ b/tests/android_androidqf/test_dumpsysreceivers.py @@ -3,16 +3,21 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ +from pathlib import Path + from mvt.android.modules.androidqf.dumpsys_receivers import DumpsysReceivers from mvt.common.module import run_module -from ..utils import get_android_androidqf +from ..utils import get_android_androidqf, list_files class TestDumpsysReceiversModule: def test_parsing(self): data_path = get_android_androidqf() m = DumpsysReceivers(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 4 assert len(m.detected) == 0 diff --git a/tests/android_androidqf/test_getprop.py b/tests/android_androidqf/test_getprop.py index 34572c6..5e8bc0e 100644 --- a/tests/android_androidqf/test_getprop.py +++ b/tests/android_androidqf/test_getprop.py @@ -4,20 +4,35 @@ # https://license.mvt.re/1.1/ import logging -import os +import zipfile +from pathlib import Path from mvt.android.modules.androidqf.getprop import Getprop from mvt.common.indicators import Indicators from mvt.common.module import run_module -from ..utils import get_artifact_folder +from ..utils import get_android_androidqf, get_artifact, list_files class TestAndroidqfGetpropAnalysis: def test_androidqf_getprop(self): - m = Getprop( - target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging - ) + data_path = get_android_androidqf() + m = Getprop(target_path=data_path, log=logging) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) + run_module(m) + assert len(m.results) == 10 + assert m.results[0]["name"] == "dalvik.vm.appimageformat" + assert m.results[0]["value"] == "lz4" + assert len(m.timeline) == 0 + assert len(m.detected) == 0 + + def test_getprop_parsing_zip(self): + fpath = get_artifact("androidqf.zip") + m = Getprop(target_path=fpath, log=logging) + archive = zipfile.ZipFile(fpath) + m.from_zip_file(archive, archive.namelist()) run_module(m) assert len(m.results) == 10 assert m.results[0]["name"] == "dalvik.vm.appimageformat" @@ -26,9 +41,11 @@ class TestAndroidqfGetpropAnalysis: assert len(m.detected) == 0 def test_androidqf_getprop_detection(self, indicator_file): - m = Getprop( - target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging - ) + data_path = get_android_androidqf() + m = Getprop(target_path=data_path, log=logging) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) ind = Indicators(log=logging.getLogger()) ind.parse_stix2(indicator_file) ind.ioc_collections[0]["android_property_names"].append("dalvik.vm.heapmaxfree") diff --git a/tests/android_androidqf/test_processes.py b/tests/android_androidqf/test_processes.py index f1cb53a..7e6a570 100644 --- a/tests/android_androidqf/test_processes.py +++ b/tests/android_androidqf/test_processes.py @@ -4,19 +4,21 @@ # https://license.mvt.re/1.1/ import logging -import os +from pathlib import Path from mvt.android.modules.androidqf.processes import Processes from mvt.common.module import run_module -from ..utils import get_artifact_folder +from ..utils import get_android_androidqf, list_files class TestAndroidqfProcessesAnalysis: def test_androidqf_processes(self): - m = Processes( - target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging - ) + data_path = get_android_androidqf() + m = Processes(target_path=data_path, log=logging) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 15 assert len(m.timeline) == 0 diff --git a/tests/android_androidqf/test_settings.py b/tests/android_androidqf/test_settings.py index 2aa3797..ec4c75d 100644 --- a/tests/android_androidqf/test_settings.py +++ b/tests/android_androidqf/test_settings.py @@ -3,16 +3,21 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ +from pathlib import Path + from mvt.android.modules.androidqf.settings import Settings from mvt.common.module import run_module -from ..utils import get_android_androidqf +from ..utils import get_android_androidqf, list_files class TestSettingsModule: def test_parsing(self): data_path = get_android_androidqf() m = Settings(target_path=data_path) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 1 assert "random" in m.results.keys() diff --git a/tests/android_androidqf/test_sms.py b/tests/android_androidqf/test_sms.py index df69f0f..884cc29 100644 --- a/tests/android_androidqf/test_sms.py +++ b/tests/android_androidqf/test_sms.py @@ -5,65 +5,84 @@ import logging import os +from pathlib import Path from mvt.android.modules.androidqf.sms import SMS from mvt.common.module import run_module -from ..utils import get_artifact_folder +from ..utils import get_android_androidqf, get_artifact_folder, list_files TEST_BACKUP_PASSWORD = "123456" class TestAndroidqfSMSAnalysis: def test_androidqf_sms(self): - m = SMS( - target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging - ) + data_path = get_android_androidqf() + m = SMS(target_path=data_path, log=logging) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 2 assert len(m.timeline) == 0 assert len(m.detected) == 0 def test_androidqf_sms_encrypted_password_valid(self): + data_path = os.path.join(get_artifact_folder(), "androidqf_encrypted") m = SMS( - target_path=os.path.join(get_artifact_folder(), "androidqf_encrypted"), + target_path=data_path, log=logging, module_options={"backup_password": TEST_BACKUP_PASSWORD}, ) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 1 def test_androidqf_sms_encrypted_password_prompt(self, mocker): + data_path = os.path.join(get_artifact_folder(), "androidqf_encrypted") prompt_mock = mocker.patch( "rich.prompt.Prompt.ask", return_value=TEST_BACKUP_PASSWORD ) m = SMS( - target_path=os.path.join(get_artifact_folder(), "androidqf_encrypted"), + target_path=data_path, log=logging, module_options={}, ) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert prompt_mock.call_count == 1 assert len(m.results) == 1 def test_androidqf_sms_encrypted_password_invalid(self, caplog): + data_path = os.path.join(get_artifact_folder(), "androidqf_encrypted") with caplog.at_level(logging.CRITICAL): m = SMS( - target_path=os.path.join(get_artifact_folder(), "androidqf_encrypted"), + target_path=data_path, log=logging, module_options={"backup_password": "invalid_password"}, ) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 0 assert "Invalid backup password" in caplog.text def test_androidqf_sms_encrypted_no_interactive(self, caplog): + data_path = os.path.join(get_artifact_folder(), "androidqf_encrypted") with caplog.at_level(logging.CRITICAL): m = SMS( - target_path=os.path.join(get_artifact_folder(), "androidqf_encrypted"), + target_path=data_path, log=logging, module_options={"interactive": False}, ) + files = list_files(data_path) + parent_path = Path(data_path).absolute().parent.as_posix() + m.from_folder(parent_path, files) run_module(m) assert len(m.results) == 0 assert ( diff --git a/tests/artifacts/androidqf.zip b/tests/artifacts/androidqf.zip new file mode 100644 index 0000000000000000000000000000000000000000..9712eb15f67605f46c5bc48eba86bf18386e70d5 GIT binary patch literal 492 zcmWIWW@Zs#W&namEI-0PGz{Eevw=YCcUxC|frAO%UsPBN4PQyHe|8sI`}K?I!fvO> zSz`CUOWez5o1lI6=qj!a(?mFAdV>XS9p5^!`f6NV52JaW!iMFa1n+B?Hf`_7S3O_M zD(yW#LfzNi*xz~?!}^NY=gLbm59A3haTmV(K