Standardized code with flake8

This commit is contained in:
Nex 2021-11-19 15:27:51 +01:00
parent b3a464ba58
commit 512f40dcb4
63 changed files with 127 additions and 65 deletions

View File

@ -9,7 +9,9 @@ import os
import click import click
from rich.logging import RichHandler from rich.logging import RichHandler
from mvt.common.help import * from mvt.common.help import HELP_MSG_MODULE, HELP_MSG_IOC
from mvt.common.help import HELP_MSG_OUTPUT, HELP_MSG_LIST_MODULES
from mvt.common.help import HELP_MSG_SERIAL
from mvt.common.indicators import Indicators, IndicatorsFileBadFormat from mvt.common.indicators import Indicators, IndicatorsFileBadFormat
from mvt.common.logo import logo from mvt.common.logo import logo
from mvt.common.module import run_module, save_timeline from mvt.common.module import run_module, save_timeline
@ -26,6 +28,7 @@ logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[
RichHandler(show_path=False, log_time_format="%X")]) RichHandler(show_path=False, log_time_format="%X")])
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#============================================================================== #==============================================================================
# Main # Main
#============================================================================== #==============================================================================
@ -191,7 +194,7 @@ def check_backup(ctx, iocs, output, backup_path, serial):
log.critical("The path you specified is a not a folder!") log.critical("The path you specified is a not a folder!")
if os.path.basename(backup_path) == "backup.ab": if os.path.basename(backup_path) == "backup.ab":
log.info("You can use ABE (https://github.com/nelenkov/android-backup-extractor) " \ log.info("You can use ABE (https://github.com/nelenkov/android-backup-extractor) "
"to extract 'backup.ab' files!") "to extract 'backup.ab' files!")
ctx.exit(1) ctx.exit(1)

View File

@ -16,6 +16,7 @@ from .modules.adb.packages import Packages
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# TODO: Would be better to replace tqdm with rich.progress to reduce # TODO: Would be better to replace tqdm with rich.progress to reduce
# the number of dependencies. Need to investigate whether # the number of dependencies. Need to investigate whether
# it's possible to have a similar callback system. # it's possible to have a similar callback system.
@ -137,7 +138,7 @@ class DownloadAPKs(AndroidExtraction):
packages_selection.append(package) packages_selection.append(package)
log.info("Selected only %d packages which are not marked as system", log.info("Selected only %d packages which are not marked as system",
len(packages_selection)) len(packages_selection))
if len(packages_selection) == 0: if len(packages_selection) == 0:
log.info("No packages were selected for download") log.info("No packages were selected for download")

View File

@ -13,6 +13,7 @@ from rich.text import Text
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def koodous_lookup(packages): def koodous_lookup(packages):
log.info("Looking up all extracted files on Koodous (www.koodous.com)") log.info("Looking up all extracted files on Koodous (www.koodous.com)")
log.info("This might take a while...") log.info("This might take a while...")

View File

@ -13,6 +13,7 @@ from rich.text import Text
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def get_virustotal_report(hashes): def get_virustotal_report(hashes):
apikey = "233f22e200ca5822bd91103043ccac138b910db79f29af5616a9afe8b6f215ad" apikey = "233f22e200ca5822bd91103043ccac138b910db79f29af5616a9afe8b6f215ad"
url = f"https://www.virustotal.com/partners/sysinternals/file-reports?apikey={apikey}" url = f"https://www.virustotal.com/partners/sysinternals/file-reports?apikey={apikey}"
@ -36,6 +37,7 @@ def get_virustotal_report(hashes):
log.error("Unexpected response from VirusTotal: %s", res.status_code) log.error("Unexpected response from VirusTotal: %s", res.status_code)
return None return None
def virustotal_lookup(packages): def virustotal_lookup(packages):
log.info("Looking up all extracted files on VirusTotal (www.virustotal.com)") log.info("Looking up all extracted files on VirusTotal (www.virustotal.com)")
@ -48,6 +50,7 @@ def virustotal_lookup(packages):
total_unique_hashes = len(unique_hashes) total_unique_hashes = len(unique_hashes)
detections = {} detections = {}
def virustotal_query(batch): def virustotal_query(batch):
report = get_virustotal_report(batch) report = get_virustotal_report(batch)
if not report: if not report:

View File

@ -25,6 +25,7 @@ log = logging.getLogger(__name__)
ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey") ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey")
ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub") 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."""
@ -89,7 +90,7 @@ class AndroidExtraction(MVTModule):
except OSError as e: except OSError as e:
if e.errno == 113 and self.serial: if e.errno == 113 and self.serial:
log.critical("Unable to connect to the device %s: did you specify the correct IP addres?", log.critical("Unable to connect to the device %s: did you specify the correct IP addres?",
self.serial) self.serial)
sys.exit(-1) sys.exit(-1)
else: else:
break break

View File

@ -16,6 +16,7 @@ log = logging.getLogger(__name__)
CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History" 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."""

View File

@ -10,6 +10,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class DumpsysBatterystats(AndroidExtraction): class DumpsysBatterystats(AndroidExtraction):
"""This module extracts stats on battery consumption by processes.""" """This module extracts stats on battery consumption by processes."""
@ -30,7 +31,7 @@ class DumpsysBatterystats(AndroidExtraction):
handle.write(stats) handle.write(stats)
log.info("Records from dumpsys batterystats stored at %s", log.info("Records from dumpsys batterystats stored at %s",
stats_path) stats_path)
history = self._adb_command("dumpsys batterystats --history") history = self._adb_command("dumpsys batterystats --history")
if self.output_folder: if self.output_folder:

View File

