mirror of https://github.com/mvt-project/mvt.git
Starting to add type hints
This commit is contained in:
parent
b77749e6ba
commit
2bc6fbef2f
|
@ -17,8 +17,9 @@ class CmdAndroidCheckADB(Command):
|
||||||
name = "check-adb"
|
name = "check-adb"
|
||||||
modules = ADB_MODULES
|
modules = ADB_MODULES
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
|
@ -26,8 +26,9 @@ class CmdAndroidCheckBackup(Command):
|
||||||
name = "check-backup"
|
name = "check-backup"
|
||||||
modules = BACKUP_MODULES
|
modules = BACKUP_MODULES
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
|
@ -20,8 +20,9 @@ class CmdAndroidCheckBugreport(Command):
|
||||||
name = "check-bugreport"
|
name = "check-bugreport"
|
||||||
modules = BUGREPORT_MODULES
|
modules = BUGREPORT_MODULES
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
|
@ -176,7 +176,7 @@ class DownloadAPKs(AndroidExtraction):
|
||||||
with open(json_path, "w", encoding="utf-8") as handle:
|
with open(json_path, "w", encoding="utf-8") as handle:
|
||||||
json.dump(self.packages, handle, indent=4)
|
json.dump(self.packages, handle, indent=4)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Run all steps of fetch-apk."""
|
"""Run all steps of fetch-apk."""
|
||||||
self.get_packages()
|
self.get_packages()
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
|
@ -33,8 +33,9 @@ ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub")
|
||||||
class AndroidExtraction(MVTModule):
|
class AndroidExtraction(MVTModule):
|
||||||
"""This class provides a base for all Android extraction modules."""
|
"""This class provides a base for all Android extraction modules."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -273,6 +274,6 @@ class AndroidExtraction(MVTModule):
|
||||||
|
|
||||||
self.log.warn("All attempts to decrypt backup with password failed!")
|
self.log.warn("All attempts to decrypt backup with password failed!")
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Run the main procedure."""
|
"""Run the main procedure."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -20,13 +20,14 @@ CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History"
|
||||||
class ChromeHistory(AndroidExtraction):
|
class ChromeHistory(AndroidExtraction):
|
||||||
"""This module extracts records from Android's Chrome browsing history."""
|
"""This module extracts records from Android's Chrome browsing history."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -34,7 +35,7 @@ class ChromeHistory(AndroidExtraction):
|
||||||
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ class ChromeHistory(AndroidExtraction):
|
||||||
|
|
||||||
log.info("Extracted a total of %d history items", len(self.results))
|
log.info("Extracted a total of %d history items", len(self.results))
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
self._adb_process_file(os.path.join("/", CHROME_HISTORY_PATH),
|
self._adb_process_file(os.path.join("/", CHROME_HISTORY_PATH),
|
||||||
self._parse_db)
|
self._parse_db)
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class DumpsysAccessibility(AndroidExtraction):
|
class DumpsysAccessibility(AndroidExtraction):
|
||||||
"""This module extracts stats on accessibility."""
|
"""This module extracts stats on accessibility."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class DumpsysAccessibility(AndroidExtraction):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys accessibility")
|
output = self._adb_command("dumpsys accessibility")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -15,15 +15,16 @@ log = logging.getLogger(__name__)
|
||||||
class DumpsysActivities(AndroidExtraction):
|
class DumpsysActivities(AndroidExtraction):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = results if results else {}
|
self.results = results if results else {}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class DumpsysActivities(AndroidExtraction):
|
||||||
self.detected.append({intent: activity})
|
self.detected.append({intent: activity})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys package")
|
output = self._adb_command("dumpsys package")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -17,13 +17,14 @@ class DumpsysAppOps(AndroidExtraction):
|
||||||
|
|
||||||
slug = "dumpsys_appops"
|
slug = "dumpsys_appops"
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
for perm in record["permissions"]:
|
for perm in record["permissions"]:
|
||||||
if "entries" not in perm:
|
if "entries" not in perm:
|
||||||
|
@ -40,7 +41,7 @@ class DumpsysAppOps(AndroidExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
if self.indicators:
|
if self.indicators:
|
||||||
ioc = self.indicators.check_app_id(result.get("package_name"))
|
ioc = self.indicators.check_app_id(result.get("package_name"))
|
||||||
|
@ -54,7 +55,7 @@ class DumpsysAppOps(AndroidExtraction):
|
||||||
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission",
|
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission",
|
||||||
result["package_name"])
|
result["package_name"])
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys appops")
|
output = self._adb_command("dumpsys appops")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class DumpsysBatteryDaily(AndroidExtraction):
|
class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
"""This module extracts records from battery daily updates."""
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["from"],
|
"timestamp": record["from"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -29,7 +30,7 @@ class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys batterystats --daily")
|
output = self._adb_command("dumpsys batterystats --daily")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class DumpsysBatteryHistory(AndroidExtraction):
|
class DumpsysBatteryHistory(AndroidExtraction):
|
||||||
"""This module extracts records from battery history events."""
|
"""This module extracts records from battery history events."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class DumpsysBatteryHistory(AndroidExtraction):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys batterystats --history")
|
output = self._adb_command("dumpsys batterystats --history")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -17,13 +17,14 @@ class DumpsysDBInfo(AndroidExtraction):
|
||||||
|
|
||||||
slug = "dumpsys_dbinfo"
|
slug = "dumpsys_dbinfo"
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class DumpsysDBInfo(AndroidExtraction):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys dbinfo")
|
output = self._adb_command("dumpsys dbinfo")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -14,13 +14,14 @@ log = logging.getLogger(__name__)
|
||||||
class DumpsysFull(AndroidExtraction):
|
class DumpsysFull(AndroidExtraction):
|
||||||
"""This module extracts stats on battery consumption by processes."""
|
"""This module extracts stats on battery consumption by processes."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("dumpsys")
|
output = self._adb_command("dumpsys")
|
||||||
|
|
|
@ -21,15 +21,16 @@ INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
|
||||||
class DumpsysReceivers(AndroidExtraction):
|
class DumpsysReceivers(AndroidExtraction):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = results if results else {}
|
self.results = results if results else {}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ class DumpsysReceivers(AndroidExtraction):
|
||||||
self.detected.append({intent: receiver})
|
self.detected.append({intent: receiver})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("dumpsys package")
|
output = self._adb_command("dumpsys package")
|
||||||
|
|
|
@ -17,8 +17,9 @@ log = logging.getLogger(__name__)
|
||||||
class Files(AndroidExtraction):
|
class Files(AndroidExtraction):
|
||||||
"""This module extracts the list of files on the device."""
|
"""This module extracts the list of files on the device."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -46,7 +47,7 @@ class Files(AndroidExtraction):
|
||||||
for file_line in output.splitlines():
|
for file_line in output.splitlines():
|
||||||
self.results.append({"path": file_line.rstrip()})
|
self.results.append({"path": file_line.rstrip()})
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
if "modified_time" in record:
|
if "modified_time" in record:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["modified_time"],
|
"timestamp": record["modified_time"],
|
||||||
|
@ -63,7 +64,7 @@ class Files(AndroidExtraction):
|
||||||
result["path"])
|
result["path"])
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
"""Check file list for known suspicious files or suspicious properties"""
|
"""Check file list for known suspicious files or suspicious properties"""
|
||||||
self.check_suspicious()
|
self.check_suspicious()
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ class Files(AndroidExtraction):
|
||||||
self.log.warning("Found a known suspicous file at path: \"%s\"", result["path"])
|
self.log.warning("Found a known suspicous file at path: \"%s\"", result["path"])
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
||||||
|
|
|
@ -16,15 +16,16 @@ log = logging.getLogger(__name__)
|
||||||
class Getprop(AndroidExtraction):
|
class Getprop(AndroidExtraction):
|
||||||
"""This module extracts device properties from getprop command."""
|
"""This module extracts device properties from getprop command."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {} if not results else results
|
self.results = {} if not results else results
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("getprop")
|
output = self._adb_command("getprop")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -14,13 +14,14 @@ log = logging.getLogger(__name__)
|
||||||
class Logcat(AndroidExtraction):
|
class Logcat(AndroidExtraction):
|
||||||
"""This module extracts details on installed packages."""
|
"""This module extracts details on installed packages."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
# Get the current logcat.
|
# Get the current logcat.
|
||||||
|
|
|
@ -72,13 +72,14 @@ ROOT_PACKAGES = [
|
||||||
class Packages(AndroidExtraction):
|
class Packages(AndroidExtraction):
|
||||||
"""This module extracts the list of installed packages."""
|
"""This module extracts the list of installed packages."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
|
|
||||||
timestamps = [
|
timestamps = [
|
||||||
|
@ -97,7 +98,7 @@ class Packages(AndroidExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
if result["package_name"] in ROOT_PACKAGES:
|
if result["package_name"] in ROOT_PACKAGES:
|
||||||
self.log.warning("Found an installed package related to rooting/jailbreaking: \"%s\"",
|
self.log.warning("Found an installed package related to rooting/jailbreaking: \"%s\"",
|
||||||
|
@ -238,7 +239,7 @@ class Packages(AndroidExtraction):
|
||||||
|
|
||||||
return package_files
|
return package_files
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
packages = self._adb_command("pm list packages -u -i -f")
|
packages = self._adb_command("pm list packages -u -i -f")
|
||||||
|
|
|
@ -13,13 +13,14 @@ log = logging.getLogger(__name__)
|
||||||
class Processes(AndroidExtraction):
|
class Processes(AndroidExtraction):
|
||||||
"""This module extracts details on running processes."""
|
"""This module extracts details on running processes."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ class Processes(AndroidExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("ps -e")
|
output = self._adb_command("ps -e")
|
||||||
|
|
|
@ -13,13 +13,14 @@ log = logging.getLogger(__name__)
|
||||||
class RootBinaries(AndroidExtraction):
|
class RootBinaries(AndroidExtraction):
|
||||||
"""This module extracts the list of installed packages."""
|
"""This module extracts the list of installed packages."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
root_binaries = [
|
root_binaries = [
|
||||||
"su",
|
"su",
|
||||||
"busybox",
|
"busybox",
|
||||||
|
|
|
@ -15,15 +15,16 @@ class SELinuxStatus(AndroidExtraction):
|
||||||
|
|
||||||
slug = "selinux_status"
|
slug = "selinux_status"
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {} if not results else results
|
self.results = {} if not results else results
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("getenforce")
|
output = self._adb_command("getenforce")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
|
@ -57,15 +57,16 @@ ANDROID_DANGEROUS_SETTINGS = [
|
||||||
class Settings(AndroidExtraction):
|
class Settings(AndroidExtraction):
|
||||||
"""This module extracts Android system settings."""
|
"""This module extracts Android system settings."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {} if not results else results
|
self.results = {} if not results else results
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
for namespace, settings in self.results.items():
|
for namespace, settings in self.results.items():
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
for danger in ANDROID_DANGEROUS_SETTINGS:
|
for danger in ANDROID_DANGEROUS_SETTINGS:
|
||||||
|
@ -76,7 +77,7 @@ class Settings(AndroidExtraction):
|
||||||
key, value, danger["description"])
|
key, value, danger["description"])
|
||||||
break
|
break
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
for namespace in ["system", "secure", "global"]:
|
for namespace in ["system", "secure", "global"]:
|
||||||
|
|
|
@ -46,13 +46,14 @@ FROM sms;
|
||||||
class SMS(AndroidExtraction):
|
class SMS(AndroidExtraction):
|
||||||
"""This module extracts all SMS messages containing links."""
|
"""This module extracts all SMS messages containing links."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
body = record["body"].replace("\n", "\\n")
|
body = record["body"].replace("\n", "\\n")
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
|
@ -61,7 +62,7 @@ class SMS(AndroidExtraction):
|
||||||
"data": f"{record['address']}: \"{body}\""
|
"data": f"{record['address']}: \"{body}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -126,7 +127,7 @@ class SMS(AndroidExtraction):
|
||||||
|
|
||||||
log.info("Extracted a total of %d SMS messages containing links", len(self.results))
|
log.info("Extracted a total of %d SMS messages containing links", len(self.results))
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
if (self._adb_check_file_exists(os.path.join("/", SMS_BUGLE_PATH))):
|
if (self._adb_check_file_exists(os.path.join("/", SMS_BUGLE_PATH))):
|
||||||
self.SMS_DB_TYPE = 1
|
self.SMS_DB_TYPE = 1
|
||||||
|
|
|
@ -20,13 +20,14 @@ WHATSAPP_PATH = "data/data/com.whatsapp/databases/msgstore.db"
|
||||||
class Whatsapp(AndroidExtraction):
|
class Whatsapp(AndroidExtraction):
|
||||||
"""This module extracts all WhatsApp messages containing links."""
|
"""This module extracts all WhatsApp messages containing links."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
text = record["data"].replace("\n", "\\n")
|
text = record["data"].replace("\n", "\\n")
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
|
@ -35,7 +36,7 @@ class Whatsapp(AndroidExtraction):
|
||||||
"data": f"\"{text}\""
|
"data": f"\"{text}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ class Whatsapp(AndroidExtraction):
|
||||||
log.info("Extracted a total of %d WhatsApp messages containing links", len(messages))
|
log.info("Extracted a total of %d WhatsApp messages containing links", len(messages))
|
||||||
self.results = messages
|
self.results = messages
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
self._adb_process_file(os.path.join("/", WHATSAPP_PATH), self._parse_db)
|
self._adb_process_file(os.path.join("/", WHATSAPP_PATH), self._parse_db)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -3,19 +3,22 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from mvt.android.modules.backup.base import BackupExtraction
|
from mvt.android.modules.backup.base import BackupExtraction
|
||||||
from mvt.android.parsers.backup import parse_sms_file
|
from mvt.android.parsers.backup import parse_sms_file
|
||||||
|
|
||||||
|
|
||||||
class SMS(BackupExtraction):
|
class SMS(BackupExtraction):
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
self.results = []
|
self.results = []
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -26,7 +29,7 @@ class SMS(BackupExtraction):
|
||||||
if self.indicators.check_domains(message["links"]):
|
if self.indicators.check_domains(message["links"]):
|
||||||
self.detected.append(message)
|
self.detected.append(message)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for file in self._get_files_by_pattern("apps/com.android.providers.telephony/d_f/*_sms_backup"):
|
for file in self._get_files_by_pattern("apps/com.android.providers.telephony/d_f/*_sms_backup"):
|
||||||
self.log.info("Processing SMS backup file at %s", file)
|
self.log.info("Processing SMS backup file at %s", file)
|
||||||
data = self._get_file_content(file)
|
data = self._get_file_content(file)
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class Accessibility(BugReportModule):
|
class Accessibility(BugReportModule):
|
||||||
"""This module extracts stats on accessibility."""
|
"""This module extracts stats on accessibility."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class Accessibility(BugReportModule):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -15,15 +15,16 @@ log = logging.getLogger(__name__)
|
||||||
class Activities(BugReportModule):
|
class Activities(BugReportModule):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = results if results else {}
|
self.results = results if results else {}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class Activities(BugReportModule):
|
||||||
self.detected.append({intent: activity})
|
self.detected.append({intent: activity})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class Appops(BugReportModule):
|
class Appops(BugReportModule):
|
||||||
"""This module extracts information on package from App-Ops Manager."""
|
"""This module extracts information on package from App-Ops Manager."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
for perm in record["permissions"]:
|
for perm in record["permissions"]:
|
||||||
if "entries" not in perm:
|
if "entries" not in perm:
|
||||||
|
@ -38,7 +39,7 @@ class Appops(BugReportModule):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
if self.indicators:
|
if self.indicators:
|
||||||
ioc = self.indicators.check_app_id(result.get("package_name"))
|
ioc = self.indicators.check_app_id(result.get("package_name"))
|
||||||
|
@ -51,7 +52,7 @@ class Appops(BugReportModule):
|
||||||
if perm["name"] == "REQUEST_INSTALL_PACKAGES" and perm["access"] == "allow":
|
if perm["name"] == "REQUEST_INSTALL_PACKAGES" and perm["access"] == "allow":
|
||||||
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission", result["package_name"])
|
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission", result["package_name"])
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class BatteryDaily(BugReportModule):
|
class BatteryDaily(BugReportModule):
|
||||||
"""This module extracts records from battery daily updates."""
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["from"],
|
"timestamp": record["from"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -29,7 +30,7 @@ class BatteryDaily(BugReportModule):
|
||||||
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ class BatteryDaily(BugReportModule):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -15,13 +15,14 @@ log = logging.getLogger(__name__)
|
||||||
class BatteryHistory(BugReportModule):
|
class BatteryHistory(BugReportModule):
|
||||||
"""This module extracts records from battery daily updates."""
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class BatteryHistory(BugReportModule):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -17,13 +17,14 @@ class DBInfo(BugReportModule):
|
||||||
|
|
||||||
slug = "dbinfo"
|
slug = "dbinfo"
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class DBInfo(BugReportModule):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -16,15 +16,16 @@ log = logging.getLogger(__name__)
|
||||||
class Getprop(BugReportModule):
|
class Getprop(BugReportModule):
|
||||||
"""This module extracts device properties from getprop command."""
|
"""This module extracts device properties from getprop command."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {} if not results else results
|
self.results = {} if not results else results
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -14,13 +14,14 @@ log = logging.getLogger(__name__)
|
||||||
class Packages(BugReportModule):
|
class Packages(BugReportModule):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
|
|
||||||
timestamps = [
|
timestamps = [
|
||||||
|
@ -39,7 +40,7 @@ class Packages(BugReportModule):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ class Packages(BugReportModule):
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -21,15 +21,16 @@ INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
|
||||||
class Receivers(BugReportModule):
|
class Receivers(BugReportModule):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = results if results else {}
|
self.results = results if results else {}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ class Receivers(BugReportModule):
|
||||||
self.detected.append({intent: receiver})
|
self.detected.append({intent: receiver})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
content = self._get_dumpstate_file()
|
||||||
if not content:
|
if not content:
|
||||||
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?")
|
||||||
|
|
|
@ -9,7 +9,7 @@ from datetime import datetime
|
||||||
from mvt.common.utils import convert_timestamp_to_iso
|
from mvt.common.utils import convert_timestamp_to_iso
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_accessibility(output):
|
def parse_dumpsys_accessibility(output: str) -> list:
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
in_services = False
|
in_services = False
|
||||||
|
@ -34,7 +34,7 @@ def parse_dumpsys_accessibility(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_activity_resolver_table(output):
|
def parse_dumpsys_activity_resolver_table(output: str) -> dict:
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
in_activity_resolver_table = False
|
in_activity_resolver_table = False
|
||||||
|
@ -90,7 +90,7 @@ def parse_dumpsys_activity_resolver_table(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_battery_daily(output):
|
def parse_dumpsys_battery_daily(output: str) -> list:
|
||||||
results = []
|
results = []
|
||||||
daily = None
|
daily = None
|
||||||
daily_updates = []
|
daily_updates = []
|
||||||
|
@ -136,7 +136,7 @@ def parse_dumpsys_battery_daily(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_battery_history(output):
|
def parse_dumpsys_battery_history(output: str) -> list:
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
|
@ -181,7 +181,7 @@ def parse_dumpsys_battery_history(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_dbinfo(output):
|
def parse_dumpsys_dbinfo(output: str) -> list:
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
rxp = re.compile(r'.*\[([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})\].*\[Pid:\((\d+)\)\](\w+).*sql\=\"(.+?)\"')
|
rxp = re.compile(r'.*\[([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})\].*\[Pid:\((\d+)\)\](\w+).*sql\=\"(.+?)\"')
|
||||||
|
@ -234,7 +234,7 @@ def parse_dumpsys_dbinfo(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_receiver_resolver_table(output):
|
def parse_dumpsys_receiver_resolver_table(output: str) -> dict:
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
in_receiver_resolver_table = False
|
in_receiver_resolver_table = False
|
||||||
|
@ -290,7 +290,7 @@ def parse_dumpsys_receiver_resolver_table(output):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_appops(output):
|
def parse_dumpsys_appops(output: str) -> list:
|
||||||
results = []
|
results = []
|
||||||
perm = {}
|
perm = {}
|
||||||
package = {}
|
package = {}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def parse_getprop(output):
|
def parse_getprop(output: str) -> dict:
|
||||||
results = {}
|
results = {}
|
||||||
rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]")
|
rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]")
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,14 @@ class CmdCheckIOCS(Command):
|
||||||
name = "check-iocs"
|
name = "check-iocs"
|
||||||
modules = []
|
modules = []
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
all_modules = []
|
all_modules = []
|
||||||
for entry in self.modules:
|
for entry in self.modules:
|
||||||
if entry not in all_modules:
|
if entry not in all_modules:
|
||||||
|
|
|
@ -9,6 +9,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from mvt.common.indicators import Indicators
|
from mvt.common.indicators import Indicators
|
||||||
from mvt.common.module import run_module, save_timeline
|
from mvt.common.module import run_module, save_timeline
|
||||||
|
@ -18,9 +19,10 @@ from mvt.common.version import MVT_VERSION
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False,
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
log=logging.getLogger(__name__)):
|
fast_mode: bool = False,
|
||||||
|
log: logging.Logger = logging.getLogger(__name__)):
|
||||||
self.name = ""
|
self.name = ""
|
||||||
|
|
||||||
self.target_path = target_path
|
self.target_path = target_path
|
||||||
|
@ -37,7 +39,7 @@ class Command(object):
|
||||||
self.timeline = []
|
self.timeline = []
|
||||||
self.timeline_detected = []
|
self.timeline_detected = []
|
||||||
|
|
||||||
def _create_storage(self):
|
def _create_storage(self) -> None:
|
||||||
if self.results_path and not os.path.exists(self.results_path):
|
if self.results_path and not os.path.exists(self.results_path):
|
||||||
try:
|
try:
|
||||||
os.makedirs(self.results_path)
|
os.makedirs(self.results_path)
|
||||||
|
@ -46,7 +48,7 @@ class Command(object):
|
||||||
self.results_path, e)
|
self.results_path, e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def _add_log_file_handler(self, logger):
|
def _add_log_file_handler(self, logger: logging.Logger) -> None:
|
||||||
if not self.results_path:
|
if not self.results_path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ class Command(object):
|
||||||
fh.setFormatter(formatter)
|
fh.setFormatter(formatter)
|
||||||
logger.addHandler(fh)
|
logger.addHandler(fh)
|
||||||
|
|
||||||
def _store_timeline(self):
|
def _store_timeline(self) -> None:
|
||||||
if not self.results_path:
|
if not self.results_path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -68,12 +70,16 @@ class Command(object):
|
||||||
save_timeline(self.timeline_detected,
|
save_timeline(self.timeline_detected,
|
||||||
os.path.join(self.results_path, "timeline_detected.csv"))
|
os.path.join(self.results_path, "timeline_detected.csv"))
|
||||||
|
|
||||||
def _store_info(self):
|
def _store_info(self) -> None:
|
||||||
if not self.results_path:
|
if not self.results_path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
target_path = None
|
||||||
|
if self.target_path:
|
||||||
|
target_path = os.path.abspath(self.target_path)
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
"target_path": os.path.abspath(self.target_path),
|
"target_path": target_path,
|
||||||
"mvt_version": MVT_VERSION,
|
"mvt_version": MVT_VERSION,
|
||||||
"date": convert_timestamp_to_iso(datetime.now()),
|
"date": convert_timestamp_to_iso(datetime.now()),
|
||||||
"ioc_files": [],
|
"ioc_files": [],
|
||||||
|
@ -83,44 +89,45 @@ class Command(object):
|
||||||
for coll in self.iocs.ioc_collections:
|
for coll in self.iocs.ioc_collections:
|
||||||
info["ioc_files"].append(coll.get("stix2_file_path", ""))
|
info["ioc_files"].append(coll.get("stix2_file_path", ""))
|
||||||
|
|
||||||
if os.path.isfile(self.target_path):
|
if self.target_path:
|
||||||
h = hashlib.sha256()
|
if os.path.isfile(self.target_path):
|
||||||
with open(self.target_path, "rb") as handle:
|
h = hashlib.sha256()
|
||||||
h.update(handle.read())
|
with open(self.target_path, "rb") as handle:
|
||||||
|
h.update(handle.read())
|
||||||
|
|
||||||
info["hashes"].append({
|
info["hashes"].append({
|
||||||
"file_path": self.target_path,
|
"file_path": self.target_path,
|
||||||
"sha256": h.hexdigest(),
|
"sha256": h.hexdigest(),
|
||||||
})
|
})
|
||||||
elif os.path.isdir(self.target_path):
|
elif os.path.isdir(self.target_path):
|
||||||
for (root, dirs, files) in os.walk(self.target_path):
|
for (root, dirs, files) in os.walk(self.target_path):
|
||||||
for file in files:
|
for file in files:
|
||||||
file_path = os.path.join(root, file)
|
file_path = os.path.join(root, file)
|
||||||
h = hashlib.sha256()
|
h = hashlib.sha256()
|
||||||
|
|
||||||
with open(file_path, "rb") as handle:
|
with open(file_path, "rb") as handle:
|
||||||
h.update(handle.read())
|
h.update(handle.read())
|
||||||
|
|
||||||
info["hashes"].append({
|
info["hashes"].append({
|
||||||
"file_path": file_path,
|
"file_path": file_path,
|
||||||
"sha256": h.hexdigest(),
|
"sha256": h.hexdigest(),
|
||||||
})
|
})
|
||||||
|
|
||||||
with open(os.path.join(self.results_path, "info.json"), "w+") as handle:
|
with open(os.path.join(self.results_path, "info.json"), "w+") as handle:
|
||||||
json.dump(info, handle, indent=4)
|
json.dump(info, handle, indent=4)
|
||||||
|
|
||||||
def list_modules(self):
|
def list_modules(self) -> None:
|
||||||
self.log.info("Following is the list of available %s modules:", self.name)
|
self.log.info("Following is the list of available %s modules:", self.name)
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
self.log.info(" - %s", module.__name__)
|
self.log.info(" - %s", module.__name__)
|
||||||
|
|
||||||
def init(self):
|
def init(self) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def module_init(self, module):
|
def module_init(self, module: Callable) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._create_storage()
|
self._create_storage()
|
||||||
self._add_log_file_handler(self.log)
|
self._add_log_file_handler(self.log)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
@ -17,13 +18,13 @@ class Indicators:
|
||||||
functions to compare extracted artifacts to the indicators.
|
functions to compare extracted artifacts to the indicators.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, log=None):
|
def __init__(self, log=logging.Logger) -> None:
|
||||||
self.data_dir = user_data_dir("mvt")
|
self.data_dir = user_data_dir("mvt")
|
||||||
self.log = log
|
self.log = log
|
||||||
self.ioc_collections = []
|
self.ioc_collections = []
|
||||||
self.total_ioc_count = 0
|
self.total_ioc_count = 0
|
||||||
|
|
||||||
def _load_downloaded_indicators(self):
|
def _load_downloaded_indicators(self) -> None:
|
||||||
if not os.path.isdir(self.data_dir):
|
if not os.path.isdir(self.data_dir):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ class Indicators:
|
||||||
if f.lower().endswith(".stix2"):
|
if f.lower().endswith(".stix2"):
|
||||||
self.parse_stix2(os.path.join(self.data_dir, f))
|
self.parse_stix2(os.path.join(self.data_dir, f))
|
||||||
|
|
||||||
def _check_stix2_env_variable(self):
|
def _check_stix2_env_variable(self) -> None:
|
||||||
"""
|
"""
|
||||||
Checks if a variable MVT_STIX2 contains path to a STIX files.
|
Checks if a variable MVT_STIX2 contains path to a STIX files.
|
||||||
"""
|
"""
|
||||||
|
@ -46,8 +47,8 @@ class Indicators:
|
||||||
self.log.error("Path specified with env MVT_STIX2 is not a valid file: %s",
|
self.log.error("Path specified with env MVT_STIX2 is not a valid file: %s",
|
||||||
path)
|
path)
|
||||||
|
|
||||||
def _new_collection(self, cid="", name="", description="", file_name="",
|
def _new_collection(self, cid: str = "", name: str = "", description: str = "",
|
||||||
file_path=""):
|
file_name: str = "", file_path: str = "") -> dict:
|
||||||
return {
|
return {
|
||||||
"id": cid,
|
"id": cid,
|
||||||
"name": name,
|
"name": name,
|
||||||
|
@ -65,14 +66,14 @@ class Indicators:
|
||||||
"count": 0,
|
"count": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _add_indicator(self, ioc, ioc_coll, ioc_coll_list):
|
def _add_indicator(self, ioc: str, ioc_coll: dict, ioc_coll_list: list) -> None:
|
||||||
ioc = ioc.strip("'")
|
ioc = ioc.strip("'")
|
||||||
if ioc not in ioc_coll_list:
|
if ioc not in ioc_coll_list:
|
||||||
ioc_coll_list.append(ioc)
|
ioc_coll_list.append(ioc)
|
||||||
ioc_coll["count"] += 1
|
ioc_coll["count"] += 1
|
||||||
self.total_ioc_count += 1
|
self.total_ioc_count += 1
|
||||||
|
|
||||||
def parse_stix2(self, file_path):
|
def parse_stix2(self, file_path: str) -> None:
|
||||||
"""Extract indicators from a STIX2 file.
|
"""Extract indicators from a STIX2 file.
|
||||||
|
|
||||||
:param file_path: Path to the STIX2 file to parse
|
:param file_path: Path to the STIX2 file to parse
|
||||||
|
@ -178,7 +179,7 @@ class Indicators:
|
||||||
|
|
||||||
self.ioc_collections.extend(collections)
|
self.ioc_collections.extend(collections)
|
||||||
|
|
||||||
def load_indicators_files(self, files, load_default=True):
|
def load_indicators_files(self, files: list, load_default: bool = True) -> None:
|
||||||
"""
|
"""
|
||||||
Load a list of indicators files.
|
Load a list of indicators files.
|
||||||
"""
|
"""
|
||||||
|
@ -196,7 +197,7 @@ class Indicators:
|
||||||
self._check_stix2_env_variable()
|
self._check_stix2_env_variable()
|
||||||
self.log.info("Loaded a total of %d unique indicators", self.total_ioc_count)
|
self.log.info("Loaded a total of %d unique indicators", self.total_ioc_count)
|
||||||
|
|
||||||
def get_iocs(self, ioc_type):
|
def get_iocs(self, ioc_type: str) -> dict:
|
||||||
for ioc_collection in self.ioc_collections:
|
for ioc_collection in self.ioc_collections:
|
||||||
for ioc in ioc_collection.get(ioc_type, []):
|
for ioc in ioc_collection.get(ioc_type, []):
|
||||||
yield {
|
yield {
|
||||||
|
@ -206,7 +207,7 @@ class Indicators:
|
||||||
"stix2_file_name": ioc_collection["stix2_file_name"],
|
"stix2_file_name": ioc_collection["stix2_file_name"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_domain(self, url):
|
def check_domain(self, url: str) -> dict:
|
||||||
"""Check if a given URL matches any of the provided domain indicators.
|
"""Check if a given URL matches any of the provided domain indicators.
|
||||||
|
|
||||||
:param url: URL to match against domain indicators
|
:param url: URL to match against domain indicators
|
||||||
|
@ -278,7 +279,7 @@ class Indicators:
|
||||||
|
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_domains(self, urls):
|
def check_domains(self, urls: list) -> dict:
|
||||||
"""Check a list of URLs against the provided list of domain indicators.
|
"""Check a list of URLs against the provided list of domain indicators.
|
||||||
|
|
||||||
:param urls: List of URLs to check against domain indicators
|
:param urls: List of URLs to check against domain indicators
|
||||||
|
@ -294,7 +295,7 @@ class Indicators:
|
||||||
if check:
|
if check:
|
||||||
return check
|
return check
|
||||||
|
|
||||||
def check_process(self, process):
|
def check_process(self, process: str) -> dict:
|
||||||
"""Check the provided process name against the list of process
|
"""Check the provided process name against the list of process
|
||||||
indicators.
|
indicators.
|
||||||
|
|
||||||
|
@ -319,7 +320,7 @@ class Indicators:
|
||||||
process, ioc["name"])
|
process, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_processes(self, processes):
|
def check_processes(self, processes: list) -> dict:
|
||||||
"""Check the provided list of processes against the list of
|
"""Check the provided list of processes against the list of
|
||||||
process indicators.
|
process indicators.
|
||||||
|
|
||||||
|
@ -336,7 +337,7 @@ class Indicators:
|
||||||
if check:
|
if check:
|
||||||
return check
|
return check
|
||||||
|
|
||||||
def check_email(self, email):
|
def check_email(self, email: str) -> dict:
|
||||||
"""Check the provided email against the list of email indicators.
|
"""Check the provided email against the list of email indicators.
|
||||||
|
|
||||||
:param email: Email address to check against email indicators
|
:param email: Email address to check against email indicators
|
||||||
|
@ -353,7 +354,7 @@ class Indicators:
|
||||||
email, ioc["name"])
|
email, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_file_name(self, file_name):
|
def check_file_name(self, file_name: str) -> dict:
|
||||||
"""Check the provided file name against the list of file indicators.
|
"""Check the provided file name against the list of file indicators.
|
||||||
|
|
||||||
:param file_name: File name to check against file
|
:param file_name: File name to check against file
|
||||||
|
@ -371,7 +372,7 @@ class Indicators:
|
||||||
file_name, ioc["name"])
|
file_name, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_file_path(self, file_path):
|
def check_file_path(self, file_path: str) -> dict:
|
||||||
"""Check the provided file path against the list of file indicators (both path and name).
|
"""Check the provided file path against the list of file indicators (both path and name).
|
||||||
|
|
||||||
:param file_path: File path or file name to check against file
|
:param file_path: File path or file name to check against file
|
||||||
|
@ -394,7 +395,7 @@ class Indicators:
|
||||||
file_path, ioc["name"])
|
file_path, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_profile(self, profile_uuid):
|
def check_profile(self, profile_uuid: str) -> dict:
|
||||||
"""Check the provided configuration profile UUID against the list of indicators.
|
"""Check the provided configuration profile UUID against the list of indicators.
|
||||||
|
|
||||||
:param profile_uuid: Profile UUID to check against configuration profile indicators
|
:param profile_uuid: Profile UUID to check against configuration profile indicators
|
||||||
|
@ -411,7 +412,7 @@ class Indicators:
|
||||||
profile_uuid, ioc["name"])
|
profile_uuid, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_file_hash(self, file_hash):
|
def check_file_hash(self, file_hash: str) -> dict:
|
||||||
"""Check the provided SHA256 file hash against the list of indicators.
|
"""Check the provided SHA256 file hash against the list of indicators.
|
||||||
|
|
||||||
:param file_hash: SHA256 hash to check
|
:param file_hash: SHA256 hash to check
|
||||||
|
@ -428,7 +429,7 @@ class Indicators:
|
||||||
file_hash, ioc["name"])
|
file_hash, ioc["name"])
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
def check_app_id(self, app_id):
|
def check_app_id(self, app_id: str) -> dict:
|
||||||
"""Check the provided app identifier (typically an Android package name)
|
"""Check the provided app identifier (typically an Android package name)
|
||||||
against the list of indicators.
|
against the list of indicators.
|
||||||
|
|
||||||
|
@ -447,7 +448,7 @@ class Indicators:
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
|
|
||||||
def download_indicators_files(log):
|
def download_indicators_files(log: logging.Logger) -> None:
|
||||||
"""
|
"""
|
||||||
Download indicators from repo into MVT app data directory.
|
Download indicators from repo into MVT app data directory.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .updates import check_for_updates
|
||||||
from .version import MVT_VERSION
|
from .version import MVT_VERSION
|
||||||
|
|
||||||
|
|
||||||
def logo():
|
def logo() -> None:
|
||||||
print("\n")
|
print("\n")
|
||||||
print("\t[bold]MVT[/bold] - Mobile Verification Toolkit")
|
print("\t[bold]MVT[/bold] - Mobile Verification Toolkit")
|
||||||
print("\t\thttps://mvt.re")
|
print("\t\thttps://mvt.re")
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
|
@ -28,8 +30,9 @@ class MVTModule(object):
|
||||||
enabled = True
|
enabled = True
|
||||||
slug = None
|
slug = None
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=None):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = None):
|
||||||
"""Initialize module.
|
"""Initialize module.
|
||||||
|
|
||||||
:param file_path: Path to the module's database file, if there is any
|
:param file_path: Path to the module's database file, if there is any
|
||||||
|
@ -56,7 +59,7 @@ class MVTModule(object):
|
||||||
self.timeline_detected = []
|
self.timeline_detected = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json_path, log=None):
|
def from_json(cls, json_path: str, log: logging.Logger = None):
|
||||||
with open(json_path, "r", encoding="utf-8") as handle:
|
with open(json_path, "r", encoding="utf-8") as handle:
|
||||||
results = json.load(handle)
|
results = json.load(handle)
|
||||||
if log:
|
if log:
|
||||||
|
@ -72,7 +75,7 @@ class MVTModule(object):
|
||||||
sub = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", self.__class__.__name__)
|
sub = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", self.__class__.__name__)
|
||||||
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", sub).lower()
|
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", sub).lower()
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
"""Check the results of this module against a provided list of
|
"""Check the results of this module against a provided list of
|
||||||
indicators.
|
indicators.
|
||||||
|
|
||||||
|
@ -80,7 +83,7 @@ class MVTModule(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def save_to_json(self):
|
def save_to_json(self) -> None:
|
||||||
"""Save the collected results to a json file."""
|
"""Save the collected results to a json file."""
|
||||||
if not self.results_path:
|
if not self.results_path:
|
||||||
return
|
return
|
||||||
|
@ -103,11 +106,11 @@ class MVTModule(object):
|
||||||
with open(detected_json_path, "w", encoding="utf-8") as handle:
|
with open(detected_json_path, "w", encoding="utf-8") as handle:
|
||||||
json.dump(self.detected, handle, indent=4, default=str)
|
json.dump(self.detected, handle, indent=4, default=str)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _deduplicate_timeline(timeline):
|
def _deduplicate_timeline(timeline: list) -> list:
|
||||||
"""Serialize entry as JSON to deduplicate repeated entries
|
"""Serialize entry as JSON to deduplicate repeated entries
|
||||||
|
|
||||||
:param timeline: List of entries from timeline to deduplicate
|
:param timeline: List of entries from timeline to deduplicate
|
||||||
|
@ -118,7 +121,7 @@ class MVTModule(object):
|
||||||
timeline_set.add(json.dumps(record, sort_keys=True))
|
timeline_set.add(json.dumps(record, sort_keys=True))
|
||||||
return [json.loads(record) for record in timeline_set]
|
return [json.loads(record) for record in timeline_set]
|
||||||
|
|
||||||
def to_timeline(self):
|
def to_timeline(self) -> None:
|
||||||
"""Convert results into a timeline."""
|
"""Convert results into a timeline."""
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
record = self.serialize(result)
|
record = self.serialize(result)
|
||||||
|
@ -140,12 +143,12 @@ class MVTModule(object):
|
||||||
self.timeline = self._deduplicate_timeline(self.timeline)
|
self.timeline = self._deduplicate_timeline(self.timeline)
|
||||||
self.timeline_detected = self._deduplicate_timeline(self.timeline_detected)
|
self.timeline_detected = self._deduplicate_timeline(self.timeline_detected)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Run the main module procedure."""
|
"""Run the main module procedure."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def run_module(module):
|
def run_module(module: Callable):
|
||||||
module.log.info("Running module %s...", module.__class__.__name__)
|
module.log.info("Running module %s...", module.__class__.__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -184,7 +187,7 @@ def run_module(module):
|
||||||
module.save_to_json()
|
module.save_to_json()
|
||||||
|
|
||||||
|
|
||||||
def save_timeline(timeline, timeline_path):
|
def save_timeline(timeline: list, timeline_path: str):
|
||||||
"""Save the timeline in a csv file.
|
"""Save the timeline in a csv file.
|
||||||
|
|
||||||
:param timeline: List of records to order and store
|
:param timeline: List of records to order and store
|
||||||
|
|
|
@ -9,7 +9,7 @@ from packaging import version
|
||||||
from .version import MVT_VERSION
|
from .version import MVT_VERSION
|
||||||
|
|
||||||
|
|
||||||
def check_for_updates():
|
def check_for_updates() -> None:
|
||||||
res = requests.get("https://pypi.org/pypi/mvt/json")
|
res = requests.get("https://pypi.org/pypi/mvt/json")
|
||||||
data = res.json()
|
data = res.json()
|
||||||
latest_version = data.get("info", {}).get("version", "")
|
latest_version = data.get("info", {}).get("version", "")
|
||||||
|
|
|
@ -253,7 +253,7 @@ SHORTENER_DOMAINS = [
|
||||||
|
|
||||||
class URL:
|
class URL:
|
||||||
|
|
||||||
def __init__(self, url):
|
def __init__(self, url: str) -> None:
|
||||||
if type(url) == bytes:
|
if type(url) == bytes:
|
||||||
url = url.decode()
|
url = url.decode()
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ class URL:
|
||||||
self.top_level = self.get_top_level()
|
self.top_level = self.get_top_level()
|
||||||
self.is_shortened = False
|
self.is_shortened = False
|
||||||
|
|
||||||
def get_domain(self):
|
def get_domain(self) -> None:
|
||||||
"""Get the domain from a URL.
|
"""Get the domain from a URL.
|
||||||
|
|
||||||
:param url: URL to parse
|
:param url: URL to parse
|
||||||
|
@ -273,11 +273,13 @@ class URL:
|
||||||
"""
|
"""
|
||||||
# TODO: Properly handle exception.
|
# TODO: Properly handle exception.
|
||||||
try:
|
try:
|
||||||
return get_tld(self.url, as_object=True, fix_protocol=True).parsed_url.netloc.lower().lstrip("www.")
|
return get_tld(self.url,
|
||||||
|
as_object=True,
|
||||||
|
fix_protocol=True).parsed_url.netloc.lower().lstrip("www.")
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_top_level(self):
|
def get_top_level(self) -> None:
|
||||||
"""Get only the top-level domain from a URL.
|
"""Get only the top-level domain from a URL.
|
||||||
|
|
||||||
:param url: URL to parse
|
:param url: URL to parse
|
||||||
|
@ -306,7 +308,7 @@ class URL:
|
||||||
|
|
||||||
return self.is_shortened
|
return self.is_shortened
|
||||||
|
|
||||||
def unshorten(self):
|
def unshorten(self) -> None:
|
||||||
"""Unshorten the URL by requesting an HTTP HEAD response."""
|
"""Unshorten the URL by requesting an HTTP HEAD response."""
|
||||||
res = requests.head(self.url)
|
res = requests.head(self.url)
|
||||||
if str(res.status_code).startswith("30"):
|
if str(res.status_code).startswith("30"):
|
||||||
|
|
|
@ -8,7 +8,7 @@ import hashlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def convert_mactime_to_unix(timestamp, from_2001=True):
|
def convert_mactime_to_unix(timestamp, from_2001: bool = True):
|
||||||
"""Converts Mac Standard Time to a Unix timestamp.
|
"""Converts Mac Standard Time to a Unix timestamp.
|
||||||
|
|
||||||
:param timestamp: MacTime timestamp (either int or float).
|
:param timestamp: MacTime timestamp (either int or float).
|
||||||
|
|
|
@ -21,7 +21,7 @@ class VTQuotaExceeded(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def virustotal_lookup(file_hash):
|
def virustotal_lookup(file_hash: str):
|
||||||
if MVT_VT_API_KEY not in os.environ:
|
if MVT_VT_API_KEY not in os.environ:
|
||||||
raise VTNoKey("No VirusTotal API key provided: to use VirusTotal lookups please provide your API key with `export MVT_VT_API_KEY=<key>`")
|
raise VTNoKey("No VirusTotal API key provided: to use VirusTotal lookups please provide your API key with `export MVT_VT_API_KEY=<key>`")
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@ class CmdIOSCheckBackup(Command):
|
||||||
name = "check-backup"
|
name = "check-backup"
|
||||||
modules = BACKUP_MODULES + MIXED_MODULES
|
modules = BACKUP_MODULES + MIXED_MODULES
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
|
@ -18,8 +18,9 @@ class CmdIOSCheckFS(Command):
|
||||||
name = "check-fs"
|
name = "check-fs"
|
||||||
modules = FS_MODULES + MIXED_MODULES
|
modules = FS_MODULES + MIXED_MODULES
|
||||||
|
|
||||||
def __init__(self, target_path=None, results_path=None, ioc_files=[],
|
def __init__(self, target_path: str = None, results_path: str = None,
|
||||||
module_name=None, serial=None, fast_mode=False):
|
ioc_files: list = [], module_name: str = None, serial: str = None,
|
||||||
|
fast_mode: bool = False):
|
||||||
super().__init__(target_path=target_path, results_path=results_path,
|
super().__init__(target_path=target_path, results_path=results_path,
|
||||||
ioc_files=ioc_files, module_name=module_name,
|
ioc_files=ioc_files, module_name=module_name,
|
||||||
serial=serial, fast_mode=fast_mode, log=log)
|
serial=serial, fast_mode=fast_mode, log=log)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class DecryptBackup:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, backup_path, dest_path=None):
|
def __init__(self, backup_path: str, dest_path: str = None) -> None:
|
||||||
"""Decrypts an encrypted iOS backup.
|
"""Decrypts an encrypted iOS backup.
|
||||||
:param backup_path: Path to the encrypted backup folder
|
:param backup_path: Path to the encrypted backup folder
|
||||||
:param dest_path: Path to the folder where to store the decrypted backup
|
:param dest_path: Path to the folder where to store the decrypted backup
|
||||||
|
@ -38,7 +38,7 @@ class DecryptBackup:
|
||||||
return self._backup is not None
|
return self._backup is not None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_encrypted(backup_path) -> bool:
|
def is_encrypted(backup_path: str) -> bool:
|
||||||
"""Query Manifest.db file to see if it's encrypted or not.
|
"""Query Manifest.db file to see if it's encrypted or not.
|
||||||
|
|
||||||
:param backup_path: Path to the backup to decrypt
|
:param backup_path: Path to the backup to decrypt
|
||||||
|
@ -54,13 +54,14 @@ class DecryptBackup:
|
||||||
log.critical("The backup does not seem encrypted!")
|
log.critical("The backup does not seem encrypted!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _process_file(self, relative_path, domain, item, file_id, item_folder):
|
def _process_file(self, relative_path: str, domain: str, item,
|
||||||
|
file_id: str, item_folder: str) -> None:
|
||||||
self._backup.getFileDecryptedCopy(manifestEntry=item,
|
self._backup.getFileDecryptedCopy(manifestEntry=item,
|
||||||
targetName=file_id,
|
targetName=file_id,
|
||||||
targetFolder=item_folder)
|
targetFolder=item_folder)
|
||||||
log.info("Decrypted file %s [%s] to %s/%s", relative_path, domain, item_folder, file_id)
|
log.info("Decrypted file %s [%s] to %s/%s", relative_path, domain, item_folder, file_id)
|
||||||
|
|
||||||
def process_backup(self):
|
def process_backup(self) -> None:
|
||||||
if not os.path.exists(self.dest_path):
|
if not os.path.exists(self.dest_path):
|
||||||
os.makedirs(self.dest_path)
|
os.makedirs(self.dest_path)
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ class DecryptBackup:
|
||||||
shutil.copy(os.path.join(self.backup_path, file_name),
|
shutil.copy(os.path.join(self.backup_path, file_name),
|
||||||
self.dest_path)
|
self.dest_path)
|
||||||
|
|
||||||
def decrypt_with_password(self, password):
|
def decrypt_with_password(self, password: str) -> None:
|
||||||
"""Decrypts an encrypted iOS backup.
|
"""Decrypts an encrypted iOS backup.
|
||||||
|
|
||||||
:param password: Password to use to decrypt the original backup
|
:param password: Password to use to decrypt the original backup
|
||||||
|
@ -149,7 +150,7 @@ class DecryptBackup:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?")
|
log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?")
|
||||||
|
|
||||||
def decrypt_with_key_file(self, key_file):
|
def decrypt_with_key_file(self, key_file: str) -> None:
|
||||||
"""Decrypts an encrypted iOS backup using a key file.
|
"""Decrypts an encrypted iOS backup using a key file.
|
||||||
|
|
||||||
:param key_file: File to read the key bytes to decrypt the backup
|
:param key_file: File to read the key bytes to decrypt the backup
|
||||||
|
@ -179,7 +180,7 @@ class DecryptBackup:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
log.critical("Failed to decrypt backup. Did you provide the correct key file?")
|
log.critical("Failed to decrypt backup. Did you provide the correct key file?")
|
||||||
|
|
||||||
def get_key(self):
|
def get_key(self) -> None:
|
||||||
"""Retrieve and prints the encryption key."""
|
"""Retrieve and prints the encryption key."""
|
||||||
if not self._backup:
|
if not self._backup:
|
||||||
return
|
return
|
||||||
|
@ -188,7 +189,7 @@ class DecryptBackup:
|
||||||
log.info("Derived decryption key for backup at path %s is: \"%s\"",
|
log.info("Derived decryption key for backup at path %s is: \"%s\"",
|
||||||
self.backup_path, self._decryption_key)
|
self.backup_path, self._decryption_key)
|
||||||
|
|
||||||
def write_key(self, key_path):
|
def write_key(self, key_path: str) -> None:
|
||||||
"""Save extracted key to file.
|
"""Save extracted key to file.
|
||||||
|
|
||||||
:param key_path: Path to the file where to write the derived decryption key.
|
:param key_path: Path to the file where to write the derived decryption key.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
|
@ -15,15 +16,16 @@ from ..base import IOSExtraction
|
||||||
class BackupInfo(IOSExtraction):
|
class BackupInfo(IOSExtraction):
|
||||||
"""This module extracts information about the device and the backup."""
|
"""This module extracts information about the device and the backup."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {}
|
self.results = {}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
info_path = os.path.join(self.target_path, "Info.plist")
|
info_path = os.path.join(self.target_path, "Info.plist")
|
||||||
if not os.path.exists(info_path):
|
if not os.path.exists(info_path):
|
||||||
raise DatabaseNotFoundError("No Info.plist at backup path, unable to extract device information")
|
raise DatabaseNotFoundError("No Info.plist at backup path, unable to extract device information")
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
@ -17,13 +18,14 @@ CONF_PROFILES_DOMAIN = "SysSharedContainerDomain-systemgroup.com.apple.configura
|
||||||
class ConfigurationProfiles(IOSExtraction):
|
class ConfigurationProfiles(IOSExtraction):
|
||||||
"""This module extracts the full plist data from configuration profiles."""
|
"""This module extracts the full plist data from configuration profiles."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
if not record["install_date"]:
|
if not record["install_date"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ class ConfigurationProfiles(IOSExtraction):
|
||||||
"data": f"{record['plist']['PayloadType']} installed: {record['plist']['PayloadUUID']} - {payload_name}: {payload_description}"
|
"data": f"{record['plist']['PayloadType']} installed: {record['plist']['PayloadUUID']} - {payload_name}: {payload_description}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -58,7 +60,7 @@ class ConfigurationProfiles(IOSExtraction):
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for conf_file in self._get_backup_files_from_manifest(domain=CONF_PROFILES_DOMAIN):
|
for conf_file in self._get_backup_files_from_manifest(domain=CONF_PROFILES_DOMAIN):
|
||||||
conf_rel_path = conf_file["relative_path"]
|
conf_rel_path = conf_file["relative_path"]
|
||||||
# Filter out all configuration files that are not configuration profiles.
|
# Filter out all configuration files that are not configuration profiles.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -18,8 +19,9 @@ from ..base import IOSExtraction
|
||||||
class Manifest(IOSExtraction):
|
class Manifest(IOSExtraction):
|
||||||
"""This module extracts information from a backup Manifest.db file."""
|
"""This module extracts information from a backup Manifest.db file."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -47,7 +49,7 @@ class Manifest(IOSExtraction):
|
||||||
timestamp = datetime.datetime.utcfromtimestamp(timestamp_or_unix_time_int)
|
timestamp = datetime.datetime.utcfromtimestamp(timestamp_or_unix_time_int)
|
||||||
return convert_timestamp_to_iso(timestamp)
|
return convert_timestamp_to_iso(timestamp)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
if "modified" not in record or "status_changed" not in record:
|
if "modified" not in record or "status_changed" not in record:
|
||||||
return
|
return
|
||||||
|
@ -67,7 +69,7 @@ class Manifest(IOSExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -92,7 +94,7 @@ class Manifest(IOSExtraction):
|
||||||
ioc["value"], rel_path)
|
ioc["value"], rel_path)
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
|
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
|
||||||
if not os.path.isfile(manifest_db_path):
|
if not os.path.isfile(manifest_db_path):
|
||||||
raise DatabaseNotFoundError("unable to find backup's Manifest.db")
|
raise DatabaseNotFoundError("unable to find backup's Manifest.db")
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
from mvt.common.utils import convert_timestamp_to_iso
|
from mvt.common.utils import convert_timestamp_to_iso
|
||||||
|
@ -18,14 +19,14 @@ class ProfileEvents(IOSExtraction):
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
results_path: str = None, fast_mode: bool = False,
|
||||||
fast_mode=False, log=None, results=[]):
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record.get("timestamp"),
|
"timestamp": record.get("timestamp"),
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -33,7 +34,7 @@ class ProfileEvents(IOSExtraction):
|
||||||
"data": f"Process {record.get('process')} started operation {record.get('operation')} of profile {record.get('profile_id')}"
|
"data": f"Process {record.get('process')} started operation {record.get('operation')} of profile {record.get('profile_id')}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for events_file in self._get_backup_files_from_manifest(relative_path=CONF_PROFILES_EVENTS_RELPATH):
|
for events_file in self._get_backup_files_from_manifest(relative_path=CONF_PROFILES_EVENTS_RELPATH):
|
||||||
events_file_path = self._get_backup_file_from_id(events_file["file_id"])
|
events_file_path = self._get_backup_file_from_id(events_file["file_id"])
|
||||||
if not events_file_path:
|
if not events_file_path:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -16,8 +17,9 @@ from mvt.common.module import (DatabaseCorruptedError, DatabaseNotFoundError,
|
||||||
class IOSExtraction(MVTModule):
|
class IOSExtraction(MVTModule):
|
||||||
"""This class provides a base for all iOS filesystem/backup extraction modules."""
|
"""This class provides a base for all iOS filesystem/backup extraction modules."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -18,13 +19,14 @@ ANALYTICS_DB_PATH = [
|
||||||
class Analytics(IOSExtraction):
|
class Analytics(IOSExtraction):
|
||||||
"""This module extracts information from the private/var/Keychains/Analytics/*.db files."""
|
"""This module extracts information from the private/var/Keychains/Analytics/*.db files."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["timestamp"],
|
"timestamp": record["timestamp"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -32,7 +34,7 @@ class Analytics(IOSExtraction):
|
||||||
"data": f"{record}",
|
"data": f"{record}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ class Analytics(IOSExtraction):
|
||||||
|
|
||||||
self.log.info("Extracted information on %d analytics data from %s", len(self.results), artifact)
|
self.log.info("Extracted information on %d analytics data from %s", len(self.results), artifact)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for file_path in self._get_fs_files_from_patterns(ANALYTICS_DB_PATH):
|
for file_path in self._get_fs_files_from_patterns(ANALYTICS_DB_PATH):
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
self.log.info("Found Analytics database file at path: %s", file_path)
|
self.log.info("Found Analytics database file at path: %s", file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -11,13 +12,14 @@ from ..base import IOSExtraction
|
||||||
|
|
||||||
class CacheFiles(IOSExtraction):
|
class CacheFiles(IOSExtraction):
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
for item in self.results[record]:
|
for item in self.results[record]:
|
||||||
records.append({
|
records.append({
|
||||||
|
@ -29,7 +31,7 @@ class CacheFiles(IOSExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ class CacheFiles(IOSExtraction):
|
||||||
"isodate": row[5],
|
"isodate": row[5],
|
||||||
})
|
})
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self.results = {}
|
self.results = {}
|
||||||
for root, dirs, files in os.walk(self.target_path):
|
for root, dirs, files in os.walk(self.target_path):
|
||||||
for file_name in files:
|
for file_name in files:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from mvt.common.utils import convert_timestamp_to_iso
|
from mvt.common.utils import convert_timestamp_to_iso
|
||||||
|
@ -18,13 +19,14 @@ class Filesystem(IOSExtraction):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["modified"],
|
"timestamp": record["modified"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -32,7 +34,7 @@ class Filesystem(IOSExtraction):
|
||||||
"data": record["path"],
|
"data": record["path"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@ class Filesystem(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for root, dirs, files in os.walk(self.target_path):
|
for root, dirs, files in os.walk(self.target_path):
|
||||||
for dir_name in dirs:
|
for dir_name in dirs:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from ..net_base import NetBase
|
from ..net_base import NetBase
|
||||||
|
@ -20,13 +21,14 @@ class Netusage(NetBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for netusage_path in self._get_fs_files_from_patterns(NETUSAGE_ROOT_PATHS):
|
for netusage_path in self._get_fs_files_from_patterns(NETUSAGE_ROOT_PATHS):
|
||||||
self.file_path = netusage_path
|
self.file_path = netusage_path
|
||||||
self.log.info("Found NetUsage database at path: %s", self.file_path)
|
self.log.info("Found NetUsage database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
@ -18,13 +19,14 @@ SAFARI_FAVICON_ROOT_PATHS = [
|
||||||
class SafariFavicon(IOSExtraction):
|
class SafariFavicon(IOSExtraction):
|
||||||
"""This module extracts all Safari favicon records."""
|
"""This module extracts all Safari favicon records."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -32,7 +34,7 @@ class SafariFavicon(IOSExtraction):
|
||||||
"data": f"Safari favicon from {record['url']} with icon URL {record['icon_url']} ({record['type']})",
|
"data": f"Safari favicon from {record['url']} with icon URL {record['icon_url']} ({record['type']})",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -92,7 +94,7 @@ class SafariFavicon(IOSExtraction):
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for file_path in self._get_fs_files_from_patterns(SAFARI_FAVICON_ROOT_PATHS):
|
for file_path in self._get_fs_files_from_patterns(SAFARI_FAVICON_ROOT_PATHS):
|
||||||
self.log.info("Found Safari favicon cache database at path: %s", file_path)
|
self.log.info("Found Safari favicon cache database at path: %s", file_path)
|
||||||
self._process_favicon_db(file_path)
|
self._process_favicon_db(file_path)
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
|
||||||
from ..base import IOSExtraction
|
from ..base import IOSExtraction
|
||||||
|
@ -15,13 +17,14 @@ SHUTDOWN_LOG_PATH = [
|
||||||
class ShutdownLog(IOSExtraction):
|
class ShutdownLog(IOSExtraction):
|
||||||
"""This module extracts processes information from the shutdown log file."""
|
"""This module extracts processes information from the shutdown log file."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -29,7 +32,7 @@ class ShutdownLog(IOSExtraction):
|
||||||
"data": f"Client {record['client']} with PID {record['pid']} was running when the device was shut down",
|
"data": f"Client {record['client']} with PID {record['pid']} was running when the device was shut down",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -83,7 +86,7 @@ class ShutdownLog(IOSExtraction):
|
||||||
|
|
||||||
self.results = sorted(self.results, key=lambda entry: entry["isodate"])
|
self.results = sorted(self.results, key=lambda entry: entry["isodate"])
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(root_paths=SHUTDOWN_LOG_PATH)
|
self._find_ios_database(root_paths=SHUTDOWN_LOG_PATH)
|
||||||
self.log.info("Found shutdown log at path: %s", self.file_path)
|
self.log.info("Found shutdown log at path: %s", self.file_path)
|
||||||
with open(self.file_path, "r", encoding="utf-8") as handle:
|
with open(self.file_path, "r", encoding="utf-8") as handle:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from mvt.common.utils import convert_timestamp_to_iso
|
from mvt.common.utils import convert_timestamp_to_iso
|
||||||
|
|
||||||
|
@ -18,13 +19,14 @@ IOS_ANALYTICS_JOURNAL_PATHS = [
|
||||||
class IOSVersionHistory(IOSExtraction):
|
class IOSVersionHistory(IOSExtraction):
|
||||||
"""This module extracts iOS update history from Analytics Journal log files."""
|
"""This module extracts iOS update history from Analytics Journal log files."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -32,7 +34,7 @@ class IOSVersionHistory(IOSExtraction):
|
||||||
"data": f"Recorded iOS version {record['os_version']}",
|
"data": f"Recorded iOS version {record['os_version']}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
for found_path in self._get_fs_files_from_patterns(IOS_ANALYTICS_JOURNAL_PATHS):
|
for found_path in self._get_fs_files_from_patterns(IOS_ANALYTICS_JOURNAL_PATHS):
|
||||||
with open(found_path, "r", encoding="utf-8") as analytics_log:
|
with open(found_path, "r", encoding="utf-8") as analytics_log:
|
||||||
log_line = json.loads(analytics_log.readline().strip())
|
log_line = json.loads(analytics_log.readline().strip())
|
||||||
|
|
|
@ -14,7 +14,7 @@ from ..base import IOSExtraction
|
||||||
class WebkitBase(IOSExtraction):
|
class WebkitBase(IOSExtraction):
|
||||||
"""This class is a base for other WebKit-related modules."""
|
"""This class is a base for other WebKit-related modules."""
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from .webkit_base import WebkitBase
|
from .webkit_base import WebkitBase
|
||||||
|
|
||||||
WEBKIT_INDEXEDDB_ROOT_PATHS = [
|
WEBKIT_INDEXEDDB_ROOT_PATHS = [
|
||||||
|
@ -19,13 +21,14 @@ class WebkitIndexedDB(WebkitBase):
|
||||||
|
|
||||||
slug = "webkit_indexeddb"
|
slug = "webkit_indexeddb"
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -33,7 +36,7 @@ class WebkitIndexedDB(WebkitBase):
|
||||||
"data": f"IndexedDB folder {record['folder']} containing file for URL {record['url']}",
|
"data": f"IndexedDB folder {record['folder']} containing file for URL {record['url']}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._process_webkit_folder(WEBKIT_INDEXEDDB_ROOT_PATHS)
|
self._process_webkit_folder(WEBKIT_INDEXEDDB_ROOT_PATHS)
|
||||||
self.log.info("Extracted a total of %d WebKit IndexedDB records",
|
self.log.info("Extracted a total of %d WebKit IndexedDB records",
|
||||||
len(self.results))
|
len(self.results))
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from .webkit_base import WebkitBase
|
from .webkit_base import WebkitBase
|
||||||
|
|
||||||
WEBKIT_LOCALSTORAGE_ROOT_PATHS = [
|
WEBKIT_LOCALSTORAGE_ROOT_PATHS = [
|
||||||
|
@ -17,13 +19,14 @@ class WebkitLocalStorage(WebkitBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -31,7 +34,7 @@ class WebkitLocalStorage(WebkitBase):
|
||||||
"data": f"WebKit Local Storage folder {record['folder']} containing file for URL {record['url']}",
|
"data": f"WebKit Local Storage folder {record['folder']} containing file for URL {record['url']}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._process_webkit_folder(WEBKIT_LOCALSTORAGE_ROOT_PATHS)
|
self._process_webkit_folder(WEBKIT_LOCALSTORAGE_ROOT_PATHS)
|
||||||
self.log.info("Extracted a total of %d records from WebKit Local Storages",
|
self.log.info("Extracted a total of %d records from WebKit Local Storages",
|
||||||
len(self.results))
|
len(self.results))
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from .webkit_base import WebkitBase
|
from .webkit_base import WebkitBase
|
||||||
|
|
||||||
WEBKIT_SAFARIVIEWSERVICE_ROOT_PATHS = [
|
WEBKIT_SAFARIVIEWSERVICE_ROOT_PATHS = [
|
||||||
|
@ -17,13 +19,14 @@ class WebkitSafariViewService(WebkitBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._process_webkit_folder(WEBKIT_SAFARIVIEWSERVICE_ROOT_PATHS)
|
self._process_webkit_folder(WEBKIT_SAFARIVIEWSERVICE_ROOT_PATHS)
|
||||||
self.log.info("Extracted a total of %d records from WebKit SafariViewService WebsiteData",
|
self.log.info("Extracted a total of %d records from WebKit SafariViewService WebsiteData",
|
||||||
len(self.results))
|
len(self.results))
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
@ -20,13 +21,14 @@ CALLS_ROOT_PATHS = [
|
||||||
class Calls(IOSExtraction):
|
class Calls(IOSExtraction):
|
||||||
"""This module extracts phone calls details"""
|
"""This module extracts phone calls details"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -34,7 +36,7 @@ class Calls(IOSExtraction):
|
||||||
"data": f"From {record['number']} using {record['provider']} during {record['duration']} seconds"
|
"data": f"From {record['number']} using {record['provider']} during {record['duration']} seconds"
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=CALLS_BACKUP_IDS,
|
self._find_ios_database(backup_ids=CALLS_BACKUP_IDS,
|
||||||
root_paths=CALLS_ROOT_PATHS)
|
root_paths=CALLS_ROOT_PATHS)
|
||||||
self.log.info("Found Calls database at path: %s", self.file_path)
|
self.log.info("Found Calls database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mvt.common.utils import (convert_chrometime_to_unix,
|
from mvt.common.utils import (convert_chrometime_to_unix,
|
||||||
|
@ -23,13 +24,14 @@ CHROME_FAVICON_ROOT_PATHS = [
|
||||||
class ChromeFavicon(IOSExtraction):
|
class ChromeFavicon(IOSExtraction):
|
||||||
"""This module extracts all Chrome favicon records."""
|
"""This module extracts all Chrome favicon records."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -37,7 +39,7 @@ class ChromeFavicon(IOSExtraction):
|
||||||
"data": f"{record['icon_url']} from {record['url']}"
|
"data": f"{record['icon_url']} from {record['url']}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ class ChromeFavicon(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=CHROME_FAVICON_BACKUP_IDS,
|
self._find_ios_database(backup_ids=CHROME_FAVICON_BACKUP_IDS,
|
||||||
root_paths=CHROME_FAVICON_ROOT_PATHS)
|
root_paths=CHROME_FAVICON_ROOT_PATHS)
|
||||||
self.log.info("Found Chrome favicon cache database at path: %s", self.file_path)
|
self.log.info("Found Chrome favicon cache database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mvt.common.utils import (convert_chrometime_to_unix,
|
from mvt.common.utils import (convert_chrometime_to_unix,
|
||||||
|
@ -22,13 +23,14 @@ CHROME_HISTORY_ROOT_PATHS = [
|
||||||
class ChromeHistory(IOSExtraction):
|
class ChromeHistory(IOSExtraction):
|
||||||
"""This module extracts all Chome visits."""
|
"""This module extracts all Chome visits."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -36,7 +38,7 @@ class ChromeHistory(IOSExtraction):
|
||||||
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -46,7 +48,7 @@ class ChromeHistory(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=CHROME_HISTORY_BACKUP_IDS,
|
self._find_ios_database(backup_ids=CHROME_HISTORY_BACKUP_IDS,
|
||||||
root_paths=CHROME_HISTORY_ROOT_PATHS)
|
root_paths=CHROME_HISTORY_ROOT_PATHS)
|
||||||
self.log.info("Found Chrome history database at path: %s", self.file_path)
|
self.log.info("Found Chrome history database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from ..base import IOSExtraction
|
from ..base import IOSExtraction
|
||||||
|
@ -18,13 +19,14 @@ CONTACTS_ROOT_PATHS = [
|
||||||
class Contacts(IOSExtraction):
|
class Contacts(IOSExtraction):
|
||||||
"""This module extracts all contact details from the phone's address book."""
|
"""This module extracts all contact details from the phone's address book."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=CONTACTS_BACKUP_IDS, root_paths=CONTACTS_ROOT_PATHS)
|
self._find_ios_database(backup_ids=CONTACTS_BACKUP_IDS, root_paths=CONTACTS_ROOT_PATHS)
|
||||||
self.log.info("Found Contacts database at path: %s", self.file_path)
|
self.log.info("Found Contacts database at path: %s", self.file_path)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
@ -21,13 +22,14 @@ FIREFOX_HISTORY_ROOT_PATHS = [
|
||||||
class FirefoxFavicon(IOSExtraction):
|
class FirefoxFavicon(IOSExtraction):
|
||||||
"""This module extracts all Firefox favicon"""
|
"""This module extracts all Firefox favicon"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -35,7 +37,7 @@ class FirefoxFavicon(IOSExtraction):
|
||||||
"data": f"Firefox favicon {record['url']} when visiting {record['history_url']}",
|
"data": f"Firefox favicon {record['url']} when visiting {record['history_url']}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ class FirefoxFavicon(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=FIREFOX_HISTORY_BACKUP_IDS,
|
self._find_ios_database(backup_ids=FIREFOX_HISTORY_BACKUP_IDS,
|
||||||
root_paths=FIREFOX_HISTORY_ROOT_PATHS)
|
root_paths=FIREFOX_HISTORY_ROOT_PATHS)
|
||||||
self.log.info("Found Firefox favicon database at path: %s", self.file_path)
|
self.log.info("Found Firefox favicon database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
@ -25,13 +26,14 @@ class FirefoxHistory(IOSExtraction):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -39,7 +41,7 @@ class FirefoxHistory(IOSExtraction):
|
||||||
"data": f"Firefox visit with ID {record['id']} to URL: {record['url']}",
|
"data": f"Firefox visit with ID {record['id']} to URL: {record['url']}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ class FirefoxHistory(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=FIREFOX_HISTORY_BACKUP_IDS, root_paths=FIREFOX_HISTORY_ROOT_PATHS)
|
self._find_ios_database(backup_ids=FIREFOX_HISTORY_BACKUP_IDS, root_paths=FIREFOX_HISTORY_ROOT_PATHS)
|
||||||
self.log.info("Found Firefox history database at path: %s", self.file_path)
|
self.log.info("Found Firefox history database at path: %s", self.file_path)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
@ -22,13 +23,14 @@ IDSTATUSCACHE_ROOT_PATHS = [
|
||||||
class IDStatusCache(IOSExtraction):
|
class IDStatusCache(IOSExtraction):
|
||||||
"""Extracts Apple Authentication information from idstatuscache.plist"""
|
"""Extracts Apple Authentication information from idstatuscache.plist"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -36,7 +38,7 @@ class IDStatusCache(IOSExtraction):
|
||||||
"data": f"Lookup of {record['user']} within {record['package']} (Status {record['idstatus']})"
|
"data": f"Lookup of {record['user']} within {record['package']} (Status {record['idstatus']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -83,7 +85,7 @@ class IDStatusCache(IOSExtraction):
|
||||||
entry["occurrences"] = entry_counter[entry["user"]]
|
entry["occurrences"] = entry_counter[entry["user"]]
|
||||||
self.results.append(entry)
|
self.results.append(entry)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
|
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
self._find_ios_database(backup_ids=IDSTATUSCACHE_BACKUP_IDS)
|
self._find_ios_database(backup_ids=IDSTATUSCACHE_BACKUP_IDS)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
@ -20,8 +21,9 @@ INTERACTIONC_ROOT_PATHS = [
|
||||||
class InteractionC(IOSExtraction):
|
class InteractionC(IOSExtraction):
|
||||||
"""This module extracts data from InteractionC db."""
|
"""This module extracts data from InteractionC db."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -39,7 +41,7 @@ class InteractionC(IOSExtraction):
|
||||||
"last_outgoing_recipient_date",
|
"last_outgoing_recipient_date",
|
||||||
]
|
]
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
processed = []
|
processed = []
|
||||||
for ts in self.timestamps:
|
for ts in self.timestamps:
|
||||||
|
@ -63,7 +65,7 @@ class InteractionC(IOSExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=INTERACTIONC_BACKUP_IDS, root_paths=INTERACTIONC_ROOT_PATHS)
|
self._find_ios_database(backup_ids=INTERACTIONC_BACKUP_IDS, root_paths=INTERACTIONC_ROOT_PATHS)
|
||||||
self.log.info("Found InteractionC database at path: %s", self.file_path)
|
self.log.info("Found InteractionC database at path: %s", self.file_path)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso
|
||||||
|
@ -21,8 +22,9 @@ LOCATIOND_ROOT_PATHS = [
|
||||||
class LocationdClients(IOSExtraction):
|
class LocationdClients(IOSExtraction):
|
||||||
"""Extract information from apps who used geolocation."""
|
"""Extract information from apps who used geolocation."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -39,7 +41,7 @@ class LocationdClients(IOSExtraction):
|
||||||
"BeaconRegionTimeStopped",
|
"BeaconRegionTimeStopped",
|
||||||
]
|
]
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
records = []
|
records = []
|
||||||
for timestamp in self.timestamps:
|
for timestamp in self.timestamps:
|
||||||
if timestamp in record.keys():
|
if timestamp in record.keys():
|
||||||
|
@ -52,7 +54,7 @@ class LocationdClients(IOSExtraction):
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -108,7 +110,7 @@ class LocationdClients(IOSExtraction):
|
||||||
|
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
|
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
self._find_ios_database(backup_ids=LOCATIOND_BACKUP_IDS)
|
self._find_ios_database(backup_ids=LOCATIOND_BACKUP_IDS)
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from ..net_base import NetBase
|
from ..net_base import NetBase
|
||||||
|
|
||||||
DATAUSAGE_BACKUP_IDS = [
|
DATAUSAGE_BACKUP_IDS = [
|
||||||
|
@ -20,13 +22,14 @@ class Datausage(NetBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=DATAUSAGE_BACKUP_IDS,
|
self._find_ios_database(backup_ids=DATAUSAGE_BACKUP_IDS,
|
||||||
root_paths=DATAUSAGE_ROOT_PATHS)
|
root_paths=DATAUSAGE_ROOT_PATHS)
|
||||||
self.log.info("Found DataUsage database at path: %s", self.file_path)
|
self.log.info("Found DataUsage database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
from mvt.common.utils import convert_timestamp_to_iso
|
from mvt.common.utils import convert_timestamp_to_iso
|
||||||
|
@ -20,13 +21,14 @@ OSANALYTICS_ADDAILY_ROOT_PATHS = [
|
||||||
class OSAnalyticsADDaily(IOSExtraction):
|
class OSAnalyticsADDaily(IOSExtraction):
|
||||||
"""Extract network usage information by process, from com.apple.osanalytics.addaily.plist"""
|
"""Extract network usage information by process, from com.apple.osanalytics.addaily.plist"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
record_data = f"{record['package']} WIFI IN: {record['wifi_in']}, WIFI OUT: {record['wifi_out']} - " \
|
record_data = f"{record['package']} WIFI IN: {record['wifi_in']}, WIFI OUT: {record['wifi_out']} - " \
|
||||||
f"WWAN IN: {record['wwan_in']}, WWAN OUT: {record['wwan_out']}"
|
f"WWAN IN: {record['wwan_in']}, WWAN OUT: {record['wwan_out']}"
|
||||||
return {
|
return {
|
||||||
|
@ -36,7 +38,7 @@ class OSAnalyticsADDaily(IOSExtraction):
|
||||||
"data": record_data,
|
"data": record_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -46,7 +48,7 @@ class OSAnalyticsADDaily(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=OSANALYTICS_ADDAILY_BACKUP_IDS,
|
self._find_ios_database(backup_ids=OSANALYTICS_ADDAILY_BACKUP_IDS,
|
||||||
root_paths=OSANALYTICS_ADDAILY_ROOT_PATHS)
|
root_paths=OSANALYTICS_ADDAILY_ROOT_PATHS)
|
||||||
self.log.info("Found com.apple.osanalytics.addaily plist at path: %s", self.file_path)
|
self.log.info("Found com.apple.osanalytics.addaily plist at path: %s", self.file_path)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -23,15 +24,16 @@ SAFARI_BROWSER_STATE_ROOT_PATHS = [
|
||||||
class SafariBrowserState(IOSExtraction):
|
class SafariBrowserState(IOSExtraction):
|
||||||
"""This module extracts all Safari browser state records."""
|
"""This module extracts all Safari browser state records."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self._session_history_count = 0
|
self._session_history_count = 0
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["last_viewed_timestamp"],
|
"timestamp": record["last_viewed_timestamp"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -39,7 +41,7 @@ class SafariBrowserState(IOSExtraction):
|
||||||
"data": f"{record['tab_title']} - {record['tab_url']}"
|
"data": f"{record['tab_title']} - {record['tab_url']}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ class SafariBrowserState(IOSExtraction):
|
||||||
"safari_browser_state_db": os.path.relpath(db_path, self.target_path),
|
"safari_browser_state_db": os.path.relpath(db_path, self.target_path),
|
||||||
})
|
})
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
for backup_file in self._get_backup_files_from_manifest(relative_path=SAFARI_BROWSER_STATE_BACKUP_RELPATH):
|
for backup_file in self._get_backup_files_from_manifest(relative_path=SAFARI_BROWSER_STATE_BACKUP_RELPATH):
|
||||||
self.file_path = self._get_backup_file_from_id(backup_file["file_id"])
|
self.file_path = self._get_backup_file_from_id(backup_file["file_id"])
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -25,13 +26,14 @@ class SafariHistory(IOSExtraction):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -73,7 +75,7 @@ class SafariHistory(IOSExtraction):
|
||||||
if elapsed_time.seconds == 0:
|
if elapsed_time.seconds == 0:
|
||||||
self.log.warning("Redirect took less than a second! (%d milliseconds)", elapsed_ms)
|
self.log.warning("Redirect took less than a second! (%d milliseconds)", elapsed_ms)
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
self._find_injections()
|
self._find_injections()
|
||||||
|
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
|
@ -117,7 +119,7 @@ class SafariHistory(IOSExtraction):
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
for history_file in self._get_backup_files_from_manifest(relative_path=SAFARI_HISTORY_BACKUP_RELPATH):
|
for history_file in self._get_backup_files_from_manifest(relative_path=SAFARI_HISTORY_BACKUP_RELPATH):
|
||||||
history_path = self._get_backup_file_from_id(history_file["file_id"])
|
history_path = self._get_backup_file_from_id(history_file["file_id"])
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -24,13 +25,14 @@ SHORTCUT_ROOT_PATHS = [
|
||||||
class Shortcuts(IOSExtraction):
|
class Shortcuts(IOSExtraction):
|
||||||
"""This module extracts all info about SMS/iMessage attachments."""
|
"""This module extracts all info about SMS/iMessage attachments."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
found_urls = ""
|
found_urls = ""
|
||||||
if record["action_urls"]:
|
if record["action_urls"]:
|
||||||
found_urls = "- URLs in actions: {}".format(", ".join(record["action_urls"]))
|
found_urls = "- URLs in actions: {}".format(", ".join(record["action_urls"]))
|
||||||
|
@ -50,7 +52,7 @@ class Shortcuts(IOSExtraction):
|
||||||
"data": f"iOS Shortcut '{record['shortcut_name'].decode('utf-8')}': {desc} {found_urls}"
|
"data": f"iOS Shortcut '{record['shortcut_name'].decode('utf-8')}': {desc} {found_urls}"
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ class Shortcuts(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=SHORTCUT_BACKUP_IDS,
|
self._find_ios_database(backup_ids=SHORTCUT_BACKUP_IDS,
|
||||||
root_paths=SHORTCUT_ROOT_PATHS)
|
root_paths=SHORTCUT_ROOT_PATHS)
|
||||||
self.log.info("Found Shortcuts database at path: %s", self.file_path)
|
self.log.info("Found Shortcuts database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
|
@ -22,13 +23,14 @@ SMS_ROOT_PATHS = [
|
||||||
class SMS(IOSExtraction):
|
class SMS(IOSExtraction):
|
||||||
"""This module extracts all SMS messages containing links."""
|
"""This module extracts all SMS messages containing links."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
text = record["text"].replace("\n", "\\n")
|
text = record["text"].replace("\n", "\\n")
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
|
@ -37,7 +39,7 @@ class SMS(IOSExtraction):
|
||||||
"data": f"{record['service']}: {record['guid']} \"{text}\" from {record['phone_number']} ({record['account']})"
|
"data": f"{record['service']}: {record['guid']} \"{text}\" from {record['phone_number']} ({record['account']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ class SMS(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=SMS_BACKUP_IDS,
|
self._find_ios_database(backup_ids=SMS_BACKUP_IDS,
|
||||||
root_paths=SMS_ROOT_PATHS)
|
root_paths=SMS_ROOT_PATHS)
|
||||||
self.log.info("Found SMS database at path: %s", self.file_path)
|
self.log.info("Found SMS database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
|
@ -21,13 +22,14 @@ SMS_ROOT_PATHS = [
|
||||||
class SMSAttachments(IOSExtraction):
|
class SMSAttachments(IOSExtraction):
|
||||||
"""This module extracts all info about SMS/iMessage attachments."""
|
"""This module extracts all info about SMS/iMessage attachments."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
return {
|
return {
|
||||||
"timestamp": record["isodate"],
|
"timestamp": record["isodate"],
|
||||||
"module": self.__class__.__name__,
|
"module": self.__class__.__name__,
|
||||||
|
@ -36,7 +38,7 @@ class SMSAttachments(IOSExtraction):
|
||||||
f"with {record['total_bytes']} bytes (is_sticker: {record['is_sticker']}, has_user_info: {record['has_user_info']})"
|
f"with {record['total_bytes']} bytes (is_sticker: {record['is_sticker']}, has_user_info: {record['has_user_info']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=SMS_BACKUP_IDS,
|
self._find_ios_database(backup_ids=SMS_BACKUP_IDS,
|
||||||
root_paths=SMS_ROOT_PATHS)
|
root_paths=SMS_ROOT_PATHS)
|
||||||
self.log.info("Found SMS database at path: %s", self.file_path)
|
self.log.info("Found SMS database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
@ -47,13 +48,14 @@ AUTH_REASONS = {
|
||||||
class TCC(IOSExtraction):
|
class TCC(IOSExtraction):
|
||||||
"""This module extracts records from the TCC.db SQLite database."""
|
"""This module extracts records from the TCC.db SQLite database."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
if "last_modified" in record:
|
if "last_modified" in record:
|
||||||
if "allowed_value" in record:
|
if "allowed_value" in record:
|
||||||
msg = f"Access to {record['service']} by {record['client']} {record['allowed_value']}"
|
msg = f"Access to {record['service']} by {record['client']} {record['allowed_value']}"
|
||||||
|
@ -66,7 +68,7 @@ class TCC(IOSExtraction):
|
||||||
"data": msg
|
"data": msg
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ class TCC(IOSExtraction):
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=TCC_BACKUP_IDS, root_paths=TCC_ROOT_PATHS)
|
self._find_ios_database(backup_ids=TCC_BACKUP_IDS, root_paths=TCC_ROOT_PATHS)
|
||||||
self.log.info("Found TCC database at path: %s", self.file_path)
|
self.log.info("Found TCC database at path: %s", self.file_path)
|
||||||
self.process_db(self.file_path)
|
self.process_db(self.file_path)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -22,15 +23,16 @@ class WebkitResourceLoadStatistics(IOSExtraction):
|
||||||
"""This module extracts records from WebKit ResourceLoadStatistics observations.db."""
|
"""This module extracts records from WebKit ResourceLoadStatistics observations.db."""
|
||||||
# TODO: Add serialize().
|
# TODO: Add serialize().
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
self.results = {} if not results else results
|
self.results = {} if not results else results
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
|
||||||
if len(self.results[key]) > 0:
|
if len(self.results[key]) > 0:
|
||||||
self.log.info("Extracted a total of %d records from %s", len(self.results[key]), db_path)
|
self.log.info("Extracted a total of %d records from %s", len(self.results[key]), db_path)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
try:
|
try:
|
||||||
for backup_file in self._get_backup_files_from_manifest(relative_path=WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH):
|
for backup_file in self._get_backup_files_from_manifest(relative_path=WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH):
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
|
|
||||||
|
@ -29,8 +30,9 @@ class WebkitSessionResourceLog(IOSExtraction):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -51,7 +53,7 @@ class WebkitSessionResourceLog(IOSExtraction):
|
||||||
|
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ class WebkitSessionResourceLog(IOSExtraction):
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
if self.is_backup:
|
if self.is_backup:
|
||||||
for log_file in self._get_backup_files_from_manifest(relative_path=WEBKIT_SESSION_RESOURCE_LOG_BACKUP_RELPATH):
|
for log_file in self._get_backup_files_from_manifest(relative_path=WEBKIT_SESSION_RESOURCE_LOG_BACKUP_RELPATH):
|
||||||
log_path = self._get_backup_file_from_id(log_file["file_id"])
|
log_path = self._get_backup_file_from_id(log_file["file_id"])
|
||||||
|
|
|
@ -24,13 +24,14 @@ WHATSAPP_ROOT_PATHS = [
|
||||||
class Whatsapp(IOSExtraction):
|
class Whatsapp(IOSExtraction):
|
||||||
"""This module extracts all WhatsApp messages containing links."""
|
"""This module extracts all WhatsApp messages containing links."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
text = record.get("ZTEXT", "").replace("\n", "\\n")
|
text = record.get("ZTEXT", "").replace("\n", "\\n")
|
||||||
links_text = ""
|
links_text = ""
|
||||||
if record["links"]:
|
if record["links"]:
|
||||||
|
@ -43,7 +44,7 @@ class Whatsapp(IOSExtraction):
|
||||||
"data": f"\'{text}\' from {record.get('ZFROMJID', 'Unknown')}{links_text}",
|
"data": f"\'{text}\' from {record.get('ZFROMJID', 'Unknown')}{links_text}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ class Whatsapp(IOSExtraction):
|
||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self._find_ios_database(backup_ids=WHATSAPP_BACKUP_IDS,
|
self._find_ios_database(backup_ids=WHATSAPP_BACKUP_IDS,
|
||||||
root_paths=WHATSAPP_ROOT_PATHS)
|
root_paths=WHATSAPP_ROOT_PATHS)
|
||||||
self.log.info("Found WhatsApp database at path: %s", self.file_path)
|
self.log.info("Found WhatsApp database at path: %s", self.file_path)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import operator
|
import operator
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -15,8 +16,9 @@ from .base import IOSExtraction
|
||||||
class NetBase(IOSExtraction):
|
class NetBase(IOSExtraction):
|
||||||
"""This class provides a base for DataUsage and NetUsage extraction modules."""
|
"""This class provides a base for DataUsage and NetUsage extraction modules."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, target_path=None, results_path=None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
fast_mode=False, log=None, results=[]):
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
log: logging.Logger = None, results: list = []) -> None:
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
@ -78,7 +80,7 @@ class NetBase(IOSExtraction):
|
||||||
|
|
||||||
self.log.info("Extracted information on %d processes", len(self.results))
|
self.log.info("Extracted information on %d processes", len(self.results))
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record: dict) -> None:
|
||||||
record_data = f"{record['proc_name']} (Bundle ID: {record['bundle_id']}, ID: {record['proc_id']})"
|
record_data = f"{record['proc_name']} (Bundle ID: {record['bundle_id']}, ID: {record['proc_id']})"
|
||||||
record_data_usage = record_data + f" WIFI IN: {record['wifi_in']}, WIFI OUT: {record['wifi_out']} - " \
|
record_data_usage = record_data + f" WIFI IN: {record['wifi_in']}, WIFI OUT: {record['wifi_out']} - " \
|
||||||
f"WWAN IN: {record['wwan_in']}, WWAN OUT: {record['wwan_out']}"
|
f"WWAN IN: {record['wwan_in']}, WWAN OUT: {record['wwan_out']}"
|
||||||
|
@ -214,7 +216,7 @@ class NetBase(IOSExtraction):
|
||||||
|
|
||||||
self.results = sorted(self.results, key=operator.itemgetter("first_isodate"))
|
self.results = sorted(self.results, key=operator.itemgetter("first_isodate"))
|
||||||
|
|
||||||
def check_indicators(self):
|
def check_indicators(self) -> None:
|
||||||
# Check for manipulated process records.
|
# Check for manipulated process records.
|
||||||
# TODO: Catching KeyError for live_isodate for retro-compatibility.
|
# TODO: Catching KeyError for live_isodate for retro-compatibility.
|
||||||
# This is not very good.
|
# This is not very good.
|
||||||
|
|
|
@ -244,18 +244,19 @@ IPHONE_IOS_VERSIONS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_device_desc_from_id(identifier, devices_list=IPHONE_MODELS):
|
def get_device_desc_from_id(identifier: str,
|
||||||
|
devices_list: list = IPHONE_MODELS) -> str:
|
||||||
for model in IPHONE_MODELS:
|
for model in IPHONE_MODELS:
|
||||||
if identifier == model["identifier"]:
|
if identifier == model["identifier"]:
|
||||||
return model["description"]
|
return model["description"]
|
||||||
|
|
||||||
|
|
||||||
def find_version_by_build(build):
|
def find_version_by_build(build: str) -> str:
|
||||||
build = build.upper()
|
build = build.upper()
|
||||||
for version in IPHONE_IOS_VERSIONS:
|
for version in IPHONE_IOS_VERSIONS:
|
||||||
if build == version["build"]:
|
if build == version["build"]:
|
||||||
return version["version"]
|
return version["version"]
|
||||||
|
|
||||||
|
|
||||||
def latest_ios_version():
|
def latest_ios_version() -> str:
|
||||||
return IPHONE_IOS_VERSIONS[-1]
|
return IPHONE_IOS_VERSIONS[-1]
|
||||||
|
|
Loading…
Reference in New Issue