Merge pull request #282 from mvt-project/cli_refactor

CLI refactor
This commit is contained in:
Nex 2022-06-17 10:27:47 +02:00 committed by GitHub
commit 36a67911b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 681 additions and 607 deletions

View File

@ -3,28 +3,23 @@
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import getpass
import io
import logging
import os
import tarfile
from pathlib import Path
from zipfile import ZipFile
import click
from rich.logging import RichHandler
from mvt.android.parsers.backup import (AndroidBackupParsingError,
InvalidBackupPassword, parse_ab_header,
parse_backup_file)
from mvt.common.cmd_check_iocs import CmdCheckIOCS
from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_IOC,
HELP_MSG_LIST_MODULES, HELP_MSG_MODULE,
HELP_MSG_OUTPUT, HELP_MSG_SERIAL)
from mvt.common.indicators import Indicators, download_indicators_files
from mvt.common.indicators import download_indicators_files
from mvt.common.logo import logo
from mvt.common.module import run_module, save_timeline
from .download_apks import DownloadAPKs
from .cmd_check_adb import CmdAndroidCheckADB
from .cmd_check_backup import CmdAndroidCheckBackup
from .cmd_check_bugreport import CmdAndroidCheckBugreport
from .cmd_download_apks import DownloadAPKs
from .modules.adb import ADB_MODULES
from .modules.adb.packages import Packages
from .modules.backup import BACKUP_MODULES
@ -122,48 +117,20 @@ def download_apks(ctx, all_apks, virustotal, output, from_file, serial):
@click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.pass_context
def check_adb(ctx, iocs, output, fast, list_modules, module, serial):
if list_modules:
log.info("Following is the list of available check-adb modules:")
for adb_module in ADB_MODULES:
log.info(" - %s", adb_module.__name__)
cmd = CmdAndroidCheckADB(results_path=output, ioc_files=iocs,
module_name=module, serial=serial, fast_mode=fast)
if list_modules:
cmd.list_modules()
return
log.info("Checking Android through adb bridge")
log.info("Checking Android device over debug bridge")
if output and not os.path.exists(output):
try:
os.makedirs(output)
except Exception as e:
log.critical("Unable to create output folder %s: %s", output, e)
ctx.exit(1)
cmd.run()
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
timeline = []
timeline_detected = []
for adb_module in ADB_MODULES:
if module and adb_module.__name__ != module:
continue
m = adb_module(output_folder=output, fast_mode=fast,
log=logging.getLogger(adb_module.__module__))
if indicators.total_ioc_count:
m.indicators = indicators
m.indicators.log = m.log
if serial:
m.serial = serial
run_module(m)
timeline.extend(m.timeline)
timeline_detected.extend(m.timeline_detected)
if output:
if len(timeline) > 0:
save_timeline(timeline, os.path.join(output, "timeline.csv"))
if len(timeline_detected) > 0:
save_timeline(timeline_detected, os.path.join(output, "timeline_detected.csv"))
if len(cmd.timeline_detected) > 0:
log.warning("The analysis of the Android device produced %d detections!",
len(cmd.timeline_detected))
#==============================================================================
@ -178,66 +145,20 @@ def check_adb(ctx, iocs, output, fast, list_modules, module, serial):
@click.argument("BUGREPORT_PATH", type=click.Path(exists=True))
@click.pass_context
def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path):
if list_modules:
log.info("Following is the list of available check-bugreport modules:")
for adb_module in BUGREPORT_MODULES:
log.info(" - %s", adb_module.__name__)
cmd = CmdAndroidCheckBugreport(target_path=bugreport_path, results_path=output,
ioc_files=iocs, module_name=module)
if list_modules:
cmd.list_modules()
return
log.info("Checking an Android Bug Report located at: %s", bugreport_path)
log.info("Checking Android bug report at path: %s", bugreport_path)
if output and not os.path.exists(output):
try:
os.makedirs(output)
except Exception as e:
log.critical("Unable to create output folder %s: %s", output, e)
ctx.exit(1)
cmd.run()
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
if os.path.isfile(bugreport_path):
bugreport_format = "zip"
zip_archive = ZipFile(bugreport_path)
zip_files = []
for file_name in zip_archive.namelist():
zip_files.append(file_name)
elif os.path.isdir(bugreport_path):
bugreport_format = "dir"
folder_files = []
parent_path = Path(bugreport_path).absolute().as_posix()
for root, subdirs, subfiles in os.walk(os.path.abspath(bugreport_path)):
for file_name in subfiles:
folder_files.append(os.path.relpath(os.path.join(root, file_name), parent_path))
timeline = []
timeline_detected = []
for bugreport_module in BUGREPORT_MODULES:
if module and bugreport_module.__name__ != module:
continue
m = bugreport_module(base_folder=bugreport_path, output_folder=output,
log=logging.getLogger(bugreport_module.__module__))
if bugreport_format == "zip":
m.from_zip(zip_archive, zip_files)
else:
m.from_folder(bugreport_path, folder_files)
if indicators.total_ioc_count:
m.indicators = indicators
m.indicators.log = m.log
run_module(m)
timeline.extend(m.timeline)
timeline_detected.extend(m.timeline_detected)
if output:
if len(timeline) > 0:
save_timeline(timeline, os.path.join(output, "timeline.csv"))
if len(timeline_detected) > 0:
save_timeline(timeline_detected, os.path.join(output, "timeline_detected.csv"))
if len(cmd.timeline_detected) > 0:
log.warning("The analysis of the Android bug report produced %d detections!",
len(cmd.timeline_detected))
#==============================================================================
@ -248,74 +169,24 @@ def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path):
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
default=[], help=HELP_MSG_IOC)
@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.argument("BACKUP_PATH", type=click.Path(exists=True))
@click.pass_context
def check_backup(ctx, iocs, output, backup_path, serial):
log.info("Checking ADB backup located at: %s", backup_path)
def check_backup(ctx, iocs, output, list_modules, backup_path, serial):
cmd = CmdAndroidCheckBackup(target_path=backup_path, results_path=output,
ioc_files=iocs)
if os.path.isfile(backup_path):
# AB File
backup_type = "ab"
with open(backup_path, "rb") as handle:
data = handle.read()
header = parse_ab_header(data)
if not header["backup"]:
log.critical("Invalid backup format, file should be in .ab format")
ctx.exit(1)
password = None
if header["encryption"] != "none":
password = getpass.getpass(prompt="Backup Password: ", stream=None)
try:
tardata = parse_backup_file(data, password=password)
except InvalidBackupPassword:
log.critical("Invalid backup password")
ctx.exit(1)
except AndroidBackupParsingError:
log.critical("Impossible to parse this backup file, please use Android Backup Extractor instead")
ctx.exit(1)
if list_modules:
cmd.list_modules()
return
dbytes = io.BytesIO(tardata)
tar = tarfile.open(fileobj=dbytes)
files = []
for member in tar:
files.append(member.name)
log.info("Checking Android backup at path: %s", backup_path)
elif os.path.isdir(backup_path):
backup_type = "folder"
backup_path = Path(backup_path).absolute().as_posix()
files = []
for root, subdirs, subfiles in os.walk(os.path.abspath(backup_path)):
for fname in subfiles:
files.append(os.path.relpath(os.path.join(root, fname), backup_path))
else:
log.critical("Invalid backup path, path should be a folder or an Android Backup (.ab) file")
ctx.exit(1)
cmd.run()
if output and not os.path.exists(output):
try:
os.makedirs(output)
except Exception as e:
log.critical("Unable to create output folder %s: %s", output, e)
ctx.exit(1)
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
for module in BACKUP_MODULES:
m = module(base_folder=backup_path, output_folder=output,
log=logging.getLogger(module.__module__))
if indicators.total_ioc_count:
m.indicators = indicators
m.indicators.log = m.log
if serial:
m.serial = serial
if backup_type == "folder":
m.from_folder(backup_path, files)
else:
m.from_ab(backup_path, tar, files)
run_module(m)
if len(cmd.timeline_detected) > 0:
log.warning("The analysis of the Android backup produced %d detections!",
len(cmd.timeline_detected))
#==============================================================================
@ -329,59 +200,14 @@ def check_backup(ctx, iocs, output, backup_path, serial):
@click.argument("FOLDER", type=click.Path(exists=True))
@click.pass_context
def check_iocs(ctx, iocs, list_modules, module, folder):
all_modules = []
for entry in BACKUP_MODULES + ADB_MODULES:
if entry not in all_modules:
all_modules.append(entry)
cmd = CmdCheckIOCS(target_path=folder, ioc_files=iocs, module_name=module)
cmd.modules = BACKUP_MODULES + ADB_MODULES + BUGREPORT_MODULES
if list_modules:
log.info("Following is the list of available check-iocs modules:")
for iocs_module in all_modules:
log.info(" - %s", iocs_module.__name__)
cmd.list_modules()
return
log.info("Checking stored results against provided indicators...")
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
total_detections = 0
for file_name in os.listdir(folder):
name_only, ext = os.path.splitext(file_name)
file_path = os.path.join(folder, file_name)
# TODO: Skipping processing of result files that are not json.
# We might want to revisit this eventually.
if ext != ".json":
continue
for iocs_module in all_modules:
if module and iocs_module.__name__ != module:
continue
if iocs_module().get_slug() != name_only:
continue
log.info("Loading results from \"%s\" with module %s", file_name,
iocs_module.__name__)
m = iocs_module.from_json(file_path,
log=logging.getLogger(iocs_module.__module__))
if indicators.total_ioc_count > 0:
m.indicators = indicators
m.indicators.log = m.log
try:
m.check_indicators()
except NotImplementedError:
continue
else:
total_detections += len(m.detected)
if total_detections > 0:
log.warning("The check of the results produced %d detections!",
total_detections)
cmd.run()
#==============================================================================