@ -10,6 +10,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) 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."""
@ -30,6 +31,6 @@ class DumpsysFull(AndroidExtraction):
handle.write(stats) handle.write(stats)
log.info("Full dumpsys output stored at %s", log.info("Full dumpsys output stored at %s",
stats_path) stats_path)
self._adb_disconnect() self._adb_disconnect()

View File

@ -10,6 +10,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class DumpsysProcstats(AndroidExtraction): class DumpsysProcstats(AndroidExtraction):
"""This module extracts stats on memory consumption by processes.""" """This module extracts stats on memory consumption by processes."""

View File

@ -14,6 +14,7 @@ ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"
ACTION_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED" ACTION_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED"
ACTION_PHONE_STATE = "android.intent.action.PHONE_STATE" ACTION_PHONE_STATE = "android.intent.action.PHONE_STATE"
class DumpsysReceivers(AndroidExtraction): class DumpsysReceivers(AndroidExtraction):
"""This module extracts details on receivers for risky activities.""" """This module extracts details on receivers for risky activities."""
@ -66,16 +67,16 @@ class DumpsysReceivers(AndroidExtraction):
if activity == ACTION_NEW_OUTGOING_SMS: if activity == ACTION_NEW_OUTGOING_SMS:
self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"", self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"",
receiver) receiver)
elif activity == ACTION_SMS_RECEIVED: elif activity == ACTION_SMS_RECEIVED:
self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"", self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"",
receiver) receiver)
elif activity == ACTION_DATA_SMS_RECEIVED: elif activity == ACTION_DATA_SMS_RECEIVED:
self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"", self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"",
receiver) receiver)
elif activity == ACTION_PHONE_STATE: elif activity == ACTION_PHONE_STATE:
self.log.info("Found a receiver monitoring telephony state: \"%s\"", self.log.info("Found a receiver monitoring telephony state: \"%s\"",
receiver) receiver)
self.results.append({ self.results.append({
"activity": activity, "activity": activity,

View File

@ -10,6 +10,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Files(AndroidExtraction): class Files(AndroidExtraction):
"""This module extracts the list of installed packages.""" """This module extracts the list of installed packages."""

View File

@ -12,6 +12,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Packages(AndroidExtraction): class Packages(AndroidExtraction):
"""This module extracts the list of installed packages.""" """This module extracts the list of installed packages."""
@ -49,11 +50,10 @@ class Packages(AndroidExtraction):
root_packages = root_packages_string.decode("utf-8").split("\n") root_packages = root_packages_string.decode("utf-8").split("\n")
root_packages = [rp.strip() for rp in root_packages] root_packages = [rp.strip() for rp in root_packages]
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\"",
result["package_name"]) result["package_name"])
self.detected.append(result) self.detected.append(result)
if result["package_name"] in self.indicators.ioc_app_ids: if result["package_name"] in self.indicators.ioc_app_ids:
self.log.warning("Found a malicious package name: \"%s\"", self.log.warning("Found a malicious package name: \"%s\"",

View File

@ -9,6 +9,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Processes(AndroidExtraction): class Processes(AndroidExtraction):
"""This module extracts details on running processes.""" """This module extracts details on running processes."""

View File

@ -12,6 +12,7 @@ from .base import AndroidExtraction
log = logging.getLogger(__name__) 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."""

View File

@ -15,12 +15,12 @@ log = logging.getLogger(__name__)
SMS_BUGLE_PATH = "data/data/com.google.android.apps.messaging/databases/bugle_db" SMS_BUGLE_PATH = "data/data/com.google.android.apps.messaging/databases/bugle_db"
SMS_BUGLE_QUERY = """ SMS_BUGLE_QUERY = """
SELECT SELECT
ppl.normalized_destination AS number, ppl.normalized_destination AS number,
p.timestamp AS timestamp, p.timestamp AS timestamp,
CASE WHEN m.sender_id IN CASE WHEN m.sender_id IN
(SELECT _id FROM participants WHERE contact_id=-1) (SELECT _id FROM participants WHERE contact_id=-1)
THEN 2 ELSE 1 END incoming, p.text AS text THEN 2 ELSE 1 END incoming, p.text AS text
FROM messages m, conversations c, parts p, FROM messages m, conversations c, parts p,
participants ppl, conversation_participants cp participants ppl, conversation_participants cp
WHERE (m.conversation_id = c._id) WHERE (m.conversation_id = c._id)
@ -31,14 +31,15 @@ WHERE (m.conversation_id = c._id)
SMS_MMSSMS_PATH = "data/data/com.android.providers.telephony/databases/mmssms.db" SMS_MMSSMS_PATH = "data/data/com.android.providers.telephony/databases/mmssms.db"
SMS_MMSMS_QUERY = """ SMS_MMSMS_QUERY = """
SELECT SELECT
address AS number, address AS number,
date_sent AS timestamp, date_sent AS timestamp,
type as incoming, type as incoming,
body AS text body AS text
FROM sms; FROM sms;
""" """
class SMS(AndroidExtraction): class SMS(AndroidExtraction):
"""This module extracts all SMS messages containing links.""" """This module extracts all SMS messages containing links."""
@ -62,7 +63,7 @@ class SMS(AndroidExtraction):
return return
for message in self.results: for message in self.results:
if not "text" in message: if "text" not in message:
continue continue
message_links = check_for_links(message["text"]) message_links = check_for_links(message["text"])
@ -77,7 +78,7 @@ class SMS(AndroidExtraction):
""" """
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cur = conn.cursor() cur = conn.cursor()
if (self.SMS_DB_TYPE == 1): if (self.SMS_DB_TYPE == 1):
cur.execute(SMS_BUGLE_QUERY) cur.execute(SMS_BUGLE_QUERY)
elif (self.SMS_DB_TYPE == 2): elif (self.SMS_DB_TYPE == 2):

