From fd81e3aa1374539dbb4d49333e5eaf63c69f87fd Mon Sep 17 00:00:00 2001 From: tek Date: Tue, 25 Apr 2023 11:13:46 +0200 Subject: [PATCH] Adds verbose mode --- mvt/android/cli.py | 32 ++++++++++++++++++++------------ mvt/android/cmd_download_apks.py | 4 +--- mvt/common/command.py | 18 +++++++++--------- mvt/common/help.py | 1 + mvt/common/utils.py | 28 ++++++++++++++++++++++++++++ mvt/ios/cli.py | 22 ++++++++++++---------- 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/mvt/android/cli.py b/mvt/android/cli.py index 64138cb..ad2a41f 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -6,14 +6,15 @@ import logging import click -from rich.logging import RichHandler from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, - HELP_MSG_OUTPUT, HELP_MSG_SERIAL) + HELP_MSG_OUTPUT, HELP_MSG_SERIAL, + HELP_MSG_VERBOSE) from mvt.common.logo import logo from mvt.common.updates import IndicatorsUpdates +from mvt.common.utils import init_logging, set_verbose_logging from .cmd_check_adb import CmdAndroidCheckADB from .cmd_check_androidqf import CmdAndroidCheckAndroidQF @@ -25,11 +26,8 @@ from .modules.adb.packages import Packages from .modules.backup import BACKUP_MODULES from .modules.bugreport import BUGREPORT_MODULES -# Setup logging using Rich. -LOG_FORMAT = "[%(name)s] %(message)s" -logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[ - RichHandler(show_path=False, log_time_format="%X")]) -log = logging.getLogger(__name__) +init_logging() +log = logging.getLogger("mvt") CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) @@ -63,8 +61,10 @@ def version(): @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)") +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.pass_context -def download_apks(ctx, all_apks, virustotal, output, from_file, serial): +def download_apks(ctx, all_apks, virustotal, output, from_file, serial, verbose): + set_verbose_logging(verbose) try: if from_file: download = DownloadAPKs.from_json(from_file) @@ -112,8 +112,10 @@ def download_apks(ctx, all_apks, virustotal, output, from_file, serial): @click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--module", "-m", help=HELP_MSG_MODULE) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.pass_context -def check_adb(ctx, serial, iocs, output, fast, list_modules, module): +def check_adb(ctx, serial, iocs, output, fast, list_modules, module, verbose): + set_verbose_logging(verbose) cmd = CmdAndroidCheckADB(results_path=output, ioc_files=iocs, module_name=module, serial=serial, fast_mode=fast) @@ -141,9 +143,11 @@ def check_adb(ctx, serial, iocs, output, fast, list_modules, module): help=HELP_MSG_OUTPUT) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--module", "-m", help=HELP_MSG_MODULE) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.argument("BUGREPORT_PATH", type=click.Path(exists=True)) @click.pass_context -def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path): +def check_bugreport(ctx, iocs, output, list_modules, module, verbose, bugreport_path): + set_verbose_logging(verbose) # Always generate hashes as bug reports are small. cmd = CmdAndroidCheckBugreport(target_path=bugreport_path, results_path=output, ioc_files=iocs, @@ -172,9 +176,11 @@ def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path): @click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_OUTPUT) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) @click.pass_context -def check_backup(ctx, iocs, output, list_modules, backup_path): +def check_backup(ctx, iocs, output, list_modules, verbose, backup_path): + set_verbose_logging(verbose) # Always generate hashes as backups are generally small. cmd = CmdAndroidCheckBackup(target_path=backup_path, results_path=output, ioc_files=iocs, hashes=True) @@ -204,9 +210,11 @@ def check_backup(ctx, iocs, output, list_modules, backup_path): @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.argument("ANDROIDQF_PATH", type=click.Path(exists=True)) @click.pass_context -def check_androidqf(ctx, iocs, output, list_modules, module, hashes, androidqf_path): +def check_androidqf(ctx, iocs, output, list_modules, module, hashes, verbose, androidqf_path): + set_verbose_logging(verbose) cmd = CmdAndroidCheckAndroidQF(target_path=androidqf_path, results_path=output, ioc_files=iocs, module_name=module, hashes=hashes) diff --git a/mvt/android/cmd_download_apks.py b/mvt/android/cmd_download_apks.py index d32dcfd..3fcc179 100644 --- a/mvt/android/cmd_download_apks.py +++ b/mvt/android/cmd_download_apks.py @@ -21,15 +21,13 @@ log = logging.getLogger(__name__) class DownloadAPKs(AndroidExtraction): """DownloadAPKs is the main class operating the download of APKs from the device. - - """ def __init__( self, results_path: Optional[str] = None, all_apks: Optional[bool] = False, - packages: Optional[list] = None + packages: Optional[list] = None, ) -> None: """Initialize module. :param results_path: Path to the folder where data should be stored diff --git a/mvt/common/command.py b/mvt/common/command.py index 5f048f6..3791501 100644 --- a/mvt/common/command.py +++ b/mvt/common/command.py @@ -42,20 +42,21 @@ class Command: self.fast_mode = fast_mode self.log = log - self.iocs = Indicators(log=log) - self.iocs.load_indicators_files(self.ioc_files) - # This list will contain all executed modules. # We can use this to reference e.g. self.executed[0].results. self.executed = [] - self.detected_count = 0 - self.hashes = hashes self.hash_values = [] self.timeline = [] self.timeline_detected = [] + # Load IOCs + self._create_storage() + self._setup_logging() + self.iocs = Indicators(log=log) + self.iocs.load_indicators_files(self.ioc_files) + def _create_storage(self) -> None: if self.results_path and not os.path.exists(self.results_path): try: @@ -65,10 +66,11 @@ class Command: self.results_path, exc) sys.exit(1) - def _add_log_file_handler(self, logger: logging.Logger) -> None: + def _setup_logging(self): if not self.results_path: return + logger = logging.getLogger("mvt") file_handler = logging.FileHandler(os.path.join(self.results_path, "command.log")) formatter = logging.Formatter("%(asctime)s - %(name)s - " @@ -150,8 +152,6 @@ class Command: raise NotImplementedError def run(self) -> None: - self._create_storage() - self._add_log_file_handler(self.log) try: self.init() @@ -162,8 +162,8 @@ class Command: if self.module_name and module.__name__ != self.module_name: continue + # FIXME: do we need the logger here module_logger = logging.getLogger(module.__module__) - self._add_log_file_handler(module_logger) m = module(target_path=self.target_path, results_path=self.results_path, diff --git a/mvt/common/help.py b/mvt/common/help.py index f68832a..c997cba 100644 --- a/mvt/common/help.py +++ b/mvt/common/help.py @@ -10,6 +10,7 @@ HELP_MSG_FAST = "Avoid running time/resource consuming features" HELP_MSG_LIST_MODULES = "Print list of available modules and exit" HELP_MSG_MODULE = "Name of a single module you would like to run instead of all" HELP_MSG_HASHES = "Generate hashes of all the files analyzed" +HELP_MSG_VERBOSE = "Verbose mode" # Android-specific. HELP_MSG_SERIAL = "Specify a device serial number or HOST:PORT connection string" diff --git a/mvt/common/utils.py b/mvt/common/utils.py index f9a1a8b..0196555 100644 --- a/mvt/common/utils.py +++ b/mvt/common/utils.py @@ -5,10 +5,13 @@ import datetime import hashlib +import logging import os import re from typing import Any, Iterator, Union +from rich.logging import RichHandler + def convert_chrometime_to_datetime(timestamp: int) -> datetime.datetime: """Converts Chrome timestamp to a datetime. @@ -197,3 +200,28 @@ def generate_hashes_from_path(path: str, log) -> Iterator[dict]: continue yield {"file_path": file_path, "sha256": sha256} + + +def init_logging(verbose: bool = False): + """ + Initialise logging for the MVT module + """ + # Setup logging using Rich. + log = logging.getLogger("mvt") + log.setLevel(logging.DEBUG) + consoleHandler = RichHandler(show_path=False, log_time_format="%X") + consoleHandler.setFormatter(logging.Formatter("[%(name)s] %(message)s")) + if verbose: + consoleHandler.setLevel(logging.DEBUG) + else: + consoleHandler.setLevel(logging.INFO) + log.addHandler(consoleHandler) + + +def set_verbose_logging(verbose: bool = False): + log = logging.getLogger("mvt") + handler = log.handlers[0] + if verbose: + handler.setLevel(logging.DEBUG) + else: + handler.setLevel(logging.INFO) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 2a82f94..f7daf0a 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -8,17 +8,17 @@ import logging import os import click -from rich.logging import RichHandler from rich.prompt import Prompt from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, - HELP_MSG_OUTPUT) + HELP_MSG_OUTPUT, HELP_MSG_VERBOSE) from mvt.common.logo import logo from mvt.common.options import MutuallyExclusiveOption from mvt.common.updates import IndicatorsUpdates -from mvt.common.utils import generate_hashes_from_path +from mvt.common.utils import (generate_hashes_from_path, init_logging, + set_verbose_logging) from .cmd_check_backup import CmdIOSCheckBackup from .cmd_check_fs import CmdIOSCheckFS @@ -27,11 +27,8 @@ from .modules.backup import BACKUP_MODULES from .modules.fs import FS_MODULES from .modules.mixed import MIXED_MODULES -# Setup logging using Rich. -LOG_FORMAT = "[%(name)s] %(message)s" -logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[ - RichHandler(show_path=False, log_time_format="%X")]) -log = logging.getLogger(__name__) +init_logging() +log = logging.getLogger("mvt") # Set this environment variable to a password if needed. MVT_IOS_BACKUP_PASSWORD = "MVT_IOS_BACKUP_PASSWORD" @@ -166,9 +163,12 @@ def extract_key(password, key_file, backup_path): @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) @click.pass_context -def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, backup_path): +def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, verbose, backup_path): + set_verbose_logging(verbose) + cmd = CmdIOSCheckBackup(target_path=backup_path, results_path=output, ioc_files=iocs, module_name=module, fast_mode=fast, hashes=hashes) @@ -199,9 +199,11 @@ def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, backup_p @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) +@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.argument("DUMP_PATH", type=click.Path(exists=True)) @click.pass_context -def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, dump_path): +def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, verbose, dump_path): + set_verbose_logging(verbose) cmd = CmdIOSCheckFS(target_path=dump_path, results_path=output, ioc_files=iocs, module_name=module, fast_mode=fast, hashes=hashes)