View File

@ -0,0 +1,24 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from mvt.common.command import Command
from .modules.adb import ADB_MODULES
log = logging.getLogger(__name__)
class CmdAndroidCheckADB(Command):
name = "check-adb"
modules = ADB_MODULES
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)

View File

@ -0,0 +1,82 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import getpass
import io
import logging
import os
import sys
import tarfile
from pathlib import Path
from mvt.android.parsers.backup import (AndroidBackupParsingError,
InvalidBackupPassword, parse_ab_header,
parse_backup_file)
from mvt.common.command import Command
from .modules.backup import BACKUP_MODULES
log = logging.getLogger(__name__)
class CmdAndroidCheckBackup(Command):
name = "check-backup"
modules = BACKUP_MODULES
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)
self.backup_type = None
self.backup_archive = None
self.backup_files = []
def init(self):
if os.path.isfile(self.target_path):
self.backup_type = "ab"
with open(self.target_path, "rb") as handle:
data = handle.read()
header = parse_ab_header(data)
if not header["backup"]:
log.critical("Invalid backup format, file should be in .ab format")
sys.exit(1)
password = None
if header["encryption"] != "none":
password = getpass.getpass(prompt="Backup Password: ", stream=None)
try:
tardata = parse_backup_file(data, password=password)
except InvalidBackupPassword:
log.critical("Invalid backup password")
sys.exit(1)
except AndroidBackupParsingError as e:
log.critical("Impossible to parse this backup file: %s", e)
log.critical("Please use Android Backup Extractor (ABE) instead")
sys.exit(1)
dbytes = io.BytesIO(tardata)
self.backup_archive = tarfile.open(fileobj=dbytes)
for member in self.backup_archive:
self.backup_files.append(member.name)
elif os.path.isdir(self.target_path):
self.backup_type = "folder"
self.target_path = Path(self.target_path).absolute().as_posix()
for root, subdirs, subfiles in os.walk(os.path.abspath(self.target_path)):
for fname in subfiles:
self.backup_files.append(os.path.relpath(os.path.join(root, fname), self.target_path))
else:
log.critical("Invalid backup path, path should be a folder or an Android Backup (.ab) file")
sys.exit(1)
def module_init(self, module):
if self.backup_type == "folder":
module.from_folder(self.target_path, self.backup_files)
else:
module.from_ab(self.target_path, self.backup_archive, self.backup_files)

View File

@ -0,0 +1,50 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
import os
from pathlib import Path
from zipfile import ZipFile
from mvt.common.command import Command
from .modules.bugreport import BUGREPORT_MODULES
log = logging.getLogger(__name__)
class CmdAndroidCheckBugreport(Command):
name = "check-bugreport"
modules = BUGREPORT_MODULES
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)
self.bugreport_format = None
self.bugreport_archive = None
self.bugreport_files = []
def init(self):
if os.path.isfile(self.target_path):
self.bugreport_format = "zip"
self.bugreport_archive = ZipFile(self.target_path)
for file_name in self.bugreport_archive.namelist():
self.bugreport_files.append(file_name)
elif os.path.isdir(self.target_path):
self.bugreport_format = "dir"
parent_path = Path(self.target_path).absolute().as_posix()
for root, subdirs, subfiles in os.walk(os.path.abspath(self.target_path)):
for file_name in subfiles:
self.bugreport_files.append(os.path.relpath(os.path.join(root, file_name), parent_path))
def module_init(self, module):
if self.bugreport_format == "zip":
module.from_zip(self.bugreport_archive, self.bugreport_files)
else:
module.from_folder(self.target_path, self.bugreport_files)