View File

@ -16,6 +16,7 @@ log = logging.getLogger(__name__)
WHATSAPP_PATH = "data/data/com.whatsapp/databases/msgstore.db" 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."""
@ -39,7 +40,7 @@ class Whatsapp(AndroidExtraction):
return return
for message in self.results: for message in self.results:
if not "data" in message: if "data" not in message:
continue continue
message_links = check_for_links(message["data"]) message_links = check_for_links(message["data"])

View File

@ -5,4 +5,4 @@
from .sms import SMS from .sms import SMS
BACKUP_MODULES = [SMS,] BACKUP_MODULES = [SMS]

View File

@ -24,7 +24,7 @@ class SMS(MVTModule):
return return
for message in self.results: for message in self.results:
if not "body" in message: if "body" not in message:
continue continue
message_links = check_for_links(message["body"]) message_links = check_for_links(message["body"])

View File

@ -12,6 +12,7 @@ from .url import URL
class IndicatorsFileBadFormat(Exception): class IndicatorsFileBadFormat(Exception):
pass pass
class Indicators: class Indicators:
"""This class is used to parse indicators from a STIX2 file and provide """This class is used to parse indicators from a STIX2 file and provide
functions to compare extracted artifacts to the indicators. functions to compare extracted artifacts to the indicators.
@ -115,7 +116,7 @@ class Indicators:
else: else:
# If it's not shortened, we just use the original URL object. # If it's not shortened, we just use the original URL object.
final_url = orig_url final_url = orig_url
except Exception as e: except Exception:
# If URL parsing failed, we just try to do a simple substring # If URL parsing failed, we just try to do a simple substring
# match. # match.
for ioc in self.ioc_domains: for ioc in self.ioc_domains:

View File

@ -16,7 +16,7 @@ def logo():
try: try:
latest_version = check_for_updates() latest_version = check_for_updates()
except: except Exception:
pass pass
else: else:
if latest_version: if latest_version:

View File

@ -14,12 +14,15 @@ import simplejson as json
class DatabaseNotFoundError(Exception): class DatabaseNotFoundError(Exception):
pass pass
class DatabaseCorruptedError(Exception): class DatabaseCorruptedError(Exception):
pass pass
class InsufficientPrivileges(Exception): class InsufficientPrivileges(Exception):
pass pass
class MVTModule(object): class MVTModule(object):
"""This class provides a base for all extraction modules.""" """This class provides a base for all extraction modules."""

View File

@ -250,6 +250,7 @@ SHORTENER_DOMAINS = [
"zz.gd", "zz.gd",
] ]
class URL: class URL:
def __init__(self, url): def __init__(self, url):
@ -273,7 +274,7 @@ 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: except Exception:
return None return None
def get_top_level(self): def get_top_level(self):
@ -288,7 +289,7 @@ class URL:
# TODO: Properly handle exception. # TODO: Properly handle exception.
try: try:
return get_tld(self.url, as_object=True, fix_protocol=True).fld.lower() return get_tld(self.url, as_object=True, fix_protocol=True).fld.lower()
except: except Exception:
return None return None
def check_if_shortened(self) -> bool: def check_if_shortened(self) -> bool:

View File

@ -45,7 +45,7 @@ def convert_chrometime_to_unix(timestamp):
:returns: Unix epoch timestamp. :returns: Unix epoch timestamp.
""" """
epoch_start = datetime.datetime(1601, 1 , 1) epoch_start = datetime.datetime(1601, 1, 1)
delta = datetime.timedelta(microseconds=timestamp) delta = datetime.timedelta(microseconds=timestamp)
return epoch_start + delta return epoch_start + delta
@ -64,6 +64,7 @@ def convert_timestamp_to_iso(timestamp):
except Exception: except Exception:
return None return None
def check_for_links(text): def check_for_links(text):
"""Checks if a given text contains HTTP links. """Checks if a given text contains HTTP links.
@ -74,6 +75,7 @@ def check_for_links(text):
""" """
return re.findall("(?P<url>https?://[^\s]+)", text, re.IGNORECASE) return re.findall("(?P<url>https?://[^\s]+)", text, re.IGNORECASE)
def get_sha256_from_file_path(file_path): def get_sha256_from_file_path(file_path):
"""Calculate the SHA256 hash of a file from a file path. """Calculate the SHA256 hash of a file from a file path.
@ -88,6 +90,7 @@ def get_sha256_from_file_path(file_path):
return sha256_hash.hexdigest() return sha256_hash.hexdigest()
# Note: taken from here: # Note: taken from here:
# https://stackoverflow.com/questions/57014259/json-dumps-on-dictionary-with-bytes-for-keys # https://stackoverflow.com/questions/57014259/json-dumps-on-dictionary-with-bytes-for-keys
def keys_bytes_to_string(obj): def keys_bytes_to_string(obj):

View File

@ -8,6 +8,7 @@ from packaging import version
MVT_VERSION = "1.2.14" MVT_VERSION = "1.2.14"
def check_for_updates(): def check_for_updates():
res = requests.get("https://pypi.org/pypi/mvt/json") res = requests.get("https://pypi.org/pypi/mvt/json")
data = res.json() data = res.json()

View File

@ -10,7 +10,9 @@ import click
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.prompt import Prompt from rich.prompt import Prompt
from mvt.common.help import * from mvt.common.help import HELP_MSG_MODULE, HELP_MSG_IOC
from mvt.common.help import HELP_MSG_FAST, HELP_MSG_OUTPUT
from mvt.common.help import HELP_MSG_LIST_MODULES
from mvt.common.indicators import Indicators, IndicatorsFileBadFormat from mvt.common.indicators import Indicators, IndicatorsFileBadFormat
from mvt.common.logo import logo from mvt.common.logo import logo
from mvt.common.module import run_module, save_timeline from mvt.common.module import run_module, save_timeline
@ -30,6 +32,7 @@ log = logging.getLogger(__name__)
# Set this environment variable to a password if needed. # Set this environment variable to a password if needed.
PASSWD_ENV = "MVT_IOS_BACKUP_PASSWORD" PASSWD_ENV = "MVT_IOS_BACKUP_PASSWORD"
#============================================================================== #==============================================================================
# Main # Main
#============================================================================== #==============================================================================

View File

@ -14,6 +14,7 @@ from iOSbackup import iOSbackup
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class DecryptBackup: class DecryptBackup:
"""This class provides functions to decrypt an encrypted iTunes backup """This class provides functions to decrypt an encrypted iTunes backup
using either a password or a key file. using either a password or a key file.

View File

@ -10,6 +10,7 @@ from ..base import IOSExtraction
CONF_PROFILES_DOMAIN = "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles" CONF_PROFILES_DOMAIN = "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles"
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."""

