mirror of
https://github.com/mvt-project/mvt.git
synced 2024-06-18 10:29:01 +00:00
Added new check-bugreport command and modules
This commit is contained in:
parent
3483ca1584
commit
e2936c3d33
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
|
@ -21,6 +22,7 @@ from .lookups.koodous import koodous_lookup
|
||||||
from .lookups.virustotal import virustotal_lookup
|
from .lookups.virustotal import virustotal_lookup
|
||||||
from .modules.adb import ADB_MODULES
|
from .modules.adb import ADB_MODULES
|
||||||
from .modules.backup import BACKUP_MODULES
|
from .modules.backup import BACKUP_MODULES
|
||||||
|
from .modules.bugreport import BUGREPORT_MODULES
|
||||||
|
|
||||||
# Setup logging using Rich.
|
# Setup logging using Rich.
|
||||||
LOG_FORMAT = "[%(name)s] %(message)s"
|
LOG_FORMAT = "[%(name)s] %(message)s"
|
||||||
|
@ -46,7 +48,7 @@ def version():
|
||||||
|
|
||||||
|
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
# Download APKs
|
# Command: download-apks
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@cli.command("download-apks", help="Download all or non-safelisted installed APKs installed on the device")
|
@cli.command("download-apks", help="Download all or non-safelisted installed APKs installed on the device")
|
||||||
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
||||||
|
@ -99,7 +101,7 @@ def download_apks(ctx, all_apks, virustotal, koodous, all_checks, output, from_f
|
||||||
|
|
||||||
|
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
# Checks through ADB
|
# Command: check-adb
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@cli.command("check-adb", help="Check an Android device over adb")
|
@cli.command("check-adb", help="Check an Android device over adb")
|
||||||
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
||||||
|
@ -157,7 +159,69 @@ def check_adb(ctx, iocs, output, fast, list_modules, module, serial):
|
||||||
|
|
||||||
|
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
# Check ADB backup
|
# Command: check-bugreport
|
||||||
|
#==============================================================================
|
||||||
|
@cli.command("check-bugreport", help="Check an Android Bug Report")
|
||||||
|
@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.option("--module", "-m", help=HELP_MSG_MODULE)
|
||||||
|
@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__)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
log.info("Checking an Android Bug Report located at: %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)
|
||||||
|
|
||||||
|
indicators = Indicators(log=log)
|
||||||
|
indicators.load_indicators_files(iocs)
|
||||||
|
|
||||||
|
zip_archive = ZipFile(bugreport_path)
|
||||||
|
zip_files = []
|
||||||
|
for file_name in zip_archive.namelist():
|
||||||
|
zip_files.append(file_name)
|
||||||
|
|
||||||
|
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__))
|
||||||
|
|
||||||
|
m.from_zip(zip_archive, zip_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"))
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
# Command: check-backup
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@cli.command("check-backup", help="Check an Android Backup")
|
@cli.command("check-backup", help="Check an Android Backup")
|
||||||
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL)
|
||||||
|
|
|
@ -47,7 +47,6 @@ class DumpsysAccessibility(AndroidExtraction):
|
||||||
break
|
break
|
||||||
|
|
||||||
service = line.split(":")[1].strip()
|
service = line.split(":")[1].strip()
|
||||||
log.info("Found installed accessibility service \"%s\"", service)
|
|
||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"package_name": service.split("/")[0],
|
"package_name": service.split("/")[0],
|
||||||
|
@ -62,6 +61,9 @@ class DumpsysAccessibility(AndroidExtraction):
|
||||||
output = self._adb_command("dumpsys accessibility")
|
output = self._adb_command("dumpsys accessibility")
|
||||||
self.results = self.parse_accessibility(output)
|
self.results = self.parse_accessibility(output)
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
log.info("Found installed accessibility service \"%s\"", result.get("service"))
|
||||||
|
|
||||||
self.log.info("Identified a total of %d accessibility services", len(self.results))
|
self.log.info("Identified a total of %d accessibility services", len(self.results))
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -39,23 +39,22 @@ class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_battery_history(output):
|
def parse_battery_daily(output):
|
||||||
results = []
|
results = []
|
||||||
daily = None
|
daily = None
|
||||||
daily_updates = []
|
daily_updates = []
|
||||||
for line in output.split("\n")[1:]:
|
for line in output.split("\n")[1:]:
|
||||||
if line.startswith(" Daily from "):
|
if line.startswith(" Daily from "):
|
||||||
|
if len(daily_updates) > 0:
|
||||||
|
results.extend(daily_updates)
|
||||||
|
daily_updates = []
|
||||||
|
|
||||||
timeframe = line[13:].strip()
|
timeframe = line[13:].strip()
|
||||||
date_from, date_to = timeframe.strip(":").split(" to ", 1)
|
date_from, date_to = timeframe.strip(":").split(" to ", 1)
|
||||||
daily = {"from": date_from[0:10], "to": date_to[0:10]}
|
daily = {"from": date_from[0:10], "to": date_to[0:10]}
|
||||||
|
|
||||||
if not daily:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if line.strip() == "":
|
if not daily:
|
||||||
results.extend(daily_updates)
|
|
||||||
daily = None
|
|
||||||
daily_updates = []
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not line.strip().startswith("Update "):
|
if not line.strip().startswith("Update "):
|
||||||
|
@ -86,7 +85,7 @@ class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("dumpsys batterystats --daily")
|
output = self._adb_command("dumpsys batterystats --daily")
|
||||||
self.results = self.parse_battery_history(output)
|
self.results = self.parse_battery_daily(output)
|
||||||
|
|
||||||
self.log.info("Extracted %d records from battery daily stats", len(self.results))
|
self.log.info("Extracted %d records from battery daily stats", len(self.results))
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ DANGEROUS_PERMISSIONS = [
|
||||||
"com.android.browser.permission.READ_HISTORY_BOOKMARKS",
|
"com.android.browser.permission.READ_HISTORY_BOOKMARKS",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Packages(AndroidExtraction):
|
class Packages(AndroidExtraction):
|
||||||
"""This module extracts the list of installed packages."""
|
"""This module extracts the list of installed packages."""
|
||||||
|
|
||||||
|
|
14
mvt/android/modules/bugreport/__init__.py
Normal file
14
mvt/android/modules/bugreport/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
from .accessibility import Accessibility
|
||||||
|
from .activities import Activities
|
||||||
|
from .battery_daily import BatteryDaily
|
||||||
|
from .battery_history import BatteryHistory
|
||||||
|
from .dbinfo import DBInfo
|
||||||
|
from .receivers import Receivers
|
||||||
|
|
||||||
|
BUGREPORT_MODULES = [Accessibility, Activities, BatteryDaily, BatteryHistory,
|
||||||
|
DBInfo, Receivers]
|
63
mvt/android/modules/bugreport/accessibility.py
Normal file
63
mvt/android/modules/bugreport/accessibility.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_accessibility import DumpsysAccessibility
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
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,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_accessibility = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "DUMP OF SERVICE accessibility:":
|
||||||
|
in_accessibility = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_accessibility:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "------------------------------------------------------------------------------":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysAccessibility.parse_accessibility("\n".join(lines))
|
||||||
|
for result in self.results:
|
||||||
|
log.info("Found installed accessibility service \"%s\"", result.get("service"))
|
||||||
|
|
||||||
|
self.log.info("Identified a total of %d accessibility services", len(self.results))
|
62
mvt/android/modules/bugreport/activities.py
Normal file
62
mvt/android/modules/bugreport/activities.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_activities import DumpsysActivities
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
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,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
self.results = results if results else {}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for intent, activities in self.results.items():
|
||||||
|
for activity in activities:
|
||||||
|
ioc = self.indicators.check_app_id(activity["package_name"])
|
||||||
|
if ioc:
|
||||||
|
activity["matched_indicator"] = ioc
|
||||||
|
self.detected.append({intent: activity})
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_activities = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "DUMP OF SERVICE package:":
|
||||||
|
in_activities = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_activities:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "------------------------------------------------------------------------------":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysActivities.parse_activity_resolver_table("\n".join(lines))
|
46
mvt/android/modules/bugreport/base.py
Normal file
46
mvt/android/modules/bugreport/base.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 Claudio Guarnieri.
|
||||||
|
# See the file 'LICENSE' for usage and copying permissions, or find a copy at
|
||||||
|
# https://github.com/mvt-project/mvt/blob/main/LICENSE
|
||||||
|
|
||||||
|
import fnmatch
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from mvt.common.module import MVTModule
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BugReportModule(MVTModule):
|
||||||
|
"""This class provides a base for all Android Bug Report modules."""
|
||||||
|
|
||||||
|
zip_archive = None
|
||||||
|
|
||||||
|
def from_folder(self, extract_path):
|
||||||
|
self.extract_path = extract_path
|
||||||
|
|
||||||
|
def from_zip(self, zip_archive, zip_files):
|
||||||
|
self.zip_archive = zip_archive
|
||||||
|
self.zip_files = zip_files
|
||||||
|
|
||||||
|
def _get_files_by_pattern(self, pattern):
|
||||||
|
file_names = []
|
||||||
|
if self.zip_archive:
|
||||||
|
for zip_file in self.zip_files:
|
||||||
|
file_names.append(zip_file)
|
||||||
|
else:
|
||||||
|
file_names = self.files
|
||||||
|
|
||||||
|
return fnmatch.filter(file_names, pattern)
|
||||||
|
|
||||||
|
def _get_file_content(self, file_path):
|
||||||
|
if self.zip_archive:
|
||||||
|
handle = self.zip_archive.open(file_path)
|
||||||
|
else:
|
||||||
|
handle = open(os.path.join(self.parent_path, file_path), "rb")
|
||||||
|
|
||||||
|
data = handle.read()
|
||||||
|
handle.close()
|
||||||
|
|
||||||
|
return data
|
76
mvt/android/modules/bugreport/battery_daily.py
Normal file
76
mvt/android/modules/bugreport/battery_daily.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_battery_daily import DumpsysBatteryDaily
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
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,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def serialize(self, record):
|
||||||
|
return {
|
||||||
|
"timestamp": record["from"],
|
||||||
|
"module": self.__class__.__name__,
|
||||||
|
"event": "battery_daily",
|
||||||
|
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_batterystats = False
|
||||||
|
in_daily = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "DUMP OF SERVICE batterystats:":
|
||||||
|
in_batterystats = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_batterystats:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "Daily stats:":
|
||||||
|
lines.append(line)
|
||||||
|
in_daily = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_daily:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysBatteryDaily.parse_battery_daily("\n".join(lines))
|
69
mvt/android/modules/bugreport/battery_history.py
Normal file
69
mvt/android/modules/bugreport/battery_history.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_battery_history import \
|
||||||
|
DumpsysBatteryHistory
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
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,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_batterystats = False
|
||||||
|
in_history = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "********** Print latest newbatterystats **********":
|
||||||
|
in_batterystats = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_batterystats:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip().startswith("Battery History "):
|
||||||
|
lines.append(line)
|
||||||
|
in_history = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_history:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysBatteryHistory.parse_battery_history("\n".join(lines))
|
63
mvt/android/modules/bugreport/dbinfo.py
Normal file
63
mvt/android/modules/bugreport/dbinfo.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_dbinfo import DumpsysDBInfo
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DBInfo(BugReportModule):
|
||||||
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
|
slug = "dbinfo"
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
path = result.get("path", "")
|
||||||
|
for part in path.split("/"):
|
||||||
|
ioc = self.indicators.check_app_id(part)
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_dbinfo = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "DUMP OF SERVICE dbinfo:":
|
||||||
|
in_dbinfo = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_dbinfo:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "------------------------------------------------------------------------------":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysDBInfo.parse_dbinfo("\n".join(lines))
|
84
mvt/android/modules/bugreport/receivers.py
Normal file
84
mvt/android/modules/bugreport/receivers.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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.android.modules.adb.dumpsys_receivers import DumpsysReceivers
|
||||||
|
|
||||||
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
INTENT_NEW_OUTGOING_SMS = "android.provider.Telephony.NEW_OUTGOING_SMS"
|
||||||
|
INTENT_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"
|
||||||
|
INTENT_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED"
|
||||||
|
INTENT_PHONE_STATE = "android.intent.action.PHONE_STATE"
|
||||||
|
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,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
self.results = results if results else {}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for intent, receivers in self.results.items():
|
||||||
|
for receiver in receivers:
|
||||||
|
if intent == INTENT_NEW_OUTGOING_SMS:
|
||||||
|
self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
elif intent == INTENT_SMS_RECEIVED:
|
||||||
|
self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
elif intent == INTENT_DATA_SMS_RECEIVED:
|
||||||
|
self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
elif intent == INTENT_PHONE_STATE:
|
||||||
|
self.log.info("Found a receiver monitoring telephony state/incoming calls: \"%s\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
elif intent == INTENT_NEW_OUTGOING_CALL:
|
||||||
|
self.log.info("Found a receiver monitoring outgoing calls: \"%s\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
|
||||||
|
ioc = self.indicators.check_app_id(receiver["package_name"])
|
||||||
|
if ioc:
|
||||||
|
receiver["matched_indicator"] = ioc
|
||||||
|
self.detected.append({intent: receiver})
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dumpstate_files = self._get_files_by_pattern("dumpstate-*")
|
||||||
|
if not dumpstate_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = self._get_file_content(dumpstate_files[0])
|
||||||
|
if not content:
|
||||||
|
return
|
||||||
|
|
||||||
|
in_receivers = False
|
||||||
|
lines = []
|
||||||
|
for line in content.decode().split("\n"):
|
||||||
|
if line.strip() == "DUMP OF SERVICE package:":
|
||||||
|
in_receivers = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_receivers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "------------------------------------------------------------------------------":
|
||||||
|
break
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
self.results = DumpsysReceivers.parse_receiver_resolver_table("\n".join(lines))
|
|
@ -73,7 +73,7 @@ def check_for_links(text):
|
||||||
:returns: Search results.
|
:returns: Search results.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return re.findall("(?P<url>https?://[^\s]+)", text, re.IGNORECASE)
|
return re.findall(r"(?P<url>https?://[^\s]+)", text, re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
def get_sha256_from_file_path(file_path):
|
def get_sha256_from_file_path(file_path):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user