From 3608576417b1bb557fb7337ccc205c3cb5861fbb Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 20 Jun 2022 20:26:18 +0200 Subject: [PATCH] Added new AnalyticsIOSVersions to collect a timeline of iOS versions --- mvt/common/command.py | 3 ++ mvt/ios/modules/fs/__init__.py | 7 +-- mvt/ios/modules/fs/analytics.py | 5 +- mvt/ios/modules/fs/analytics_ios_versions.py | 54 ++++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 mvt/ios/modules/fs/analytics_ios_versions.py diff --git a/mvt/common/command.py b/mvt/common/command.py index 36bf076..d548d4a 100644 --- a/mvt/common/command.py +++ b/mvt/common/command.py @@ -108,6 +108,9 @@ class Command(object): try: with open(file_path, "rb") as handle: h.update(handle.read()) + except FileNotFoundError: + self.log.error("Failed to hash the file %s: might be a symlink", file_path) + continue except PermissionError: self.log.error("Failed to hash the file %s: permission denied", file_path) continue diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index ee5c4dc..4450c80 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -4,6 +4,7 @@ # https://license.mvt.re/1.1/ from .analytics import Analytics +from .analytics_ios_versions import AnalyticsIOSVersions from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage @@ -14,6 +15,6 @@ from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService -FS_MODULES = [CacheFiles, Filesystem, Netusage, Analytics, SafariFavicon, ShutdownLog, - IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, - WebkitSafariViewService] +FS_MODULES = [CacheFiles, Filesystem, Netusage, Analytics, AnalyticsIOSVersions, + SafariFavicon, ShutdownLog, IOSVersionHistory, WebkitIndexedDB, + WebkitLocalStorage, WebkitSafariViewService] diff --git a/mvt/ios/modules/fs/analytics.py b/mvt/ios/modules/fs/analytics.py index 46bfe57..96aec27 100644 --- a/mvt/ios/modules/fs/analytics.py +++ b/mvt/ios/modules/fs/analytics.py @@ -115,12 +115,15 @@ class Analytics(IOSExtraction): cur.close() conn.close() - def run(self) -> None: + def process_analytics_dbs(self): for file_path in self._get_fs_files_from_patterns(ANALYTICS_DB_PATH): self.file_path = file_path self.log.info("Found Analytics database file at path: %s", file_path) self._extract_analytics_data() + def run(self) -> None: + self.process_analytics_dbs() + self.log.info("Extracted %d records from analytics databases", len(self.results)) diff --git a/mvt/ios/modules/fs/analytics_ios_versions.py b/mvt/ios/modules/fs/analytics_ios_versions.py new file mode 100644 index 0000000..1b139ad --- /dev/null +++ b/mvt/ios/modules/fs/analytics_ios_versions.py @@ -0,0 +1,54 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 Claudio Guarnieri. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import logging + +from mvt.ios.versions import find_version_by_build + +from ..base import IOSExtraction +from .analytics import Analytics + + +class AnalyticsIOSVersions(IOSExtraction): + """This module leverages the Analytics module in order to extract + a timeline of build numbers from the private/var/Keychains/Analytics/*.db + files.""" + + def __init__(self, file_path: str = None, target_path: str = None, + 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, + results_path=results_path, fast_mode=fast_mode, + log=log, results=results) + + def serialize(self, record: dict) -> None: + return { + "timestamp": record["timestamp"], + "module": self.__class__.__name__, + "event": "analytics_ios_version", + "data": f"Seen iOS version {record['version']} ({record['build']})", + } + + def run(self): + anl = Analytics(target_path=self.target_path, log=self.log) + anl.process_analytics_dbs() + + builds = [] + for result in anl.results: + build = result.get("build") + if not build or build in builds: + continue + + version = find_version_by_build(build) + + self.log.info("iOS version %s (%s) first appeared on %s", + version, build, result["timestamp"]) + self.results.append({ + "timestamp": result["timestamp"], + "version": version, + "build": build, + }) + + builds.append(build)