View File

@ -28,8 +28,8 @@ class Manifest(IOSExtraction):
"""Unserialized plist objects can have keys which are str or byte types """Unserialized plist objects can have keys which are str or byte types
This is a helper to try fetch a key as both a byte or string type. This is a helper to try fetch a key as both a byte or string type.
:param dictionary: param key: :param dictionary:
:param key: :param key:
""" """
return dictionary.get(key.encode("utf-8"), None) or dictionary.get(key, None) return dictionary.get(key.encode("utf-8"), None) or dictionary.get(key, None)
@ -38,7 +38,7 @@ class Manifest(IOSExtraction):
def _convert_timestamp(timestamp_or_unix_time_int): def _convert_timestamp(timestamp_or_unix_time_int):
"""Older iOS versions stored the manifest times as unix timestamps. """Older iOS versions stored the manifest times as unix timestamps.
:param timestamp_or_unix_time_int: :param timestamp_or_unix_time_int:
""" """
if isinstance(timestamp_or_unix_time_int, datetime.datetime): if isinstance(timestamp_or_unix_time_int, datetime.datetime):
@ -72,7 +72,7 @@ class Manifest(IOSExtraction):
return return
for result in self.results: for result in self.results:
if not "relative_path" in result: if "relative_path" not in result:
continue continue
if not result["relative_path"]: if not result["relative_path"]:
continue continue
@ -133,7 +133,7 @@ class Manifest(IOSExtraction):
"owner": self._get_key(file_metadata, "UserID"), "owner": self._get_key(file_metadata, "UserID"),
"size": self._get_key(file_metadata, "Size"), "size": self._get_key(file_metadata, "Size"),
}) })
except: except Exception:
self.log.exception("Error reading manifest file metadata for file with ID %s and relative path %s", self.log.exception("Error reading manifest file metadata for file with ID %s and relative path %s",
file_data["fileID"], file_data["relativePath"]) file_data["fileID"], file_data["relativePath"])
pass pass

View File

@ -11,6 +11,7 @@ from ..base import IOSExtraction
CONF_PROFILES_EVENTS_RELPATH = "Library/ConfigurationProfiles/MCProfileEvents.plist" CONF_PROFILES_EVENTS_RELPATH = "Library/ConfigurationProfiles/MCProfileEvents.plist"
class ProfileEvents(IOSExtraction): class ProfileEvents(IOSExtraction):
"""This module extracts events related to the installation of configuration """This module extracts events related to the installation of configuration
profiles. profiles.

View File

@ -16,4 +16,4 @@ from .webkit_safariviewservice import WebkitSafariViewService
FS_MODULES = [CacheFiles, Filesystem, Netusage, Analytics, SafariFavicon, ShutdownLog, FS_MODULES = [CacheFiles, Filesystem, Netusage, Analytics, SafariFavicon, ShutdownLog,
IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage,
WebkitSafariViewService,] WebkitSafariViewService]

View File