View File

@ -33,10 +33,10 @@ ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub")
class AndroidExtraction(MVTModule):
"""This class provides a base for all Android extraction modules."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.device = None

View File

@ -20,10 +20,10 @@ CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History"
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,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class DumpsysAccessibility(AndroidExtraction):
"""This module extracts stats on accessibility."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class DumpsysActivities(AndroidExtraction):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = results if results else {}

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
import logging
import re
from mvt.android.parsers.dumpsys import parse_dumpsys_appops
@ -18,10 +17,10 @@ class DumpsysAppOps(AndroidExtraction):
slug = "dumpsys_appops"
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class DumpsysBatteryDaily(AndroidExtraction):
"""This module extracts records from battery daily updates."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class DumpsysBatteryHistory(AndroidExtraction):
"""This module extracts records from battery history events."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
import logging
import re
from mvt.android.parsers import parse_dumpsys_dbinfo
@ -18,10 +17,10 @@ class DumpsysDBInfo(AndroidExtraction):
slug = "dumpsys_dbinfo"
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -14,18 +14,18 @@ log = logging.getLogger(__name__)
class DumpsysFull(AndroidExtraction):
"""This module extracts stats on battery consumption by processes."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):
self._adb_connect()
output = self._adb_command("dumpsys")
if self.output_folder:
output_path = os.path.join(self.output_folder, "dumpsys.txt")
if self.results_path:
output_path = os.path.join(self.results_path, "dumpsys.txt")
with open(output_path, "w", encoding="utf-8") as handle:
handle.write(output)

View File

@ -21,10 +21,10 @@ INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
class DumpsysReceivers(AndroidExtraction):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = results if results else {}

View File

@ -17,10 +17,10 @@ log = logging.getLogger(__name__)
class Files(AndroidExtraction):
"""This module extracts the list of files on the device."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.full_find = False

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
import logging
import re
from datetime import datetime, timedelta
from mvt.android.parsers import parse_getprop
@ -17,10 +16,10 @@ log = logging.getLogger(__name__)
class Getprop(AndroidExtraction):
"""This module extracts device properties from getprop command."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results

View File

@ -14,10 +14,10 @@ log = logging.getLogger(__name__)
class Logcat(AndroidExtraction):
"""This module extracts details on installed packages."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):
@ -28,8 +28,8 @@ class Logcat(AndroidExtraction):
# Get the locat prior to last reboot.
last_output = self._adb_command("logcat -L")
if self.output_folder:
logcat_path = os.path.join(self.output_folder,
if self.results_path:
logcat_path = os.path.join(self.results_path,
"logcat.txt")
with open(logcat_path, "w", encoding="utf-8") as handle:
handle.write(output)
@ -37,7 +37,7 @@ class Logcat(AndroidExtraction):
log.info("Current logcat logs stored at %s",
logcat_path)
logcat_last_path = os.path.join(self.output_folder,
logcat_last_path = os.path.join(self.results_path,
"logcat_last.txt")
with open(logcat_last_path, "w", encoding="utf-8") as handle:
handle.write(last_output)

View File

@ -4,9 +4,7 @@
# https://license.mvt.re/1.1/
import logging
import os
import pkg_resources
from rich.console import Console
from rich.progress import track
from rich.table import Table
@ -74,10 +72,10 @@ ROOT_PACKAGES = [
class Packages(AndroidExtraction):
"""This module extracts the list of installed packages."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):
@ -116,7 +114,7 @@ class Packages(AndroidExtraction):
self.detected.append(result)
continue
for package_file in result["files"]:
for package_file in result.get("files", []):
ioc = self.indicators.check_file_hash(package_file["sha256"])
if ioc:
result["matched_indicator"] = ioc

View File

@ -13,10 +13,10 @@ log = logging.getLogger(__name__)
class Processes(AndroidExtraction):
"""This module extracts details on running processes."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -13,10 +13,10 @@ log = logging.getLogger(__name__)
class RootBinaries(AndroidExtraction):
"""This module extracts the list of installed packages."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):

View File

@ -4,9 +4,6 @@
# https://license.mvt.re/1.1/
import logging
import os
import pkg_resources
from .base import AndroidExtraction
@ -18,10 +15,10 @@ class SELinuxStatus(AndroidExtraction):
slug = "selinux_status"
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results

View File

@ -57,10 +57,10 @@ ANDROID_DANGEROUS_SETTINGS = [
class Settings(AndroidExtraction):
"""This module extracts Android system settings."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results

View File

@ -3,8 +3,6 @@
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import base64
import getpass
import logging
import os
import sqlite3
@ -48,10 +46,10 @@ FROM sms;
class SMS(AndroidExtraction):
"""This module extracts all SMS messages containing links."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -20,10 +20,10 @@ WHATSAPP_PATH = "data/data/com.whatsapp/databases/msgstore.db"
class Whatsapp(AndroidExtraction):
"""This module extracts all WhatsApp messages containing links."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -5,14 +5,13 @@
from mvt.android.modules.backup.base import BackupExtraction
from mvt.android.parsers.backup import parse_sms_file
from mvt.common.utils import check_for_links
class SMS(BackupExtraction):
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = []

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class Accessibility(BugReportModule):
"""This module extracts stats on accessibility."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class Activities(BugReportModule):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = results if results else {}

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class Appops(BugReportModule):
"""This module extracts information on package from App-Ops Manager."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class BatteryDaily(BugReportModule):
"""This module extracts records from battery daily updates."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ log = logging.getLogger(__name__)
class BatteryHistory(BugReportModule):
"""This module extracts records from battery daily updates."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -17,10 +17,10 @@ class DBInfo(BugReportModule):
slug = "dbinfo"
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def check_indicators(self):

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
import logging
import re
from datetime import datetime, timedelta
from mvt.android.parsers import parse_getprop
@ -17,10 +16,10 @@ log = logging.getLogger(__name__)
class Getprop(BugReportModule):
"""This module extracts device properties from getprop command."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results

View File

@ -14,10 +14,10 @@ log = logging.getLogger(__name__)
class Packages(BugReportModule):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -21,10 +21,10 @@ INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
class Receivers(BugReportModule):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = results if results else {}

View File

@ -70,11 +70,11 @@ def decrypt_master_key(password, user_salt, user_iv, pbkdf2_rounds, master_key_b
The backup master key is extracted from the master key blog after decryption.
"""
# Derive key from password using PBKDF2
# Derive key from password using PBKDF2.
kdf = PBKDF2HMAC(algorithm=hashes.SHA1(), length=32, salt=user_salt, iterations=pbkdf2_rounds)
key = kdf.derive(password.encode("utf-8"))
# Decrypt master key blob
# Decrypt master key blob.
cipher = Cipher(algorithms.AES(key), modes.CBC(user_iv))
decryptor = cipher.decryptor()
try:
@ -93,7 +93,7 @@ def decrypt_master_key(password, user_salt, user_iv, pbkdf2_rounds, master_key_b
except TypeError:
raise InvalidBackupPassword()
# Handle quirky encoding of master key bytes in Android original Java crypto code
# Handle quirky encoding of master key bytes in Android original Java crypto code.
if format_version > 1:
hmac_mk = to_utf8_bytes(master_key)
else:
@ -112,6 +112,7 @@ def decrypt_master_key(password, user_salt, user_iv, pbkdf2_rounds, master_key_b
def decrypt_backup_data(encrypted_backup, password, encryption_algo, format_version):
"""
Generate encryption keyffrom password and do decryption
"""
if encryption_algo != b"AES-256":
raise AndroidBackupNotImplemented("Encryption Algorithm not implemented")
@ -126,12 +127,12 @@ def decrypt_backup_data(encrypted_backup, password, encryption_algo, format_vers
user_iv = bytes.fromhex(user_iv.decode("utf-8"))
master_key_blob = bytes.fromhex(master_key_blob.decode("utf-8"))
# Derive decryption master key from password
# Derive decryption master key from password.
master_key, master_iv = decrypt_master_key(password=password, user_salt=user_salt, user_iv=user_iv,
pbkdf2_rounds=pbkdf2_rounds, master_key_blob=master_key_blob,
format_version=format_version, checksum_salt=checksum_salt)
# Decrypt and unpad backup data using derivied key
# Decrypt and unpad backup data using derivied key.
cipher = Cipher(algorithms.AES(master_key), modes.CBC(master_iv))
decryptor = cipher.decryptor()
decrypted_tar = decryptor.update(encrypted_data) + decryptor.finalize()
@ -143,6 +144,7 @@ def decrypt_backup_data(encrypted_backup, password, encryption_algo, format_vers
def parse_backup_file(data, password=None):
"""
Parse an ab file, returns a tar file
"""
if not data.startswith(b"ANDROID BACKUP"):
raise AndroidBackupParsingError("Invalid file header")

View File

@ -0,0 +1,63 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
import os
from mvt.common.command import Command
log = logging.getLogger(__name__)
class CmdCheckIOCS(Command):
name = "check-iocs"
modules = []
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)
def run(self):
all_modules = []
for entry in self.modules:
if entry not in all_modules:
all_modules.append(entry)
log.info("Checking stored results against provided indicators...")
total_detections = 0
for file_name in os.listdir(self.target_path):
name_only, ext = os.path.splitext(file_name)
file_path = os.path.join(self.target_path, file_name)
for iocs_module in all_modules:
if self.module_name and iocs_module.__name__ != self.module_name:
continue
if iocs_module().get_slug() != name_only:
continue
log.info("Loading results from \"%s\" with module %s", file_name,
iocs_module.__name__)
m = iocs_module.from_json(file_path,
log=logging.getLogger(iocs_module.__module__))
if self.iocs.total_ioc_count > 0:
m.indicators = self.iocs
m.indicators.log = m.log
try:
m.check_indicators()
except NotImplementedError:
continue
else:
total_detections += len(m.detected)
if total_detections > 0:
log.warning("The check of the results produced %d detections!",
total_detections)

95
mvt/common/command.py Normal file
View File

@ -0,0 +1,95 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
import os
import sys
from mvt.common.indicators import Indicators
from mvt.common.module import run_module, save_timeline
class Command(object):
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False,
log=logging.getLogger(__name__)):
self.target_path = target_path
self.results_path = results_path
self.ioc_files = ioc_files
self.module_name = module_name
self.serial = serial
self.fast_mode = fast_mode
self.log = log
self.iocs = Indicators(log=log)
self.iocs.load_indicators_files(ioc_files)
self.timeline = []
self.timeline_detected = []
def list_modules(self):
self.log.info("Following is the list of available %s modules:", self.name)
for module in self.modules:
self.log.info(" - %s", module.__name__)
def _create_storage(self):
if self.results_path and not os.path.exists(self.results_path):
try:
os.makedirs(self.results_path)
except Exception as e:
self.log.critical("Unable to create output folder %s: %s",
self.results_path, e)
sys.exit(1)
def _store_timeline(self):
if self.results_path:
if len(self.timeline) > 0:
save_timeline(self.timeline,
os.path.join(self.results_path, "timeline.csv"))
if len(self.timeline_detected) > 0:
save_timeline(self.timeline_detected,
os.path.join(self.results_path, "timeline_detected.csv"))
def init(self):
raise NotImplementedError
def module_init(self, module):
raise NotImplementedError
def run(self):
self._create_storage()
try:
self.init()
except NotImplementedError:
pass
for module in self.modules:
if self.module_name and module.__name__ != self.module_name:
continue
m = module(target_path=self.target_path, results_path=self.results_path, fast_mode=self.fast_mode,
log=logging.getLogger(module.__module__))
if self.iocs.total_ioc_count:
m.indicators = self.iocs
m.indicators.log = m.log
if self.serial:
m.serial = self.serial
try:
self.module_init(m)
except NotImplementedError:
pass
run_module(m)
self.timeline.extend(m.timeline)
self.timeline_detected.extend(m.timeline_detected)
self._store_timeline()

View File

@ -28,16 +28,16 @@ class MVTModule(object):
enabled = True
slug = None
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=None,
fast_mode=False, log=None, results=None):
"""Initialize module.
:param file_path: Path to the module's database file, if there is any
:type file_path: str
:param base_folder: Path to the base folder (backup or filesystem dump)
:param target_path: Path to the target folder (backup or filesystem dump)
:type file_path: str
:param output_folder: Folder where results will be stored
:type output_folder: str
:param results_path: Folder where results will be stored
:type results_path: str
:param fast_mode: Flag to enable or disable slow modules
:type fast_mode: bool
:param log: Handle to logger
@ -45,8 +45,8 @@ class MVTModule(object):
:type results: list
"""
self.file_path = file_path
self.base_folder = base_folder
self.output_folder = output_folder
self.target_path = target_path
self.results_path = results_path
self.fast_mode = fast_mode
self.log = log
self.indicators = None
@ -82,14 +82,14 @@ class MVTModule(object):
def save_to_json(self):
"""Save the collected results to a json file."""
if not self.output_folder:
if not self.results_path:
return
name = self.get_slug()
if self.results:
results_file_name = f"{name}.json"
results_json_path = os.path.join(self.output_folder, results_file_name)
results_json_path = os.path.join(self.results_path, results_file_name)
with open(results_json_path, "w", encoding="utf-8") as handle:
try:
json.dump(self.results, handle, indent=4, default=str)
@ -99,7 +99,7 @@ class MVTModule(object):
if self.detected:
detected_file_name = f"{name}_detected.json"
detected_json_path = os.path.join(self.output_folder, detected_file_name)
detected_json_path = os.path.join(self.results_path, detected_file_name)
with open(detected_json_path, "w", encoding="utf-8") as handle:
json.dump(self.detected, handle, indent=4, default=str)

View File

@ -10,14 +10,16 @@ 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_IOC,
HELP_MSG_LIST_MODULES, HELP_MSG_MODULE,
HELP_MSG_OUTPUT)
from mvt.common.indicators import Indicators, download_indicators_files
from mvt.common.indicators import download_indicators_files
from mvt.common.logo import logo
from mvt.common.module import run_module, save_timeline
from mvt.common.options import MutuallyExclusiveOption
from .cmd_check_backup import CmdIOSCheckBackup
from .cmd_check_fs import CmdIOSCheckFS
from .decrypt import DecryptBackup
from .modules.backup import BACKUP_MODULES
from .modules.fs import FS_MODULES
@ -140,51 +142,20 @@ def extract_key(password, backup_path, key_file):
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
@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 + MIXED_MODULES:
log.info(" - %s", backup_module.__name__)
cmd = CmdIOSCheckBackup(target_path=backup_path, results_path=output,
ioc_files=iocs, module_name=module, fast_mode=fast)
if list_modules:
cmd.list_modules()
return
log.info("Checking iTunes backup located at: %s", backup_path)
if output and not os.path.exists(output):
try:
os.makedirs(output)
except Exception as e:
log.critical("Unable to create output folder %s: %s", output, e)
ctx.exit(1)
cmd.run()
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
timeline = []
timeline_detected = []
for backup_module in BACKUP_MODULES + MIXED_MODULES:
if module and backup_module.__name__ != module:
continue
m = backup_module(base_folder=backup_path, output_folder=output, fast_mode=fast,
log=logging.getLogger(backup_module.__module__))
m.is_backup = True
if indicators.total_ioc_count > 0:
m.indicators = indicators
m.indicators.log = m.log
run_module(m)
timeline.extend(m.timeline)
timeline_detected.extend(m.timeline_detected)
if output:
if len(timeline) > 0:
save_timeline(timeline, os.path.join(output, "timeline.csv"))
if len(timeline_detected) > 0:
save_timeline(timeline_detected, os.path.join(output, "timeline_detected.csv"))
if len(timeline_detected) > 0:
if len(cmd.timeline_detected) > 0:
log.warning("The analysis of the backup produced %d detections!",
len(timeline_detected))
len(cmd.timeline_detected))
#==============================================================================
@ -200,52 +171,20 @@ def check_backup(ctx, iocs, output, fast, backup_path, list_modules, module):
@click.argument("DUMP_PATH", type=click.Path(exists=True))
@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 + MIXED_MODULES:
log.info(" - %s", fs_module.__name__)
cmd = CmdIOSCheckFS(target_path=dump_path, results_path=output,
ioc_files=iocs, module_name=module, fast_mode=fast)
if list_modules:
cmd.list_modules()
return
log.info("Checking filesystem dump located at: %s", dump_path)
log.info("Checking iOS filesystem located at: %s", dump_path)
if output and not os.path.exists(output):
try:
os.makedirs(output)
except Exception as e:
log.critical("Unable to create output folder %s: %s", output, e)
ctx.exit(1)
cmd.run()
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
timeline = []
timeline_detected = []
for fs_module in FS_MODULES + MIXED_MODULES:
if module and fs_module.__name__ != module:
continue
m = fs_module(base_folder=dump_path, output_folder=output, fast_mode=fast,
log=logging.getLogger(fs_module.__module__))
m.is_fs_dump = True
if indicators.total_ioc_count > 0:
m.indicators = indicators
m.indicators.log = m.log
run_module(m)
timeline.extend(m.timeline)
timeline_detected.extend(m.timeline_detected)
if output:
if len(timeline) > 0:
save_timeline(timeline, os.path.join(output, "timeline.csv"))
if len(timeline_detected) > 0:
save_timeline(timeline_detected, os.path.join(output, "timeline_detected.csv"))
if len(timeline_detected) > 0:
log.warning("The analysis of the filesystem produced %d detections!",
len(timeline_detected))
if len(cmd.timeline_detected) > 0:
log.warning("The analysis of the iOS filesystem produced %d detections!",
len(cmd.timeline_detected))
#==============================================================================
@ -259,54 +198,14 @@ def check_fs(ctx, iocs, output, fast, dump_path, list_modules, module):
@click.argument("FOLDER", type=click.Path(exists=True))
@click.pass_context
def check_iocs(ctx, iocs, list_modules, module, folder):
all_modules = []
for entry in BACKUP_MODULES + FS_MODULES + MIXED_MODULES:
if entry not in all_modules:
all_modules.append(entry)
cmd = CmdCheckIOCS(target_path=folder, ioc_files=iocs, module_name=module)
cmd.modules = BACKUP_MODULES + FS_MODULES + MIXED_MODULES
if list_modules:
log.info("Following is the list of available check-iocs modules:")
for iocs_module in all_modules:
log.info(" - %s", iocs_module.__name__)
cmd.list_modules()
return
log.info("Checking stored results against provided indicators...")
indicators = Indicators(log=log)
indicators.load_indicators_files(iocs)
total_detections = 0
for file_name in os.listdir(folder):
name_only, ext = os.path.splitext(file_name)
file_path = os.path.join(folder, file_name)
for iocs_module in all_modules:
if module and iocs_module.__name__ != module:
continue
if iocs_module().get_slug() != name_only:
continue
log.info("Loading results from \"%s\" with module %s", file_name,
iocs_module.__name__)
m = iocs_module.from_json(file_path,
log=logging.getLogger(iocs_module.__module__))
if indicators.total_ioc_count > 0:
m.indicators = indicators
m.indicators.log = m.log
try:
m.check_indicators()
except NotImplementedError:
continue
else:
total_detections += len(m.detected)
if total_detections > 0:
log.warning("The check of the results produced %d detections!",
total_detections)
cmd.run()
#==============================================================================

View File

@ -0,0 +1,25 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from mvt.common.command import Command
from .modules.backup import BACKUP_MODULES
from .modules.mixed import MIXED_MODULES
log = logging.getLogger(__name__)
class CmdIOSCheckBackup(Command):
name = "check-backup"
modules = BACKUP_MODULES + MIXED_MODULES
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)

25
mvt/ios/cmd_check_fs.py Normal file
View File

@ -0,0 +1,25 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from mvt.common.command import Command
from .modules.fs import FS_MODULES
from .modules.mixed import MIXED_MODULES
log = logging.getLogger(__name__)
class CmdIOSCheckFS(Command):
name = "check-fs"
modules = FS_MODULES + MIXED_MODULES
def __init__(self, target_path=None, results_path=None, ioc_files=[],
module_name=None, serial=None, fast_mode=False):
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)

