Clean up code TODOs and type checks

This commit is contained in:
Janik Besendorf
2026-05-11 23:47:29 +02:00
parent f65d77036a
commit 2e5bf5f678
75 changed files with 147 additions and 116 deletions
+11 -4
View File
@@ -131,10 +131,17 @@ class DumpsysADBArtifact(AndroidArtifact):
) )
return return
# TODO: Parse AdbDebuggingManager line in output. start_of_json = content.find(b"\n{")
start_of_json = content.find(b"\n{") + 2 if start_of_json == -1:
end_of_json = content.rfind(b"}\n") - 2 self.log.error("Unable to find ADB manager state in dumpsys output")
json_content = content[start_of_json:end_of_json].rstrip() return
end_of_json = content.rfind(b"}\n")
if end_of_json == -1 or end_of_json <= start_of_json:
self.log.error("Unable to find complete ADB manager state in dumpsys output")
return
json_content = content[start_of_json + 2 : end_of_json - 2].rstrip()
parsed = self.indented_dump_parser(json_content) parsed = self.indented_dump_parser(json_content)
if parsed.get("debugging_manager") is None: if parsed.get("debugging_manager") is None:
@@ -14,9 +14,12 @@ from .artifact import AndroidArtifact
class DumpsysPackagesArtifact(AndroidArtifact): class DumpsysPackagesArtifact(AndroidArtifact):
def check_indicators(self) -> None: def check_indicators(self) -> None:
alerted_root_packages = set()
for result in self.results: for result in self.results:
# XXX: De-duplication Package detections
if result["package_name"] in ROOT_PACKAGES: if result["package_name"] in ROOT_PACKAGES:
if result["package_name"] in alerted_root_packages:
continue
alerted_root_packages.add(result["package_name"])
self.alertstore.medium( self.alertstore.medium(
f'Found an installed package related to rooting/jailbreaking: "{result["package_name"]}"', f'Found an installed package related to rooting/jailbreaking: "{result["package_name"]}"',
"", "",
+9 -6
View File
@@ -67,11 +67,14 @@ class Settings(AndroidArtifact):
# Check if one of the dangerous settings is using an unsafe # Check if one of the dangerous settings is using an unsafe
# value (different than the one specified). # value (different than the one specified).
if danger["key"] == key and danger["safe_value"] != value: if danger["key"] == key and danger["safe_value"] != value:
self.log.warning( self.alertstore.medium(
'Found suspicious "%s" setting "%s = %s" (%s)', f'Found suspicious "{namespace}" setting "{key} = {value}" ({danger["description"]})',
namespace, "",
key, {
value, "namespace": namespace,
danger["description"], "key": key,
"value": value,
"description": danger["description"],
},
) )
break break
@@ -101,8 +101,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
continue continue
if result.get("command_line", []): if result.get("command_line", []):
command_name = result.get("command_line")[0].split("/")[-1] command_name = result["command_line"][0].split("/")[-1]
command_name = result["command_line"][0]
ioc_match = self.indicators.check_process(command_name) ioc_match = self.indicators.check_process(command_name)
if ioc_match: if ioc_match:
self.alertstore.critical( self.alertstore.critical(
@@ -262,7 +261,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
@staticmethod @staticmethod
def _parse_timestamp_string(timestamp: str) -> str: def _parse_timestamp_string(timestamp: str) -> str:
timestamp_parsed = parser.parse(timestamp) timestamp_parsed = parser.parse(timestamp)
# HACK: Swap the local timestamp to UTC, so keep the original time and avoid timezone conversion. # Preserve the source wall-clock time while returning the project-wide ISO format.
local_timestamp = timestamp_parsed.replace(tzinfo=datetime.timezone.utc) local_timestamp = timestamp_parsed.replace(tzinfo=datetime.timezone.utc)
return convert_datetime_to_iso(local_timestamp) return convert_datetime_to_iso(local_timestamp)
@@ -41,7 +41,7 @@ class AQFFiles(AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -145,8 +145,8 @@ class AQFFiles(AndroidQFModule):
# Convert the UTC timestamp to local time on Android device's local timezone # Convert the UTC timestamp to local time on Android device's local timezone
local_timestamp = utc_timestamp.astimezone(device_timezone) local_timestamp = utc_timestamp.astimezone(device_timezone)
# HACK: We only output the UTC timestamp in convert_datetime_to_iso, we # Preserve the device-local wall-clock time while using
# set the timestamp timezone to UTC, to avoid the timezone conversion again. # the project-wide ISO conversion helper.
local_timestamp = local_timestamp.replace( local_timestamp = local_timestamp.replace(
tzinfo=datetime.timezone.utc tzinfo=datetime.timezone.utc
) )
@@ -22,7 +22,7 @@ class AQFGetProp(GetPropArtifact, AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -32,7 +32,7 @@ class AQFGetProp(GetPropArtifact, AndroidQFModule):
log=log, log=log,
results=results, results=results,
) )
self.results: list = [] self.results: list = [] if results is None else results
def run(self) -> None: def run(self) -> None:
getprop_files = self._get_files_by_pattern("*/getprop.txt") getprop_files = self._get_files_by_pattern("*/getprop.txt")
@@ -27,7 +27,7 @@ class AQFLogTimestamps(FileTimestampsArtifact, AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -30,7 +30,7 @@ class AQFPackages(AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class AQFProcesses(ProcessesArtifact, AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class AQFSettings(SettingsArtifact, AndroidQFModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -32,7 +32,7 @@ class AQFSettings(SettingsArtifact, AndroidQFModule):
log=log, log=log,
results=results, results=results,
) )
self.results: dict = {} self.results: dict = results if results is not None else {}
def run(self) -> None: def run(self) -> None:
for setting_file in self._get_files_by_pattern("*/settings_*.txt"): for setting_file in self._get_files_by_pattern("*/settings_*.txt"):
+1 -1
View File
@@ -23,7 +23,7 @@ class AndroidQFModule(MVTModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+4 -1
View File
@@ -32,7 +32,7 @@ class Mounts(MountsArtifact, AndroidQFModule):
log=log, log=log,
results=results, results=results,
) )
self.results: list = [] self.results: list = [] if results is None else results
def run(self) -> None: def run(self) -> None:
""" """
@@ -66,6 +66,9 @@ class Mounts(MountsArtifact, AndroidQFModule):
# AndroidQF format: array of strings like # AndroidQF format: array of strings like
# "/dev/block/dm-12 on / type ext4 (ro,seclabel,noatime)" # "/dev/block/dm-12 on / type ext4 (ro,seclabel,noatime)"
mount_content = "\n".join(json_data) mount_content = "\n".join(json_data)
else:
self.log.error("Expected mounts.json to contain a list of mount lines")
return
self.parse(mount_content) self.parse(mount_content)
except Exception as exc: except Exception as exc:
self.log.error("Failed to parse mount information: %s", exc) self.log.error("Failed to parse mount information: %s", exc)
-4
View File
@@ -21,10 +21,6 @@ from .base import AndroidQFModule
class SMS(AndroidQFModule): class SMS(AndroidQFModule):
""" """
This module analyse SMS file in backup This module analyse SMS file in backup
XXX: We should also de-duplicate this AQF module, but first we
need to add tests for loading encrypted SMS backups through the backup
sub-module.
""" """
def __init__( def __init__(
+1 -1
View File
@@ -22,7 +22,7 @@ class BackupModule(MVTModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -20,7 +20,7 @@ class SMS(BackupModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -22,7 +22,7 @@ class BugReportModule(MVTModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysAccessibility(DumpsysAccessibilityArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -24,7 +24,7 @@ class DumpsysActivities(DumpsysPackageActivitiesArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysADBState(DumpsysADBArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysAppops(DumpsysAppopsArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysBatteryDaily(DumpsysBatteryDailyArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysBatteryHistory(DumpsysBatteryHistoryArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -24,7 +24,7 @@ class DumpsysDBInfo(DumpsysDBInfoArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysGetProp(GetPropArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -23,7 +23,7 @@ class DumpsysPackages(DumpsysPackagesArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -22,7 +22,7 @@ class DumpsysPlatformCompat(DumpsysPlatformCompatArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -42,8 +42,10 @@ class DumpsysPlatformCompat(DumpsysPlatformCompatArtifact, BugReportModule):
) )
return return
data = data.decode("utf-8", errors="replace") decoded_data = data.decode("utf-8", errors="replace")
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE platform_compat:") content = self.extract_dumpsys_section(
decoded_data, "DUMP OF SERVICE platform_compat:"
)
self.parse(content) self.parse(content)
self.log.info("Found %d uninstalled apps", len(self.results)) self.log.info("Found %d uninstalled apps", len(self.results))
@@ -22,7 +22,7 @@ class DumpsysReceivers(DumpsysReceiversArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -24,7 +24,7 @@ class BugReportTimestamps(FileTimestampsArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -23,7 +23,7 @@ class Tombstones(TombstoneCrashArtifact, BugReportModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+4 -5
View File
@@ -29,9 +29,6 @@ class InvalidBackupPassword(AndroidBackupParsingError):
pass pass
# TODO: Need to clean all the following code and conform it to the coding style.
def to_utf8_bytes(input_bytes): def to_utf8_bytes(input_bytes):
output = [] output = []
for byte in input_bytes: for byte in input_bytes:
@@ -157,13 +154,13 @@ def decrypt_backup_data(encrypted_backup, password, encryption_algo, format_vers
checksum_salt=checksum_salt, checksum_salt=checksum_salt,
) )
# Decrypt and unpad backup data using derivied key. # Decrypt and unpad backup data using derived key.
cipher = Cipher(algorithms.AES(master_key), modes.CBC(master_iv)) cipher = Cipher(algorithms.AES(master_key), modes.CBC(master_iv))
decryptor = cipher.decryptor() decryptor = cipher.decryptor()
decrypted_tar = decryptor.update(encrypted_data) + decryptor.finalize() decrypted_tar = decryptor.update(encrypted_data) + decryptor.finalize()
unpadder = padding.PKCS7(128).unpadder() unpadder = padding.PKCS7(128).unpadder()
return unpadder.update(decrypted_tar) return unpadder.update(decrypted_tar) + unpadder.finalize()
def parse_backup_file(data, password=None): def parse_backup_file(data, password=None):
@@ -210,6 +207,8 @@ def parse_tar_for_sms(data):
or member.name.endswith("_mms_backup") or member.name.endswith("_mms_backup")
): ):
dhandler = tar.extractfile(member) dhandler = tar.extractfile(member)
if not dhandler:
continue
res.extend(parse_sms_file(dhandler.read())) res.extend(parse_sms_file(dhandler.read()))
return res return res
+2 -1
View File
@@ -8,5 +8,6 @@ from .module import MVTModule
class Artifact(MVTModule): class Artifact(MVTModule):
"""Base class for artifacts. """Base class for artifacts.
XXX: Inheriting from MVTModule to have the same signature as other modules. Not sure if this is a good idea. Artifacts share the MVTModule lifecycle so commands can run artifacts and
extraction modules through the same interface.
""" """
-1
View File
@@ -273,7 +273,6 @@ class Command:
): ):
continue continue
# FIXME: do we need the logger here
module_logger = logging.getLogger(module.__module__) module_logger = logging.getLogger(module.__module__)
m = module( m = module(
+7 -6
View File
@@ -51,7 +51,7 @@ class MVTModule:
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[Dict[str, Any]] = None, module_options: Optional[Dict[str, Any]] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
"""Initialize module. """Initialize module.
@@ -71,15 +71,13 @@ class MVTModule:
self.file_path: Optional[str] = file_path self.file_path: Optional[str] = file_path
self.target_path: Optional[str] = target_path self.target_path: Optional[str] = target_path
self.results_path: Optional[str] = results_path self.results_path: Optional[str] = results_path
self.module_options: Optional[Dict[str, Any]] = ( self.module_options: Dict[str, Any] = module_options if module_options else {}
module_options if module_options else {}
)
self.log = log self.log = log
self.indicators: Optional[Indicators] = None self.indicators: Optional[Indicators] = None
self.alertstore: AlertStore = AlertStore(log=log) self.alertstore: AlertStore = AlertStore(log=log)
self.results: ModuleResults = results if results else [] self.results: ModuleResults = results if results is not None else []
self.timeline: ModuleTimeline = [] self.timeline: ModuleTimeline = []
@classmethod @classmethod
@@ -109,11 +107,14 @@ class MVTModule:
name = self.get_slug() name = self.get_slug()
if self.results: if self.results:
converted_results: Any
if isinstance(self.results, dict): if isinstance(self.results, dict):
converted_results = self.results converted_results = self.results
else: else:
converted_results = [ converted_results = [
asdict(result) if is_dataclass(result) else result asdict(result)
if is_dataclass(result) and not isinstance(result, type)
else result
for result in self.results for result in self.results
] ]
results_file_name = f"{name}.json" results_file_name = f"{name}.json"
+4 -1
View File
@@ -16,7 +16,10 @@ from typing import Any, Dict, List, Union
ModuleAtomicResult = Dict[str, Any] ModuleAtomicResult = Dict[str, Any]
ModuleResults = List[ModuleAtomicResult] # Extraction modules historically use either a list of records or grouped
# dictionaries keyed by source path. Keep this alias broad until those shapes
# are modeled per module.
ModuleResults = Any
@dataclass @dataclass
+2 -2
View File
@@ -25,7 +25,7 @@ class BackupInfo(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -36,7 +36,7 @@ class BackupInfo(IOSExtraction):
results=results, results=results,
) )
self.results: dict = {} self.results: dict = results if results is not None else {}
def run(self) -> None: def run(self) -> None:
if not self.target_path: if not self.target_path:
@@ -33,7 +33,7 @@ class ConfigurationProfiles(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -33,7 +33,7 @@ class Manifest(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -34,7 +34,7 @@ class ProfileEvents(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+2 -2
View File
@@ -30,7 +30,7 @@ class IOSExtraction(MVTModule):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -52,7 +52,7 @@ class IOSExtraction(MVTModule):
:param file_path: Path to the malformed database file. :param file_path: Path to the malformed database file.
""" """
# TODO: Find a better solution. # SQLite's immutable mode cannot open databases with active WAL files.
if not forced: if not forced:
# If the database is open, do not use immutable # If the database is open, do not use immutable
if os.path.isfile(file_path + "-shm"): if os.path.isfile(file_path + "-shm"):
+2 -2
View File
@@ -34,7 +34,7 @@ class Analytics(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -44,7 +44,7 @@ class Analytics(IOSExtraction):
log=log, log=log,
results=results, results=results,
) )
self.results: list = [] self.results: list = [] if results is None else results
def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult:
return { return {
@@ -30,7 +30,7 @@ class AnalyticsIOSVersions(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+5 -1
View File
@@ -25,7 +25,7 @@ class CacheFiles(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -99,6 +99,10 @@ class CacheFiles(IOSExtraction):
def run(self) -> None: def run(self) -> None:
self.results: dict = {} self.results: dict = {}
if not self.target_path:
self.log.error("No filesystem dump path provided")
return
for root, _, files in os.walk(self.target_path): for root, _, files in os.walk(self.target_path):
for file_name in files: for file_name in files:
if file_name != "Cache.db": if file_name != "Cache.db":
+5 -1
View File
@@ -29,7 +29,7 @@ class Filesystem(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -73,6 +73,10 @@ class Filesystem(IOSExtraction):
) )
def run(self) -> None: def run(self) -> None:
if not self.target_path:
self.log.error("No filesystem dump path provided")
return
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:
+1 -1
View File
@@ -30,7 +30,7 @@ class Netusage(NetBase):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+2 -2
View File
@@ -31,7 +31,7 @@ class SafariFavicon(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -41,7 +41,7 @@ class SafariFavicon(IOSExtraction):
log=log, log=log,
results=results, results=results,
) )
self.results: list = [] self.results: list = [] if results is None else results
def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult:
return { return {
+1 -1
View File
@@ -30,7 +30,7 @@ class ShutdownLog(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+2 -2
View File
@@ -32,7 +32,7 @@ class IOSVersionHistory(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -42,7 +42,7 @@ class IOSVersionHistory(IOSExtraction):
log=log, log=log,
results=results, results=results,
) )
self.results: list = [] self.results: list = [] if results is None else results
def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult:
return { return {
+1 -1
View File
@@ -34,7 +34,7 @@ class WebkitIndexedDB(WebkitBase):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -32,7 +32,7 @@ class WebkitLocalStorage(WebkitBase):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -28,7 +28,7 @@ class WebkitSafariViewService(WebkitBase):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -41,7 +41,7 @@ class Applications(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -31,7 +31,7 @@ class Calendar(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -27,7 +27,7 @@ class Calls(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: list = [], results: Optional[list] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -31,7 +31,7 @@ class ChromeFavicon(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -33,7 +33,7 @@ class ChromeHistory(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -29,7 +29,7 @@ class Contacts(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -33,7 +33,7 @@ class FirefoxFavicon(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -37,7 +37,7 @@ class FirefoxHistory(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -27,7 +27,7 @@ class GlobalPreferences(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -36,7 +36,7 @@ class IDStatusCache(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -228,7 +228,7 @@ class InteractionC(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -2
View File
@@ -36,7 +36,7 @@ class LocationdClients(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -147,7 +147,6 @@ class LocationdClients(IOSExtraction):
# Some migration information are int and not dicts # Some migration information are int and not dicts
if not isinstance(file_plist[key], dict): if not isinstance(file_plist[key], dict):
continue continue
# FIXME: unclear key format in iOS 17
result = file_plist[key] result = file_plist[key]
result["package"] = key.rstrip(":") result["package"] = key.rstrip(":")
for timestamp in self.timestamps: for timestamp in self.timestamps:
+1 -1
View File
@@ -31,7 +31,7 @@ class Datausage(NetBase):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -35,7 +35,7 @@ class OSAnalyticsADDaily(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -36,7 +36,7 @@ class SafariBrowserState(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -38,7 +38,7 @@ class SafariHistory(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -37,7 +37,7 @@ class Shortcuts(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -35,7 +35,7 @@ class SMS(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -35,7 +35,7 @@ class SMSAttachments(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -56,7 +56,7 @@ class TCC(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -37,7 +37,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -39,7 +39,7 @@ class WebkitSessionResourceLog(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
@@ -50,7 +50,7 @@ class WebkitSessionResourceLog(IOSExtraction):
results=results, results=results,
) )
self.results: dict = {} self.results: dict = results if results is not None else {}
@staticmethod @staticmethod
def _extract_domains(entries): def _extract_domains(entries):
@@ -77,14 +77,21 @@ class WebkitSessionResourceLog(IOSExtraction):
entry["redirect_destination"] entry["redirect_destination"]
) )
# TODO: Currently not used. subframe_origins = self._extract_domains(
# subframe_origins = self._extract_domains( entry["subframe_under_origin"]
# entry["subframe_under_origin"]) )
# subresource_domains = self._extract_domains( subresource_domains = self._extract_domains(
# entry["subresource_under_origin"]) entry["subresource_under_origin"]
)
all_origins = list( all_origins = list(
set([entry["origin"]] + source_domains + destination_domains) set(
[entry["origin"]]
+ source_domains
+ destination_domains
+ subframe_origins
+ subresource_domains
)
) )
ioc_match = self.indicators.check_urls(all_origins) ioc_match = self.indicators.check_urls(all_origins)
+1 -1
View File
@@ -33,7 +33,7 @@ class Whatsapp(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+1 -1
View File
@@ -30,7 +30,7 @@ class NetBase(IOSExtraction):
results_path: Optional[str] = None, results_path: Optional[str] = None,
module_options: Optional[dict] = None, module_options: Optional[dict] = None,
log: logging.Logger = logging.getLogger(__name__), log: logging.Logger = logging.getLogger(__name__),
results: ModuleResults = [], results: Optional[ModuleResults] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
file_path=file_path, file_path=file_path,
+2 -1
View File
@@ -21,4 +21,5 @@ class TestSettingsModule:
run_module(m) run_module(m)
assert len(m.results) == 1 assert len(m.results) == 1
assert "random" in m.results.keys() assert "random" in m.results.keys()
assert len(m.alertstore.alerts) == 0 assert len(m.alertstore.alerts) == 1
assert "samsung_errorlog_agree" in m.alertstore.alerts[0].message