@ -14,6 +14,7 @@ ANALYTICS_DB_PATH = [
"private/var/Keychains/Analytics/*.db", "private/var/Keychains/Analytics/*.db",
] ]
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."""
@ -30,7 +31,7 @@ class Analytics(IOSExtraction):
"event": record["artifact"], "event": record["artifact"],
"data": f"{record}", "data": f"{record}",
} }
def check_indicators(self): def check_indicators(self):
if not self.indicators: if not self.indicators:
return return
@ -50,7 +51,7 @@ class Analytics(IOSExtraction):
ioc, result["artifact"], result["timestamp"]) ioc, result["artifact"], result["timestamp"])
self.detected.append(result) self.detected.append(result)
break break
def _extract_analytics_data(self): def _extract_analytics_data(self):
artifact = self.file_path.split("/")[-1] artifact = self.file_path.split("/")[-1]
@ -87,7 +88,6 @@ class Analytics(IOSExtraction):
FROM soft_failures; FROM soft_failures;
""") """)
for row in cur: for row in cur:
if row[0] and row[1]: if row[0] and row[1]:
timestamp = convert_timestamp_to_iso(convert_mactime_to_unix(row[0], False)) timestamp = convert_timestamp_to_iso(convert_mactime_to_unix(row[0], False))

View File

@ -38,7 +38,7 @@ class CacheFiles(IOSExtraction):
for item in items: for item in items:
if self.indicators.check_domain(item["url"]): if self.indicators.check_domain(item["url"]):
if key not in self.detected: if key not in self.detected:
self.detected[key] = [item,] self.detected[key] = [item, ]
else: else:
self.detected[key].append(item) self.detected[key].append(item)
@ -54,7 +54,7 @@ class CacheFiles(IOSExtraction):
return return
key_name = os.path.relpath(file_path, self.base_folder) key_name = os.path.relpath(file_path, self.base_folder)
if not key_name in self.results: if key_name not in self.results:
self.results[key_name] = [] self.results[key_name] = []
for row in cur: for row in cur:

View File

@ -60,7 +60,7 @@ class Filesystem(IOSExtraction):
"path": os.path.relpath(dir_path, self.base_folder), "path": os.path.relpath(dir_path, self.base_folder),
"modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(dir_path).st_mtime)), "modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(dir_path).st_mtime)),
} }
except: except Exception:
continue continue
else: else:
self.results.append(result) self.results.append(result)
@ -72,7 +72,7 @@ class Filesystem(IOSExtraction):
"path": os.path.relpath(file_path, self.base_folder), "path": os.path.relpath(file_path, self.base_folder),
"modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(file_path).st_mtime)), "modified": convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(os.stat(file_path).st_mtime)),
} }
except: except Exception:
continue continue
else: else:
self.results.append(result) self.results.append(result)

View File

@ -12,6 +12,7 @@ NETUSAGE_ROOT_PATHS = [
"private/var/networkd/db/netusage.sqlite" "private/var/networkd/db/netusage.sqlite"
] ]
class Netusage(NetBase): class Netusage(NetBase):
"""This class extracts data from netusage.sqlite and attempts to identify """This class extracts data from netusage.sqlite and attempts to identify
any suspicious processes if running on a full filesystem dump. any suspicious processes if running on a full filesystem dump.

View File

@ -14,6 +14,7 @@ SAFARI_FAVICON_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Image Cache/Favicons/Favicons.db", "private/var/mobile/Containers/Data/Application/*/Library/Image Cache/Favicons/Favicons.db",
] ]
class SafariFavicon(IOSExtraction): class SafariFavicon(IOSExtraction):
"""This module extracts all Safari favicon records.""" """This module extracts all Safari favicon records."""

View File

@ -11,6 +11,7 @@ SHUTDOWN_LOG_PATH = [
"private/var/db/diagnostics/shutdown.log", "private/var/db/diagnostics/shutdown.log",
] ]
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."""
@ -27,7 +28,7 @@ class ShutdownLog(IOSExtraction):
"event": "shutdown", "event": "shutdown",
"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):
if not self.indicators: if not self.indicators:
return return
@ -57,7 +58,7 @@ class ShutdownLog(IOSExtraction):
try: try:
start = line.find(" @")+2 start = line.find(" @")+2
mac_timestamp = int(line[start:start+10]) mac_timestamp = int(line[start:start+10])
except: except Exception:
mac_timestamp = 0 mac_timestamp = 0
timestamp = convert_mactime_to_unix(mac_timestamp, from_2001=False) timestamp = convert_mactime_to_unix(mac_timestamp, from_2001=False)

View File

@ -14,6 +14,7 @@ IOS_ANALYTICS_JOURNAL_PATHS = [
"private/var/db/analyticsd/Analytics-Journal-*.ips", "private/var/db/analyticsd/Analytics-Journal-*.ips",
] ]
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."""

View File

@ -9,6 +9,7 @@ WEBKIT_INDEXEDDB_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/IndexedDB", "private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/IndexedDB",
] ]
class WebkitIndexedDB(WebkitBase): class WebkitIndexedDB(WebkitBase):
"""This module looks extracts records from WebKit IndexedDB folders, """This module looks extracts records from WebKit IndexedDB folders,
and checks them against any provided list of suspicious domains. and checks them against any provided list of suspicious domains.

View File

@ -9,6 +9,7 @@ WEBKIT_LOCALSTORAGE_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/LocalStorage/", "private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/LocalStorage/",
] ]
class WebkitLocalStorage(WebkitBase): class WebkitLocalStorage(WebkitBase):
"""This module looks extracts records from WebKit LocalStorage folders, """This module looks extracts records from WebKit LocalStorage folders,
and checks them against any provided list of suspicious domains. and checks them against any provided list of suspicious domains.

View File

@ -9,6 +9,7 @@ WEBKIT_SAFARIVIEWSERVICE_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/", "private/var/mobile/Containers/Data/Application/*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/",
] ]
class WebkitSafariViewService(WebkitBase): class WebkitSafariViewService(WebkitBase):
"""This module looks extracts records from WebKit LocalStorage folders, """This module looks extracts records from WebKit LocalStorage folders,
and checks them against any provided list of suspicious domains. and checks them against any provided list of suspicious domains.

View File

@ -27,4 +27,4 @@ MIXED_MODULES = [Calls, ChromeFavicon, ChromeHistory, Contacts, FirefoxFavicon,
FirefoxHistory, IDStatusCache, InteractionC, LocationdClients, FirefoxHistory, IDStatusCache, InteractionC, LocationdClients,
OSAnalyticsADDaily, Datausage, SafariBrowserState, SafariHistory, OSAnalyticsADDaily, Datausage, SafariBrowserState, SafariHistory,
TCC, SMS, SMSAttachments, WebkitResourceLoadStatistics, TCC, SMS, SMSAttachments, WebkitResourceLoadStatistics,
WebkitSessionResourceLog, Whatsapp,] WebkitSessionResourceLog, Whatsapp]

View File

@ -16,6 +16,7 @@ CALLS_ROOT_PATHS = [
"private/var/mobile/Library/CallHistoryDB/CallHistory.storedata" "private/var/mobile/Library/CallHistoryDB/CallHistory.storedata"
] ]
class Calls(IOSExtraction): class Calls(IOSExtraction):
"""This module extracts phone calls details""" """This module extracts phone calls details"""
@ -45,7 +46,7 @@ class Calls(IOSExtraction):
ZDATE, ZDURATION, ZLOCATION, ZADDRESS, ZSERVICE_PROVIDER ZDATE, ZDURATION, ZLOCATION, ZADDRESS, ZSERVICE_PROVIDER
FROM ZCALLRECORD; FROM ZCALLRECORD;
""") """)
names = [description[0] for description in cur.description] # names = [description[0] for description in cur.description]
for row in cur: for row in cur:
self.results.append({ self.results.append({

View File

@ -19,6 +19,7 @@ CHROME_FAVICON_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/Favicons", "private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/Favicons",
] ]
class ChromeFavicon(IOSExtraction): class ChromeFavicon(IOSExtraction):
"""This module extracts all Chrome favicon records.""" """This module extracts all Chrome favicon records."""

View File

@ -13,12 +13,12 @@ from ..base import IOSExtraction
CHROME_HISTORY_BACKUP_IDS = [ CHROME_HISTORY_BACKUP_IDS = [
"faf971ce92c3ac508c018dce1bef2a8b8e9838f1", "faf971ce92c3ac508c018dce1bef2a8b8e9838f1",
] ]
# TODO: Confirm Chrome database path. # TODO: Confirm Chrome database path.
CHROME_HISTORY_ROOT_PATHS = [ CHROME_HISTORY_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/History", "private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/History",
] ]
class ChromeHistory(IOSExtraction): class ChromeHistory(IOSExtraction):
"""This module extracts all Chome visits.""" """This module extracts all Chome visits."""

View File

@ -14,6 +14,7 @@ CONTACTS_ROOT_PATHS = [
"private/var/mobile/Library/AddressBook/AddressBook.sqlitedb", "private/var/mobile/Library/AddressBook/AddressBook.sqlitedb",
] ]
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."""

