mirror of
https://github.com/mvt-project/mvt.git
synced 2024-06-30 16:18:54 +00:00
Starting to enforce line lengths on mvt-ios
This commit is contained in:
parent
424b86a261
commit
0f503f72b5
|
@ -58,11 +58,13 @@ def version():
|
||||||
@click.option("--destination", "-d", required=True,
|
@click.option("--destination", "-d", required=True,
|
||||||
help="Path to the folder where to store the decrypted backup")
|
help="Path to the folder where to store the decrypted backup")
|
||||||
@click.option("--password", "-p", cls=MutuallyExclusiveOption,
|
@click.option("--password", "-p", cls=MutuallyExclusiveOption,
|
||||||
help=f"Password to use to decrypt the backup (or, set {MVT_IOS_BACKUP_PASSWORD} environment variable)",
|
help="Password to use to decrypt the backup (or, set "
|
||||||
|
f"{MVT_IOS_BACKUP_PASSWORD} environment variable)",
|
||||||
mutually_exclusive=["key_file"])
|
mutually_exclusive=["key_file"])
|
||||||
@click.option("--key-file", "-k", cls=MutuallyExclusiveOption,
|
@click.option("--key-file", "-k", cls=MutuallyExclusiveOption,
|
||||||
type=click.Path(exists=True),
|
type=click.Path(exists=True),
|
||||||
help="File containing raw encryption key to use to decrypt the backup",
|
help="File containing raw encryption key to use to decrypt "
|
||||||
|
"the backup",
|
||||||
mutually_exclusive=["password"])
|
mutually_exclusive=["password"])
|
||||||
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
|
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
|
@ -71,20 +73,22 @@ def decrypt_backup(ctx, destination, password, key_file, backup_path):
|
||||||
|
|
||||||
if key_file:
|
if key_file:
|
||||||
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
||||||
log.info("Ignoring %s environment variable, using --key-file '%s' instead",
|
log.info("Ignoring %s environment variable, using --key-file"
|
||||||
MVT_IOS_BACKUP_PASSWORD, key_file)
|
"'%s' instead", MVT_IOS_BACKUP_PASSWORD, key_file)
|
||||||
|
|
||||||
backup.decrypt_with_key_file(key_file)
|
backup.decrypt_with_key_file(key_file)
|
||||||
elif password:
|
elif password:
|
||||||
log.info("Your password may be visible in the process table because it was supplied on the command line!")
|
log.info("Your password may be visible in the process table because it "
|
||||||
|
"was supplied on the command line!")
|
||||||
|
|
||||||
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
||||||
log.info("Ignoring %s environment variable, using --password argument instead",
|
log.info("Ignoring %s environment variable, using --password"
|
||||||
MVT_IOS_BACKUP_PASSWORD)
|
"argument instead", MVT_IOS_BACKUP_PASSWORD)
|
||||||
|
|
||||||
backup.decrypt_with_password(password)
|
backup.decrypt_with_password(password)
|
||||||
elif MVT_IOS_BACKUP_PASSWORD in os.environ:
|
elif MVT_IOS_BACKUP_PASSWORD in os.environ:
|
||||||
log.info("Using password from %s environment variable", MVT_IOS_BACKUP_PASSWORD)
|
log.info("Using password from %s environment variable",
|
||||||
|
MVT_IOS_BACKUP_PASSWORD)
|
||||||
backup.decrypt_with_password(os.environ[MVT_IOS_BACKUP_PASSWORD])
|
backup.decrypt_with_password(os.environ[MVT_IOS_BACKUP_PASSWORD])
|
||||||
else:
|
else:
|
||||||
sekrit = Prompt.ask("Enter backup password", password=True)
|
sekrit = Prompt.ask("Enter backup password", password=True)
|
||||||
|
@ -101,23 +105,27 @@ def decrypt_backup(ctx, destination, password, key_file, backup_path):
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@cli.command("extract-key", help="Extract decryption key from an iTunes backup")
|
@cli.command("extract-key", help="Extract decryption key from an iTunes backup")
|
||||||
@click.option("--password", "-p",
|
@click.option("--password", "-p",
|
||||||
help=f"Password to use to decrypt the backup (or, set {MVT_IOS_BACKUP_PASSWORD} environment variable)")
|
help="Password to use to decrypt the backup (or, set "
|
||||||
|
f"{MVT_IOS_BACKUP_PASSWORD} environment variable)")
|
||||||
@click.option("--key-file", "-k",
|
@click.option("--key-file", "-k",
|
||||||
help="Key file to be written (if unset, will print to STDOUT)",
|
help="Key file to be written (if unset, will print to STDOUT)",
|
||||||
required=False,
|
required=False,
|
||||||
type=click.Path(exists=False, file_okay=True, dir_okay=False, writable=True))
|
type=click.Path(exists=False, file_okay=True, dir_okay=False,
|
||||||
|
writable=True))
|
||||||
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
|
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
|
||||||
def extract_key(password, key_file, backup_path):
|
def extract_key(password, key_file, backup_path):
|
||||||
backup = DecryptBackup(backup_path)
|
backup = DecryptBackup(backup_path)
|
||||||
|
|
||||||
if password:
|
if password:
|
||||||
log.info("Your password may be visible in the process table because it was supplied on the command line!")
|
log.info("Your password may be visible in the process table because it "
|
||||||
|
"was supplied on the command line!")
|
||||||
|
|
||||||
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
if MVT_IOS_BACKUP_PASSWORD in os.environ:
|
||||||
log.info("Ignoring %s environment variable, using --password argument instead",
|
log.info("Ignoring %s environment variable, using --password "
|
||||||
MVT_IOS_BACKUP_PASSWORD)
|
"argument instead", MVT_IOS_BACKUP_PASSWORD)
|
||||||
elif MVT_IOS_BACKUP_PASSWORD in os.environ:
|
elif MVT_IOS_BACKUP_PASSWORD in os.environ:
|
||||||
log.info("Using password from %s environment variable", MVT_IOS_BACKUP_PASSWORD)
|
log.info("Using password from %s environment variable",
|
||||||
|
MVT_IOS_BACKUP_PASSWORD)
|
||||||
password = os.environ[MVT_IOS_BACKUP_PASSWORD]
|
password = os.environ[MVT_IOS_BACKUP_PASSWORD]
|
||||||
else:
|
else:
|
||||||
password = Prompt.ask("Enter backup password", password=True)
|
password = Prompt.ask("Enter backup password", password=True)
|
||||||
|
@ -135,7 +143,8 @@ def extract_key(password, key_file, backup_path):
|
||||||
@cli.command("check-backup", help="Extract artifacts from an iTunes backup")
|
@cli.command("check-backup", help="Extract artifacts from an iTunes backup")
|
||||||
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
|
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
|
||||||
default=[], help=HELP_MSG_IOC)
|
default=[], help=HELP_MSG_IOC)
|
||||||
@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_OUTPUT)
|
@click.option("--output", "-o", type=click.Path(exists=False),
|
||||||
|
help=HELP_MSG_OUTPUT)
|
||||||
@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST)
|
@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST)
|
||||||
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
|
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
|
||||||
@click.option("--module", "-m", help=HELP_MSG_MODULE)
|
@click.option("--module", "-m", help=HELP_MSG_MODULE)
|
||||||
|
@ -164,7 +173,8 @@ def check_backup(ctx, iocs, output, fast, list_modules, module, backup_path):
|
||||||
@cli.command("check-fs", help="Extract artifacts from a full filesystem dump")
|
@cli.command("check-fs", help="Extract artifacts from a full filesystem dump")
|
||||||
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
|
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
|
||||||
default=[], help=HELP_MSG_IOC)
|
default=[], help=HELP_MSG_IOC)
|
||||||
@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_OUTPUT)
|
@click.option("--output", "-o", type=click.Path(exists=False),
|
||||||
|
help=HELP_MSG_OUTPUT)
|
||||||
@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST)
|
@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST)
|
||||||
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
|
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
|
||||||
@click.option("--module", "-m", help=HELP_MSG_MODULE)
|
@click.option("--module", "-m", help=HELP_MSG_MODULE)
|
||||||
|
|
|
@ -59,7 +59,8 @@ class DecryptBackup:
|
||||||
self._backup.getFileDecryptedCopy(manifestEntry=item,
|
self._backup.getFileDecryptedCopy(manifestEntry=item,
|
||||||
targetName=file_id,
|
targetName=file_id,
|
||||||
targetFolder=item_folder)
|
targetFolder=item_folder)
|
||||||
log.info("Decrypted file %s [%s] to %s/%s", relative_path, domain, item_folder, file_id)
|
log.info("Decrypted file %s [%s] to %s/%s", relative_path, domain,
|
||||||
|
item_folder, file_id)
|
||||||
|
|
||||||
def process_backup(self) -> None:
|
def process_backup(self) -> None:
|
||||||
if not os.path.exists(self.dest_path):
|
if not os.path.exists(self.dest_path):
|
||||||
|
@ -79,8 +80,10 @@ class DecryptBackup:
|
||||||
relative_path = item["relativePath"]
|
relative_path = item["relativePath"]
|
||||||
domain = item["domain"]
|
domain = item["domain"]
|
||||||
|
|
||||||
# This may be a partial backup. Skip files from the manifest which do not exist locally.
|
# This may be a partial backup. Skip files from the manifest
|
||||||
source_file_path = os.path.join(self.backup_path, file_id[0:2], file_id)
|
# which do not exist locally.
|
||||||
|
source_file_path = os.path.join(self.backup_path, file_id[0:2],
|
||||||
|
file_id)
|
||||||
if not os.path.exists(source_file_path):
|
if not os.path.exists(source_file_path):
|
||||||
log.debug("Skipping file %s. File not found in encrypted backup directory.",
|
log.debug("Skipping file %s. File not found in encrypted backup directory.",
|
||||||
source_file_path)
|
source_file_path)
|
||||||
|
@ -128,7 +131,8 @@ class DecryptBackup:
|
||||||
self.backup_path, newpath)
|
self.backup_path, newpath)
|
||||||
self.backup_path = newpath
|
self.backup_path = newpath
|
||||||
elif len(possible) > 1:
|
elif len(possible) > 1:
|
||||||
log.critical("No Manifest.plist in %s, and %d Manifest.plist files in subdirs. Please choose one!",
|
log.critical("No Manifest.plist in %s, and %d Manifest.plist "
|
||||||
|
"files in subdirs. Please choose one!",
|
||||||
self.backup_path, len(possible))
|
self.backup_path, len(possible))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -143,12 +147,16 @@ class DecryptBackup:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, KeyError) and len(e.args) > 0 and e.args[0] == b"KEY":
|
if isinstance(e, KeyError) and len(e.args) > 0 and e.args[0] == b"KEY":
|
||||||
log.critical("Failed to decrypt backup. Password is probably wrong.")
|
log.critical("Failed to decrypt backup. Password is probably wrong.")
|
||||||
elif isinstance(e, FileNotFoundError) and os.path.basename(e.filename) == "Manifest.plist":
|
elif (isinstance(e, FileNotFoundError)
|
||||||
log.critical("Failed to find a valid backup at %s. Did you point to the right backup path?",
|
and os.path.basename(e.filename) == "Manifest.plist"):
|
||||||
|
log.critical("Failed to find a valid backup at %s. "
|
||||||
|
"Did you point to the right backup path?",
|
||||||
self.backup_path)
|
self.backup_path)
|
||||||
else:
|
else:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?")
|
log.critical("Failed to decrypt backup. Did you provide the "
|
||||||
|
"correct password? Did you point to the right "
|
||||||
|
"backup path?")
|
||||||
|
|
||||||
def decrypt_with_key_file(self, key_file: str) -> None:
|
def decrypt_with_key_file(self, key_file: str) -> None:
|
||||||
"""Decrypts an encrypted iOS backup using a key file.
|
"""Decrypts an encrypted iOS backup using a key file.
|
||||||
|
@ -168,7 +176,8 @@ class DecryptBackup:
|
||||||
|
|
||||||
# Key should be 64 hex encoded characters (32 raw bytes)
|
# Key should be 64 hex encoded characters (32 raw bytes)
|
||||||
if len(key_bytes) != 64:
|
if len(key_bytes) != 64:
|
||||||
log.critical("Invalid key from key file. Did you provide the correct key file?")
|
log.critical("Invalid key from key file. Did you provide the "
|
||||||
|
"correct key file?")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -178,7 +187,8 @@ class DecryptBackup:
|
||||||
backuproot=os.path.dirname(self.backup_path))
|
backuproot=os.path.dirname(self.backup_path))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
log.critical("Failed to decrypt backup. Did you provide the correct key file?")
|
log.critical("Failed to decrypt backup. Did you provide the "
|
||||||
|
"correct key file?")
|
||||||
|
|
||||||
def get_key(self) -> None:
|
def get_key(self) -> None:
|
||||||
"""Retrieve and prints the encryption key."""
|
"""Retrieve and prints the encryption key."""
|
||||||
|
@ -192,7 +202,8 @@ class DecryptBackup:
|
||||||
def write_key(self, key_path: str) -> None:
|
def write_key(self, key_path: str) -> None:
|
||||||
"""Save extracted key to file.
|
"""Save extracted key to file.
|
||||||
|
|
||||||
:param key_path: Path to the file where to write the derived decryption key.
|
:param key_path: Path to the file where to write the derived decryption
|
||||||
|
key.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self._decryption_key:
|
if not self._decryption_key:
|
||||||
|
@ -206,5 +217,6 @@ class DecryptBackup:
|
||||||
log.critical("Failed to write key to file: %s", key_path)
|
log.critical("Failed to write key to file: %s", key_path)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
log.info("Wrote decryption key to file: %s. This file is equivalent to a plaintext password. Keep it safe!",
|
log.info("Wrote decryption key to file: %s. This file is "
|
||||||
|
"equivalent to a plaintext password. Keep it safe!",
|
||||||
key_path)
|
key_path)
|
||||||
|
|
|
@ -15,7 +15,8 @@ from mvt.common.module import (DatabaseCorruptedError, DatabaseNotFoundError,
|
||||||
|
|
||||||
|
|
||||||
class IOSExtraction(MVTModule):
|
class IOSExtraction(MVTModule):
|
||||||
"""This class provides a base for all iOS filesystem/backup extraction modules."""
|
"""This class provides a base for all iOS filesystem/backup extraction
|
||||||
|
modules."""
|
||||||
|
|
||||||
def __init__(self, file_path: str = None, target_path: str = None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
results_path: str = None, fast_mode: bool = False,
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
@ -52,12 +53,17 @@ class IOSExtraction(MVTModule):
|
||||||
if not recover:
|
if not recover:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.info("Database at path %s is malformed. Trying to recover...", file_path)
|
self.log.info("Database at path %s is malformed. Trying to recover...",
|
||||||
|
file_path)
|
||||||
|
|
||||||
if not shutil.which("sqlite3"):
|
if not shutil.which("sqlite3"):
|
||||||
raise DatabaseCorruptedError("failed to recover without sqlite3 binary: please install sqlite3!")
|
raise DatabaseCorruptedError("failed to recover without sqlite3 "
|
||||||
|
"binary: please install sqlite3!")
|
||||||
if '"' in file_path:
|
if '"' in file_path:
|
||||||
raise DatabaseCorruptedError(f"database at path '{file_path}' is corrupted. unable to recover because it has a quotation mark (\") in its name")
|
raise DatabaseCorruptedError(f"database at path '{file_path}' is "
|
||||||
|
"corrupted. unable to recover because "
|
||||||
|
"it has a quotation mark (\") in its "
|
||||||
|
"name")
|
||||||
|
|
||||||
bak_path = f"{file_path}.bak"
|
bak_path = f"{file_path}.bak"
|
||||||
shutil.move(file_path, bak_path)
|
shutil.move(file_path, bak_path)
|
||||||
|
@ -72,8 +78,10 @@ class IOSExtraction(MVTModule):
|
||||||
def _get_backup_files_from_manifest(self, relative_path=None, domain=None):
|
def _get_backup_files_from_manifest(self, relative_path=None, domain=None):
|
||||||
"""Locate files from Manifest.db.
|
"""Locate files from Manifest.db.
|
||||||
|
|
||||||
:param relative_path: Relative path to use as filter from Manifest.db. (Default value = None)
|
:param relative_path: Relative path to use as filter from Manifest.db.
|
||||||
:param domain: Domain to use as filter from Manifest.db. (Default value = None)
|
(Default value = None)
|
||||||
|
:param domain: Domain to use as filter from Manifest.db.
|
||||||
|
(Default value = None)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
|
manifest_db_path = os.path.join(self.target_path, "Manifest.db")
|
||||||
|
@ -90,7 +98,8 @@ class IOSExtraction(MVTModule):
|
||||||
(relative_path, domain))
|
(relative_path, domain))
|
||||||
else:
|
else:
|
||||||
if relative_path:
|
if relative_path:
|
||||||
cur.execute(f"{base_sql} relativePath = ?;", (relative_path,))
|
cur.execute(f"{base_sql} relativePath = ?;",
|
||||||
|
(relative_path,))
|
||||||
elif domain:
|
elif domain:
|
||||||
cur.execute(f"{base_sql} domain = ?;", (domain,))
|
cur.execute(f"{base_sql} domain = ?;", (domain,))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -112,7 +121,8 @@ class IOSExtraction(MVTModule):
|
||||||
|
|
||||||
def _get_fs_files_from_patterns(self, root_paths):
|
def _get_fs_files_from_patterns(self, root_paths):
|
||||||
for root_path in root_paths:
|
for root_path in root_paths:
|
||||||
for found_path in glob.glob(os.path.join(self.target_path, root_path)):
|
for found_path in glob.glob(os.path.join(self.target_path,
|
||||||
|
root_path)):
|
||||||
if not os.path.exists(found_path):
|
if not os.path.exists(found_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -126,7 +136,8 @@ class IOSExtraction(MVTModule):
|
||||||
you should use the helper functions above.
|
you should use the helper functions above.
|
||||||
|
|
||||||
:param backup_id: iTunes backup database file's ID (or hash).
|
:param backup_id: iTunes backup database file's ID (or hash).
|
||||||
:param root_paths: Glob patterns for files to seek in filesystem dump. (Default value = [])
|
:param root_paths: Glob patterns for files to seek in filesystem dump.
|
||||||
|
(Default value = [])
|
||||||
:param backup_ids: Default value = None)
|
:param backup_ids: Default value = None)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -155,6 +166,7 @@ class IOSExtraction(MVTModule):
|
||||||
if file_path:
|
if file_path:
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
else:
|
else:
|
||||||
raise DatabaseNotFoundError("unable to find the module's database file")
|
raise DatabaseNotFoundError("unable to find the module's "
|
||||||
|
"database file")
|
||||||
|
|
||||||
self._recover_sqlite_db_if_needed(self.file_path)
|
self._recover_sqlite_db_if_needed(self.file_path)
|
||||||
|
|
|
@ -15,7 +15,8 @@ from .base import IOSExtraction
|
||||||
|
|
||||||
|
|
||||||
class NetBase(IOSExtraction):
|
class NetBase(IOSExtraction):
|
||||||
"""This class provides a base for DataUsage and NetUsage extraction modules."""
|
"""This class provides a base for DataUsage and NetUsage extraction
|
||||||
|
modules."""
|
||||||
|
|
||||||
def __init__(self, file_path: str = None, target_path: str = None,
|
def __init__(self, file_path: str = None, target_path: str = None,
|
||||||
results_path: str = None, fast_mode: bool = False,
|
results_path: str = None, fast_mode: bool = False,
|
||||||
|
@ -45,7 +46,10 @@ class NetBase(IOSExtraction):
|
||||||
FROM ZLIVEUSAGE
|
FROM ZLIVEUSAGE
|
||||||
LEFT JOIN ZPROCESS ON ZLIVEUSAGE.ZHASPROCESS = ZPROCESS.Z_PK
|
LEFT JOIN ZPROCESS ON ZLIVEUSAGE.ZHASPROCESS = ZPROCESS.Z_PK
|
||||||
UNION
|
UNION
|
||||||
SELECT ZFIRSTTIMESTAMP, ZTIMESTAMP, ZPROCNAME, ZBUNDLENAME, Z_PK, NULL, NULL, NULL, NULL, NULL, NULL, NULL FROM ZPROCESS WHERE Z_PK NOT IN (SELECT ZHASPROCESS FROM ZLIVEUSAGE);
|
SELECT ZFIRSTTIMESTAMP, ZTIMESTAMP, ZPROCNAME, ZBUNDLENAME, Z_PK,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||||
|
FROM ZPROCESS WHERE Z_PK NOT IN
|
||||||
|
(SELECT ZHASPROCESS FROM ZLIVEUSAGE);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for row in cur:
|
for row in cur:
|
||||||
|
@ -83,9 +87,13 @@ class NetBase(IOSExtraction):
|
||||||
self.log.info("Extracted information on %d processes", len(self.results))
|
self.log.info("Extracted information on %d processes", len(self.results))
|
||||||
|
|
||||||
def serialize(self, record: dict) -> Union[dict, list]:
|
def serialize(self, record: dict) -> Union[dict, list]:
|
||||||
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']},"
|
||||||
record_data_usage = record_data + f" WIFI IN: {record['wifi_in']}, WIFI OUT: {record['wifi_out']} - " \
|
f" ID: {record['proc_id']})")
|
||||||
f"WWAN IN: {record['wwan_in']}, WWAN OUT: {record['wwan_out']}"
|
record_data_usage = (record_data + " "
|
||||||
|
f"WIFI IN: {record['wifi_in']}, "
|
||||||
|
f"WIFI OUT: {record['wifi_out']} - "
|
||||||
|
f"WWAN IN: {record['wwan_in']}, "
|
||||||
|
f"WWAN OUT: {record['wwan_out']}")
|
||||||
|
|
||||||
records = [{
|
records = [{
|
||||||
"timestamp": record["live_isodate"],
|
"timestamp": record["live_isodate"],
|
||||||
|
@ -94,8 +102,11 @@ class NetBase(IOSExtraction):
|
||||||
"data": record_data_usage,
|
"data": record_data_usage,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
# Only included first_usage and current_usage records when a ZPROCESS entry exists.
|
# Only included first_usage and current_usage records when a
|
||||||
if "MANIPULATED" not in record["proc_name"] and "MISSING" not in record["proc_name"] and record["live_proc_id"] is not None:
|
# ZPROCESS entry exists.
|
||||||
|
if ("MANIPULATED" not in record["proc_name"]
|
||||||
|
and "MISSING" not in record["proc_name"]
|
||||||
|
and record["live_proc_id"] is not None):
|
||||||
records.extend([
|
records.extend([
|
||||||
{
|
{
|
||||||
"timestamp": record["first_isodate"],
|
"timestamp": record["first_isodate"],
|
||||||
|
@ -122,7 +133,8 @@ class NetBase(IOSExtraction):
|
||||||
|
|
||||||
# If we are instructed to run fast, we skip this.
|
# If we are instructed to run fast, we skip this.
|
||||||
if self.fast_mode:
|
if self.fast_mode:
|
||||||
self.log.info("Flag --fast was enabled: skipping extended search for suspicious processes")
|
self.log.info("Flag --fast was enabled: skipping extended "
|
||||||
|
"search for suspicious processes")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.info("Extended search for suspicious processes ...")
|
self.log.info("Extended search for suspicious processes ...")
|
||||||
|
@ -139,7 +151,8 @@ class NetBase(IOSExtraction):
|
||||||
|
|
||||||
for proc in self.results:
|
for proc in self.results:
|
||||||
if not proc["bundle_id"]:
|
if not proc["bundle_id"]:
|
||||||
self.log.debug("Found process with no Bundle ID with name: %s", proc["proc_name"])
|
self.log.debug("Found process with no Bundle ID with "
|
||||||
|
"name: %s", proc["proc_name"])
|
||||||
|
|
||||||
binary_path = None
|
binary_path = None
|
||||||
for file in files:
|
for file in files:
|
||||||
|
@ -150,15 +163,20 @@ class NetBase(IOSExtraction):
|
||||||
if binary_path:
|
if binary_path:
|
||||||
self.log.debug("Located at %s", binary_path)
|
self.log.debug("Located at %s", binary_path)
|
||||||
else:
|
else:
|
||||||
msg = f"Could not find the binary associated with the process with name {proc['proc_name']}"
|
msg = ("Could not find the binary associated with the "
|
||||||
|
f"process with name {proc['proc_name']}")
|
||||||
if not proc["proc_name"]:
|
if not proc["proc_name"]:
|
||||||
msg = f"Found process entry with empty 'proc_name': {proc['live_proc_id']} at {proc['live_isodate']}"
|
msg = ("Found process entry with empty 'proc_name': "
|
||||||
|
f"{proc['live_proc_id']} at {proc['live_isodate']}")
|
||||||
elif len(proc["proc_name"]) == 16:
|
elif len(proc["proc_name"]) == 16:
|
||||||
msg = msg + " (However, the process name might have been truncated in the database)"
|
msg += (" (However, the process name might have "
|
||||||
|
"been truncated in the database)")
|
||||||
|
|
||||||
self.log.warning(msg)
|
self.log.warning(msg)
|
||||||
if not proc["live_proc_id"]:
|
if not proc["live_proc_id"]:
|
||||||
self.log.info(f"Found process entry in ZPROCESS but not in ZLIVEUSAGE: {proc['proc_name']} at {proc['live_isodate']}")
|
self.log.info("Found process entry in ZPROCESS but not in "
|
||||||
|
"ZLIVEUSAGE: %s at %s",
|
||||||
|
proc['proc_name'], proc['live_isodate'])
|
||||||
|
|
||||||
def check_manipulated(self):
|
def check_manipulated(self):
|
||||||
"""Check for missing or manipulate DB entries"""
|
"""Check for missing or manipulate DB entries"""
|
||||||
|
@ -171,8 +189,9 @@ class NetBase(IOSExtraction):
|
||||||
# Avoid duplicate warnings for same process.
|
# Avoid duplicate warnings for same process.
|
||||||
if result["live_proc_id"] not in missing_process_cache:
|
if result["live_proc_id"] not in missing_process_cache:
|
||||||
missing_process_cache.add(result["live_proc_id"])
|
missing_process_cache.add(result["live_proc_id"])
|
||||||
self.log.warning("Found manipulated process entry %s. Entry on %s",
|
self.log.warning("Found manipulated process entry %s. "
|
||||||
result["live_proc_id"], result["live_isodate"])
|
"Entry on %s", result["live_proc_id"],
|
||||||
|
result["live_isodate"])
|
||||||
|
|
||||||
# Set manipulated proc timestamp so it appears in timeline.
|
# Set manipulated proc timestamp so it appears in timeline.
|
||||||
result["first_isodate"] = result["isodate"] = result["live_isodate"]
|
result["first_isodate"] = result["isodate"] = result["live_isodate"]
|
||||||
|
@ -193,7 +212,8 @@ class NetBase(IOSExtraction):
|
||||||
if proc_id not in all_proc_id:
|
if proc_id not in all_proc_id:
|
||||||
previous_proc = results_by_proc[last_proc_id]
|
previous_proc = results_by_proc[last_proc_id]
|
||||||
self.log.info("Missing process %d. Previous process at \"%s\" (%s)",
|
self.log.info("Missing process %d. Previous process at \"%s\" (%s)",
|
||||||
proc_id, previous_proc["first_isodate"], previous_proc["proc_name"])
|
proc_id, previous_proc["first_isodate"],
|
||||||
|
previous_proc["proc_name"])
|
||||||
|
|
||||||
missing_procs[proc_id] = {
|
missing_procs[proc_id] = {
|
||||||
"proc_id": proc_id,
|
"proc_id": proc_id,
|
||||||
|
@ -216,7 +236,8 @@ class NetBase(IOSExtraction):
|
||||||
|
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
self.results = sorted(self.results, key=operator.itemgetter("first_isodate"))
|
self.results = sorted(self.results,
|
||||||
|
key=operator.itemgetter("first_isodate"))
|
||||||
|
|
||||||
def check_indicators(self) -> None:
|
def check_indicators(self) -> None:
|
||||||
# Check for manipulated process records.
|
# Check for manipulated process records.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user