View File

@ -15,16 +15,16 @@ from ..base import IOSExtraction
class BackupInfo(IOSExtraction):
"""This module extracts information about the device and the backup."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {}
def run(self):
info_path = os.path.join(self.base_folder, "Info.plist")
info_path = os.path.join(self.target_path, "Info.plist")
if not os.path.exists(info_path):
raise DatabaseNotFoundError("No Info.plist at backup path, unable to extract device information")

View File

@ -17,10 +17,10 @@ CONF_PROFILES_DOMAIN = "SysSharedContainerDomain-systemgroup.com.apple.configura
class ConfigurationProfiles(IOSExtraction):
"""This module extracts the full plist data from configuration profiles."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -18,10 +18,10 @@ from ..base import IOSExtraction
class Manifest(IOSExtraction):
"""This module extracts information from a backup Manifest.db file."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def _get_key(self, dictionary, key):
@ -93,7 +93,7 @@ class Manifest(IOSExtraction):
self.detected.append(result)
def run(self):
manifest_db_path = os.path.join(self.base_folder, "Manifest.db")
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
if not os.path.isfile(manifest_db_path):
raise DatabaseNotFoundError("unable to find backup's Manifest.db")

View File

@ -19,10 +19,10 @@ class ProfileEvents(IOSExtraction):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -16,10 +16,10 @@ from mvt.common.module import (DatabaseCorruptedError, DatabaseNotFoundError,
class IOSExtraction(MVTModule):
"""This class provides a base for all iOS filesystem/backup extraction modules."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.is_backup = False
@ -73,7 +73,7 @@ class IOSExtraction(MVTModule):
:param domain: Domain to use as filter from Manifest.db. (Default value = None)
"""
manifest_db_path = os.path.join(self.base_folder, "Manifest.db")
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
if not os.path.exists(manifest_db_path):
raise DatabaseNotFoundError("unable to find backup's Manifest.db")
@ -101,7 +101,7 @@ class IOSExtraction(MVTModule):
}
def _get_backup_file_from_id(self, file_id):
file_path = os.path.join(self.base_folder, file_id[0:2], file_id)
file_path = os.path.join(self.target_path, file_id[0:2], file_id)
if os.path.exists(file_path):
return file_path
@ -109,7 +109,7 @@ class IOSExtraction(MVTModule):
def _get_fs_files_from_patterns(self, root_paths):
for root_path in root_paths:
for found_path in glob.glob(os.path.join(self.base_folder, root_path)):
for found_path in glob.glob(os.path.join(self.target_path, root_path)):
if not os.path.exists(found_path):
continue

View File

@ -18,10 +18,10 @@ ANALYTICS_DB_PATH = [
class Analytics(IOSExtraction):
"""This module extracts information from the private/var/Keychains/Analytics/*.db files."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -11,10 +11,10 @@ from ..base import IOSExtraction
class CacheFiles(IOSExtraction):
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):
@ -55,7 +55,7 @@ class CacheFiles(IOSExtraction):
except sqlite3.OperationalError:
return
key_name = os.path.relpath(file_path, self.base_folder)
key_name = os.path.relpath(file_path, self.target_path)
if key_name not in self.results:
self.results[key_name] = []
@ -71,7 +71,7 @@ class CacheFiles(IOSExtraction):
def run(self):
self.results = {}
for root, dirs, files in os.walk(self.base_folder):
for root, dirs, files in os.walk(self.target_path):
for file_name in files:
if file_name != "Cache.db":
continue

View File

@ -18,10 +18,10 @@ class Filesystem(IOSExtraction):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):
@ -58,12 +58,12 @@ class Filesystem(IOSExtraction):
self.detected.append(result)
def run(self):
for root, dirs, files in os.walk(self.base_folder):
for root, dirs, files in os.walk(self.target_path):
for dir_name in dirs:
try:
dir_path = os.path.join(root, dir_name)
result = {
"path": os.path.relpath(dir_path, self.base_folder),
"path": os.path.relpath(dir_path, self.target_path),
"modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(dir_path).st_mtime)),
}
except Exception:
@ -75,7 +75,7 @@ class Filesystem(IOSExtraction):
try:
file_path = os.path.join(root, file_name)
result = {
"path": os.path.relpath(file_path, self.base_folder),
"path": os.path.relpath(file_path, self.target_path),
"modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(file_path).st_mtime)),
}
except Exception:

View File

@ -20,10 +20,10 @@ class Netusage(NetBase):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):

View File

@ -18,10 +18,10 @@ SAFARI_FAVICON_ROOT_PATHS = [
class SafariFavicon(IOSExtraction):
"""This module extracts all Safari favicon records."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ SHUTDOWN_LOG_PATH = [
class ShutdownLog(IOSExtraction):
"""This module extracts processes information from the shutdown log file."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -18,10 +18,10 @@ IOS_ANALYTICS_JOURNAL_PATHS = [
class IOSVersionHistory(IOSExtraction):
"""This module extracts iOS update history from Analytics Journal log files."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -26,7 +26,7 @@ class WebkitBase(IOSExtraction):
def _process_webkit_folder(self, root_paths):
for found_path in self._get_fs_files_from_patterns(root_paths):
key = os.path.relpath(found_path, self.base_folder)
key = os.path.relpath(found_path, self.target_path)
for name in os.listdir(found_path):
if not name.startswith("http"):

View File

@ -19,10 +19,10 @@ class WebkitIndexedDB(WebkitBase):
slug = "webkit_indexeddb"
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -17,10 +17,10 @@ class WebkitLocalStorage(WebkitBase):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -17,10 +17,10 @@ class WebkitSafariViewService(WebkitBase):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):

View File

@ -20,10 +20,10 @@ CALLS_ROOT_PATHS = [
class Calls(IOSExtraction):
"""This module extracts phone calls details"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -23,10 +23,10 @@ CHROME_FAVICON_ROOT_PATHS = [
class ChromeFavicon(IOSExtraction):
"""This module extracts all Chrome favicon records."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -22,10 +22,10 @@ CHROME_HISTORY_ROOT_PATHS = [
class ChromeHistory(IOSExtraction):
"""This module extracts all Chome visits."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -18,10 +18,10 @@ CONTACTS_ROOT_PATHS = [
class Contacts(IOSExtraction):
"""This module extracts all contact details from the phone's address book."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):

View File

@ -21,10 +21,10 @@ FIREFOX_HISTORY_ROOT_PATHS = [
class FirefoxFavicon(IOSExtraction):
"""This module extracts all Firefox favicon"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -25,10 +25,10 @@ class FirefoxHistory(IOSExtraction):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -22,10 +22,10 @@ IDSTATUSCACHE_ROOT_PATHS = [
class IDStatusCache(IOSExtraction):
"""Extracts Apple Authentication information from idstatuscache.plist"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -20,10 +20,10 @@ INTERACTIONC_ROOT_PATHS = [
class InteractionC(IOSExtraction):
"""This module extracts data from InteractionC db."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.timestamps = [

View File

@ -21,10 +21,10 @@ LOCATIOND_ROOT_PATHS = [
class LocationdClients(IOSExtraction):
"""Extract information from apps who used geolocation."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.timestamps = [

View File

@ -20,10 +20,10 @@ class Datausage(NetBase):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def run(self):

View File

@ -20,10 +20,10 @@ OSANALYTICS_ADDAILY_ROOT_PATHS = [
class OSAnalyticsADDaily(IOSExtraction):
"""Extract network usage information by process, from com.apple.osanalytics.addaily.plist"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -23,10 +23,10 @@ SAFARI_BROWSER_STATE_ROOT_PATHS = [
class SafariBrowserState(IOSExtraction):
"""This module extracts all Safari browser state records."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self._session_history_count = 0
@ -115,7 +115,7 @@ class SafariBrowserState(IOSExtraction):
"tab_visible_url": row[2],
"last_viewed_timestamp": convert_timestamp_to_iso(convert_mactime_to_unix(row[3])),
"session_data": session_entries,
"safari_browser_state_db": os.path.relpath(db_path, self.base_folder),
"safari_browser_state_db": os.path.relpath(db_path, self.target_path),
})
def run(self):

View File

@ -25,10 +25,10 @@ class SafariHistory(IOSExtraction):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):
@ -111,7 +111,7 @@ class SafariHistory(IOSExtraction):
"isodate": convert_timestamp_to_iso(convert_mactime_to_unix(row[3])),
"redirect_source": row[4],
"redirect_destination": row[5],
"safari_history_db": os.path.relpath(history_path, self.base_folder),
"safari_history_db": os.path.relpath(history_path, self.target_path),
})
cur.close()

View File

@ -24,10 +24,10 @@ SHORTCUT_ROOT_PATHS = [
class Shortcuts(IOSExtraction):
"""This module extracts all info about SMS/iMessage attachments."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -22,10 +22,10 @@ SMS_ROOT_PATHS = [
class SMS(IOSExtraction):
"""This module extracts all SMS messages containing links."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -21,10 +21,10 @@ SMS_ROOT_PATHS = [
class SMSAttachments(IOSExtraction):
"""This module extracts all info about SMS/iMessage attachments."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -47,10 +47,10 @@ AUTH_REASONS = {
class TCC(IOSExtraction):
"""This module extracts records from the TCC.db SQLite database."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -22,10 +22,10 @@ class WebkitResourceLoadStatistics(IOSExtraction):
"""This module extracts records from WebKit ResourceLoadStatistics observations.db."""
# TODO: Add serialize().
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results
@ -85,4 +85,4 @@ class WebkitResourceLoadStatistics(IOSExtraction):
self.log.info("Unable to search for WebKit observations.db: %s", e)
elif self.is_fs_dump:
for db_path in self._get_fs_files_from_patterns(WEBKIT_RESOURCELOADSTATICS_ROOT_PATHS):
self._process_observations_db(db_path=db_path, key=os.path.relpath(db_path, self.base_folder))
self._process_observations_db(db_path=db_path, key=os.path.relpath(db_path, self.target_path))

View File

@ -29,10 +29,10 @@ class WebkitSessionResourceLog(IOSExtraction):
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results
@ -128,7 +128,7 @@ class WebkitSessionResourceLog(IOSExtraction):
elif self.is_fs_dump:
for log_path in self._get_fs_files_from_patterns(WEBKIT_SESSION_RESOURCE_LOG_ROOT_PATHS):
self.log.info("Found Safari browsing session resource log at path: %s", log_path)
key = os.path.relpath(log_path, self.base_folder)
key = os.path.relpath(log_path, self.target_path)
self.results[key] = self._extract_browsing_stats(log_path)
self.log.info("Extracted records from %d Safari browsing session resource logs",

View File

@ -24,10 +24,10 @@ WHATSAPP_ROOT_PATHS = [
class Whatsapp(IOSExtraction):
"""This module extracts all WhatsApp messages containing links."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):

View File

@ -15,10 +15,10 @@ from .base import IOSExtraction
class NetBase(IOSExtraction):
"""This class provides a base for DataUsage and NetUsage extraction modules."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
def __init__(self, file_path=None, target_path=None, results_path=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,
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
def _extract_net_data(self):
@ -124,7 +124,7 @@ class NetBase(IOSExtraction):
self.log.info("Extended search for suspicious processes ...")
files = []
for posix_path in Path(self.base_folder).rglob("*"):
for posix_path in Path(self.target_path).rglob("*"):
try:
if not posix_path.is_file():
continue

View File

@ -48,7 +48,7 @@ max-line-length = 1000
ignore =
C901,
E265,
E127,
F401,
E127,
W503,
E226

View File

@ -18,7 +18,7 @@ from ..utils import get_android_backup_folder
class TestBackupModule:
def test_module_folder(self):
backup_path = get_android_backup_folder()
mod = SMS(base_folder=backup_path, log=logging)
mod = SMS(target_path=backup_path, log=logging)
files = []
for root, subdirs, subfiles in os.walk(os.path.abspath(backup_path)):
for fname in subfiles:
@ -31,7 +31,7 @@ class TestBackupModule:
def test_module_file(self):
fpath = os.path.join(get_android_backup_folder(), "backup.ab")
mod = SMS(base_folder=fpath, log=logging)
mod = SMS(target_path=fpath, log=logging)
with open(fpath, "rb") as f:
data = f.read()
tardata = parse_backup_file(data)
@ -47,7 +47,7 @@ class TestBackupModule:
def test_module_file2(self):
fpath = os.path.join(get_android_backup_folder(), "backup2.ab")
mod = SMS(base_folder=fpath, log=logging)
mod = SMS(target_path=fpath, log=logging)
with open(fpath, "rb") as f:
data = f.read()
tardata = parse_backup_file(data, password="123456")
@ -63,7 +63,7 @@ class TestBackupModule:
def test_module_file3(self):
fpath = os.path.join(get_android_backup_folder(), "backup3.ab")
mod = SMS(base_folder=fpath, log=logging)
mod = SMS(target_path=fpath, log=logging)
with open(fpath, "rb") as f:
data = f.read()
tardata = parse_backup_file(data)

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
import hashlib
import logging
from mvt.android.parsers.backup import parse_backup_file, parse_tar_for_sms

View File

@ -8,7 +8,6 @@ import os
from pathlib import Path
from mvt.android.modules.bugreport.appops import Appops
from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from ..utils import get_artifact_folder
@ -17,7 +16,7 @@ from ..utils import get_artifact_folder
class TestAppopsModule:
def test_appops_parsing(self):
fpath = os.path.join(get_artifact_folder(), "android_data/bugreport/")
m = Appops(base_folder=fpath, log=logging, results=[])
m = Appops(target_path=fpath, log=logging, results=[])
folder_files = []
parent_path = Path(fpath).absolute().as_posix()
for root, subdirs, subfiles in os.walk(os.path.abspath(fpath)):

View File

@ -3,9 +3,6 @@
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import hashlib
import logging
from mvt.android.parsers.dumpsys import parse_dumpsys_appops
from ..utils import get_artifact

View File

@ -13,7 +13,7 @@ from ..utils import get_ios_backup_folder
class TestBackupInfoModule:
def test_manifest(self):
m = BackupInfo(base_folder=get_ios_backup_folder(), log=logging)
m = BackupInfo(target_path=get_ios_backup_folder(), log=logging)
run_module(m)
assert m.results["Build Version"] == "18C66"
assert m.results["IMEI"] == "42"

View File

@ -14,14 +14,14 @@ from ..utils import get_ios_backup_folder
class TestDatausageModule:
def test_datausage(self):
m = Datausage(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = Datausage(target_path=get_ios_backup_folder(), log=logging, results=[])
run_module(m)
assert len(m.results) == 42
assert len(m.timeline) == 60
assert len(m.detected) == 0
def test_detection(self, indicator_file):
m = Datausage(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = Datausage(target_path=get_ios_backup_folder(), log=logging, results=[])
ind = Indicators(log=logging)
ind.parse_stix2(indicator_file)
# Adds a file that exists in the manifest.

View File

@ -14,14 +14,14 @@ from ..utils import get_ios_backup_folder
class TestManifestModule:
def test_manifest(self):
m = Manifest(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = Manifest(target_path=get_ios_backup_folder(), log=logging, results=[])
run_module(m)
assert len(m.results) == 3721
assert len(m.timeline) == 5881
assert len(m.detected) == 0
def test_detection(self, indicator_file):
m = Manifest(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = Manifest(target_path=get_ios_backup_folder(), log=logging, results=[])
ind = Indicators(log=logging)
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["file_names"].append("com.apple.CoreBrightness.plist")

View File

@ -14,7 +14,7 @@ from ..utils import get_ios_backup_folder
class TestSafariBrowserStateModule:
def test_parsing(self):
m = SafariBrowserState(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = SafariBrowserState(target_path=get_ios_backup_folder(), log=logging, results=[])
m.is_backup = True
run_module(m)
assert m.file_path is not None
@ -23,7 +23,7 @@ class TestSafariBrowserStateModule:
assert len(m.detected) == 0
def test_detection(self, indicator_file):
m = SafariBrowserState(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = SafariBrowserState(target_path=get_ios_backup_folder(), log=logging, results=[])
m.is_backup = True
ind = Indicators(log=logging)
ind.parse_stix2(indicator_file)

View File

@ -14,14 +14,14 @@ from ..utils import get_ios_backup_folder
class TestSMSModule:
def test_sms(self):
m = SMS(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = SMS(target_path=get_ios_backup_folder(), log=logging, results=[])
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 1
assert len(m.detected) == 0
def test_detection(self, indicator_file):
m = SMS(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = SMS(target_path=get_ios_backup_folder(), log=logging, results=[])
ind = Indicators(log=logging)
ind.parse_stix2(indicator_file)
# Adds a file that exists in the manifest.

View File

@ -14,7 +14,7 @@ from ..utils import get_ios_backup_folder
class TestTCCtModule:
def test_tcc(self):
m = TCC(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = TCC(target_path=get_ios_backup_folder(), log=logging, results=[])
run_module(m)
assert len(m.results) == 11
assert len(m.timeline) == 11
@ -24,7 +24,7 @@ class TestTCCtModule:
assert m.results[0]["auth_value"] == "allowed"
def test_tcc_detection(self, indicator_file):
m = TCC(base_folder=get_ios_backup_folder(), log=logging, results=[])
m = TCC(target_path=get_ios_backup_folder(), log=logging, results=[])
ind = Indicators(log=logging)
ind.parse_stix2(indicator_file)
m.indicators = ind