View File

@ -17,6 +17,7 @@ FIREFOX_HISTORY_ROOT_PATHS = [
"private/var/mobile/profile.profile/browser.db", "private/var/mobile/profile.profile/browser.db",
] ]
class FirefoxFavicon(IOSExtraction): class FirefoxFavicon(IOSExtraction):
"""This module extracts all Firefox favicon""" """This module extracts all Firefox favicon"""
@ -39,8 +40,8 @@ class FirefoxFavicon(IOSExtraction):
return return
for result in self.results: for result in self.results:
if (self.indicators.check_domain(result.get("url", "")) or if (self.indicators.check_domain(result.get("url", "")) or
self.indicators.check_domain(result.get("history_url", ""))): self.indicators.check_domain(result.get("history_url", ""))):
self.detected.append(result) self.detected.append(result)
def run(self): def run(self):

View File

@ -17,6 +17,7 @@ FIREFOX_HISTORY_ROOT_PATHS = [
"private/var/mobile/profile.profile/browser.db", "private/var/mobile/profile.profile/browser.db",
] ]
class FirefoxHistory(IOSExtraction): class FirefoxHistory(IOSExtraction):
"""This module extracts all Firefox visits and tries to detect potential """This module extracts all Firefox visits and tries to detect potential
network injection attacks. network injection attacks.

View File

@ -18,6 +18,7 @@ IDSTATUSCACHE_ROOT_PATHS = [
"private/var/mobile/Library/IdentityServices/idstatuscache.plist", "private/var/mobile/Library/IdentityServices/idstatuscache.plist",
] ]
class IDStatusCache(IOSExtraction): class IDStatusCache(IOSExtraction):
"""Extracts Apple Authentication information from idstatuscache.plist""" """Extracts Apple Authentication information from idstatuscache.plist"""
@ -91,5 +92,5 @@ class IDStatusCache(IOSExtraction):
self.file_path = idstatuscache_path self.file_path = idstatuscache_path
self.log.info("Found IDStatusCache plist at path: %s", self.file_path) self.log.info("Found IDStatusCache plist at path: %s", self.file_path)
self._extract_idstatuscache_entries(self.file_path) self._extract_idstatuscache_entries(self.file_path)
self.log.info("Extracted a total of %d ID Status Cache entries", len(self.results)) self.log.info("Extracted a total of %d ID Status Cache entries", len(self.results))

View File

@ -16,6 +16,7 @@ INTERACTIONC_ROOT_PATHS = [
"private/var/mobile/Library/CoreDuet/People/interactionC.db", "private/var/mobile/Library/CoreDuet/People/interactionC.db",
] ]
class InteractionC(IOSExtraction): class InteractionC(IOSExtraction):
"""This module extracts data from InteractionC db.""" """This module extracts data from InteractionC db."""
@ -54,8 +55,8 @@ class InteractionC(IOSExtraction):
"timestamp": record[ts], "timestamp": record[ts],
"module": self.__class__.__name__, "module": self.__class__.__name__,
"event": ts, "event": ts,
"data": f"[{record['bundle_id']}] {record['account']} - from {record['sender_display_name']} " \ "data": f"[{record['bundle_id']}] {record['account']} - from {record['sender_display_name']} "
f"({record['sender_identifier']}) to {record['recipient_display_name']} " \ f"({record['sender_identifier']}) to {record['recipient_display_name']} "
f"({record['recipient_identifier']}): {record['content']}" f"({record['recipient_identifier']}): {record['content']}"
}) })
processed.append(record[ts]) processed.append(record[ts])
@ -123,8 +124,7 @@ class InteractionC(IOSExtraction):
LEFT JOIN Z_2INTERACTIONRECIPIENT ON ZINTERACTIONS.Z_PK== Z_2INTERACTIONRECIPIENT.Z_3INTERACTIONRECIPIENT LEFT JOIN Z_2INTERACTIONRECIPIENT ON ZINTERACTIONS.Z_PK== Z_2INTERACTIONRECIPIENT.Z_3INTERACTIONRECIPIENT
LEFT JOIN ZCONTACTS RECEIPIENTCONACT ON Z_2INTERACTIONRECIPIENT.Z_2RECIPIENTS== RECEIPIENTCONACT.Z_PK; LEFT JOIN ZCONTACTS RECEIPIENTCONACT ON Z_2INTERACTIONRECIPIENT.Z_2RECIPIENTS== RECEIPIENTCONACT.Z_PK;
""") """)
# names = [description[0] for description in cur.description]
names = [description[0] for description in cur.description]
for row in cur: for row in cur:
self.results.append({ self.results.append({

View File

@ -17,6 +17,7 @@ LOCATIOND_ROOT_PATHS = [
"private/var/root/Library/Caches/locationd/clients.plist" "private/var/root/Library/Caches/locationd/clients.plist"
] ]
class LocationdClients(IOSExtraction): class LocationdClients(IOSExtraction):
"""Extract information from apps who used geolocation.""" """Extract information from apps who used geolocation."""

View File

@ -12,6 +12,7 @@ DATAUSAGE_ROOT_PATHS = [
"private/var/wireless/Library/Databases/DataUsage.sqlite", "private/var/wireless/Library/Databases/DataUsage.sqlite",
] ]
class Datausage(NetBase): class Datausage(NetBase):
"""This class extracts data from DataUsage.sqlite and attempts to identify """This class extracts data from DataUsage.sqlite and attempts to identify
any suspicious processes if running on a full filesystem dump. any suspicious processes if running on a full filesystem dump.

View File

@ -16,6 +16,7 @@ OSANALYTICS_ADDAILY_ROOT_PATHS = [
"private/var/mobile/Library/Preferences/com.apple.osanalytics.addaily.plist", "private/var/mobile/Library/Preferences/com.apple.osanalytics.addaily.plist",
] ]
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"""
@ -34,14 +35,14 @@ class OSAnalyticsADDaily(IOSExtraction):
"event": "osanalytics_addaily", "event": "osanalytics_addaily",
"data": record_data, "data": record_data,
} }
def check_indicators(self): def check_indicators(self):
if not self.indicators: if not self.indicators:
return return
for result in self.results: for result in self.results:
if self.indicators.check_process(result["package"]): if self.indicators.check_process(result["package"]):
self.detected.append(result) self.detected.append(result)
def run(self): def run(self):
self._find_ios_database(backup_ids=OSANALYTICS_ADDAILY_BACKUP_IDS, self._find_ios_database(backup_ids=OSANALYTICS_ADDAILY_BACKUP_IDS,

View File

@ -19,6 +19,7 @@ SAFARI_BROWSER_STATE_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Safari/BrowserState.db", "private/var/mobile/Containers/Data/Application/*/Library/Safari/BrowserState.db",
] ]
class SafariBrowserState(IOSExtraction): class SafariBrowserState(IOSExtraction):
"""This module extracts all Safari browser state records.""" """This module extracts all Safari browser state records."""
@ -47,7 +48,7 @@ class SafariBrowserState(IOSExtraction):
self.detected.append(result) self.detected.append(result)
continue continue
if not "session_data" in result: if "session_data" not in result:
continue continue
for session_entry in result["session_data"]: for session_entry in result["session_data"]:

View File

@ -17,6 +17,7 @@ SAFARI_HISTORY_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Safari/History.db", "private/var/mobile/Containers/Data/Application/*/Library/Safari/History.db",
] ]
class SafariHistory(IOSExtraction): class SafariHistory(IOSExtraction):
"""This module extracts all Safari visits and tries to detect potential """This module extracts all Safari visits and tries to detect potential
network injection attacks. network injection attacks.
@ -62,7 +63,7 @@ class SafariHistory(IOSExtraction):
continue continue
self.log.info("Found HTTP redirect to different domain: \"%s\" -> \"%s\"", self.log.info("Found HTTP redirect to different domain: \"%s\" -> \"%s\"",
origin_domain, redirect_domain) origin_domain, redirect_domain)
redirect_time = convert_mactime_to_unix(redirect["timestamp"]) redirect_time = convert_mactime_to_unix(redirect["timestamp"])
origin_time = convert_mactime_to_unix(result["timestamp"]) origin_time = convert_mactime_to_unix(result["timestamp"])

View File

@ -18,6 +18,7 @@ SMS_ROOT_PATHS = [
"private/var/mobile/Library/SMS/sms.db", "private/var/mobile/Library/SMS/sms.db",
] ]
class SMS(IOSExtraction): class SMS(IOSExtraction):
"""This module extracts all SMS messages containing links.""" """This module extracts all SMS messages containing links."""
@ -67,8 +68,8 @@ class SMS(IOSExtraction):
# We base64 escape some of the attributes that could contain # We base64 escape some of the attributes that could contain
# binary data. # binary data.
if (names[index] == "attributedBody" or if (names[index] == "attributedBody" or
names[index] == "payload_data" or names[index] == "payload_data" or
names[index] == "message_summary_info") and value: names[index] == "message_summary_info") and value:
value = b64encode(value).decode() value = b64encode(value).decode()
# We store the value of each column under the proper key. # We store the value of each column under the proper key.

View File

@ -17,6 +17,7 @@ SMS_ROOT_PATHS = [
"private/var/mobile/Library/SMS/sms.db", "private/var/mobile/Library/SMS/sms.db",
] ]
class SMSAttachments(IOSExtraction): class SMSAttachments(IOSExtraction):
"""This module extracts all info about SMS/iMessage attachments.""" """This module extracts all info about SMS/iMessage attachments."""
@ -45,7 +46,7 @@ class SMSAttachments(IOSExtraction):
cur.execute(""" cur.execute("""
SELECT SELECT
attachment.ROWID as "attachment_id", attachment.ROWID as "attachment_id",
attachment.*, attachment.*,
message.service as "service", message.service as "service",
handle.id as "phone_number" handle.id as "phone_number"
FROM attachment FROM attachment
@ -73,7 +74,7 @@ class SMSAttachments(IOSExtraction):
attachment["filename"] = attachment["filename"] or "NULL" attachment["filename"] = attachment["filename"] or "NULL"
if (attachment["filename"].startswith("/var/tmp/") and attachment["filename"].endswith("-1") and if (attachment["filename"].startswith("/var/tmp/") and attachment["filename"].endswith("-1") and
attachment["direction"] == "received"): attachment["direction"] == "received"):
self.log.warn(f"Suspicious iMessage attachment '{attachment['filename']}' on {attachment['isodate']}") self.log.warn(f"Suspicious iMessage attachment '{attachment['filename']}' on {attachment['isodate']}")
self.detected.append(attachment) self.detected.append(attachment)

View File

@ -38,6 +38,7 @@ AUTH_REASONS = {
12: "app_type_policy", 12: "app_type_policy",
} }
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."""
@ -50,7 +51,7 @@ class TCC(IOSExtraction):
def process_db(self, file_path): def process_db(self, file_path):
conn = sqlite3.connect(file_path) conn = sqlite3.connect(file_path)
cur = conn.cursor() cur = conn.cursor()
cur.execute("""SELECT cur.execute("""SELECT
service, client, client_type, auth_value, auth_reason, last_modified service, client, client_type, auth_value, auth_reason, last_modified
FROM access;""") FROM access;""")

View File

@ -17,6 +17,7 @@ WEBKIT_RESOURCELOADSTATICS_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/observations.db", "private/var/mobile/Containers/Data/Application/*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/observations.db",
] ]
class WebkitResourceLoadStatistics(IOSExtraction): 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().
@ -38,7 +39,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
for item in items: for item in items:
if self.indicators.check_domain(item["registrable_domain"]): if self.indicators.check_domain(item["registrable_domain"]):
if key not in self.detected: if key not in self.detected:
self.detected[key] = [item,] self.detected[key] = [item, ]
else: else:
self.detected[key].append(item) self.detected[key].append(item)
@ -55,7 +56,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
except sqlite3.OperationalError: except sqlite3.OperationalError:
return return
if not key in self.results: if key not in self.results:
self.results[key] = [] self.results[key] = []
for row in cur: for row in cur:

View File

@ -20,6 +20,7 @@ WEBKIT_SESSION_RESOURCE_LOG_ROOT_PATHS = [
"private/var/mobile/Library/WebClips/*/Storage/full_browsing_session_resourceLog.plist", "private/var/mobile/Library/WebClips/*/Storage/full_browsing_session_resourceLog.plist",
] ]
class WebkitSessionResourceLog(IOSExtraction): class WebkitSessionResourceLog(IOSExtraction):
"""This module extracts records from WebKit browsing session """This module extracts records from WebKit browsing session
resource logs, and checks them against any provided list of resource logs, and checks them against any provided list of

View File

@ -20,6 +20,7 @@ WHATSAPP_ROOT_PATHS = [
"private/var/mobile/Containers/Shared/AppGroup/*/ChatStorage.sqlite", "private/var/mobile/Containers/Shared/AppGroup/*/ChatStorage.sqlite",
] ]
class Whatsapp(IOSExtraction): class Whatsapp(IOSExtraction):
"""This module extracts all WhatsApp messages containing links.""" """This module extracts all WhatsApp messages containing links."""

View File

@ -81,7 +81,7 @@ class NetBase(IOSExtraction):
def serialize(self, record): def serialize(self, record):
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']}"
records = [{ records = [{
"timestamp": record["live_isodate"], "timestamp": record["live_isodate"],

View File

@ -233,11 +233,13 @@ IPHONE_IOS_VERSIONS = [
{"build": "19B74", "version": "15.1"}, {"build": "19B74", "version": "15.1"},
] ]
def get_device_desc_from_id(identifier, devices_list=IPHONE_MODELS): def get_device_desc_from_id(identifier, devices_list=IPHONE_MODELS):
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):
build = build.upper() build = build.upper()
for version in IPHONE_IOS_VERSIONS: for version in IPHONE_IOS_VERSIONS:

View File

@ -30,6 +30,7 @@ requires = (
"libusb1>=2.0.1", "libusb1>=2.0.1",
) )
def get_package_data(package): def get_package_data(package):
walk = [(dirpath.replace(package + os.sep, "", 1), filenames) walk = [(dirpath.replace(package + os.sep, "", 1), filenames)
for dirpath, dirnames, filenames in os.walk(package) for dirpath, dirnames, filenames in os.walk(package)
@ -41,6 +42,7 @@ def get_package_data(package):
for filename in filenames]) for filename in filenames])
return {package: filepaths} return {package: filepaths}
setup( setup(
name="mvt", name="mvt",
version=MVT_VERSION, version=MVT_VERSION,