Adds support for zip files in check-androidqf command (#372)

This commit is contained in:
Tek
2023-07-26 13:53:54 +02:00
committed by GitHub
parent 57d4aca72e
commit 3ec3b86a45
27 changed files with 225 additions and 114 deletions
+2 -2
View File
@@ -9,13 +9,13 @@ import click
from mvt.common.cmd_check_iocs import CmdCheckIOCS
from mvt.common.help import (
HELP_MSG_ANDROID_BACKUP_PASSWORD,
HELP_MSG_FAST,
HELP_MSG_HASHES,
HELP_MSG_IOC,
HELP_MSG_LIST_MODULES,
HELP_MSG_MODULE,
HELP_MSG_NONINTERACTIVE,
HELP_MSG_ANDROID_BACKUP_PASSWORD,
HELP_MSG_OUTPUT,
HELP_MSG_SERIAL,
HELP_MSG_VERBOSE,
@@ -32,8 +32,8 @@ from .cmd_download_apks import DownloadAPKs
from .modules.adb import ADB_MODULES
from .modules.adb.packages import Packages
from .modules.backup import BACKUP_MODULES
from .modules.bugreport import BUGREPORT_MODULES
from .modules.backup.helpers import cli_load_android_backup_password
from .modules.bugreport import BUGREPORT_MODULES
init_logging()
log = logging.getLogger("mvt")
+29 -1
View File
@@ -4,7 +4,10 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
import os
import zipfile
from pathlib import Path
from typing import List, Optional
from mvt.common.command import Command
@@ -37,3 +40,28 @@ class CmdAndroidCheckAndroidQF(Command):
self.name = "check-androidqf"
self.modules = ANDROIDQF_MODULES
self.format: Optional[str] = None
self.archive: Optional[zipfile.ZipFile] = None
self.files: List[str] = []
def init(self):
if os.path.isdir(self.target_path):
self.format = "dir"
parent_path = Path(self.target_path).absolute().parent.as_posix()
target_abs_path = os.path.abspath(self.target_path)
for root, subdirs, subfiles in os.walk(target_abs_path):
for fname in subfiles:
file_path = os.path.relpath(os.path.join(root, fname), parent_path)
self.files.append(file_path)
elif os.path.isfile(self.target_path):
self.format = "zip"
self.archive = zipfile.ZipFile(self.target_path)
self.files = self.archive.namelist()
def module_init(self, module):
if self.format == "zip":
module.from_zip_file(self.archive, self.files)
else:
parent_path = Path(self.target_path).absolute().parent.as_posix()
module.from_folder(parent_path, self.files)
+1 -1
View File
@@ -12,13 +12,13 @@ from pathlib import Path
from typing import List, Optional
from mvt.android.modules.backup.base import BackupExtraction
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from mvt.android.parsers.backup import (
AndroidBackupParsingError,
InvalidBackupPassword,
parse_ab_header,
parse_backup_file,
)
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from mvt.common.command import Command
from .modules.backup import BACKUP_MODULES
+1 -1
View File
@@ -24,12 +24,12 @@ from adb_shell.exceptions import (
)
from usb1 import USBErrorAccess, USBErrorBusy
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from mvt.android.parsers.backup import (
InvalidBackupPassword,
parse_ab_header,
parse_backup_file,
)
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from mvt.common.module import InsufficientPrivileges, MVTModule
ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey")
+23 -7
View File
@@ -6,6 +6,7 @@
import fnmatch
import logging
import os
import zipfile
from typing import Any, Dict, List, Optional, Union
from mvt.common.module import MVTModule
@@ -31,13 +32,28 @@ class AndroidQFModule(MVTModule):
log=log,
results=results,
)
self._path: str = target_path
self.files: List[str] = []
self.archive: Optional[zipfile.ZipFile] = None
self._path = target_path
self._files = []
def from_folder(self, parent_path: str, files: List[str]):
self.parent_path = parent_path
self.files = files
for root, dirs, files in os.walk(target_path):
for name in files:
self._files.append(os.path.join(root, name))
def from_zip_file(self, archive: zipfile.ZipFile, files: List[str]):
self.archive = archive
self.files = files
def _get_files_by_pattern(self, pattern):
return fnmatch.filter(self._files, pattern)
def _get_files_by_pattern(self, pattern: str):
return fnmatch.filter(self.files, pattern)
def _get_file_content(self, file_path):
if self.archive:
handle = self.archive.open(file_path)
else:
handle = open(os.path.join(self.parent_path, file_path), "rb")
data = handle.read()
handle.close()
return data
@@ -49,21 +49,21 @@ class DumpsysAccessibility(AndroidQFModule):
lines = []
in_accessibility = False
with open(dumpsys_file[0]) as handle:
for line in handle:
if line.strip().startswith("DUMP OF SERVICE accessibility:"):
in_accessibility = True
continue
data = self._get_file_content(dumpsys_file[0])
for line in data.decode("utf-8").split("\n"):
if line.strip().startswith("DUMP OF SERVICE accessibility:"):
in_accessibility = True
continue
if not in_accessibility:
continue
if not in_accessibility:
continue
if line.strip().startswith(
"-------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
if line.strip().startswith(
"-------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
lines.append(line.rstrip())
self.results = parse_dumpsys_accessibility("\n".join(lines))
@@ -52,21 +52,21 @@ class DumpsysActivities(AndroidQFModule):
lines = []
in_package = False
with open(dumpsys_file[0]) as handle:
for line in handle:
if line.strip() == "DUMP OF SERVICE package:":
in_package = True
continue
data = self._get_file_content(dumpsys_file[0])
for line in data.decode("utf-8").split("\n"):
if line.strip() == "DUMP OF SERVICE package:":
in_package = True
continue
if not in_package:
continue
if not in_package:
continue
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
lines.append(line.rstrip())
self.results = parse_dumpsys_activity_resolver_table("\n".join(lines))
+11 -11
View File
@@ -76,19 +76,19 @@ class DumpsysAppops(AndroidQFModule):
lines = []
in_package = False
with open(dumpsys_file[0]) as handle:
for line in handle:
if line.startswith("DUMP OF SERVICE appops:"):
in_package = True
continue
data = self._get_file_content(dumpsys_file[0])
for line in data.decode("utf-8").split("\n"):
if line.startswith("DUMP OF SERVICE appops:"):
in_package = True
continue
if in_package:
if line.startswith(
"-------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
if in_package:
if line.startswith(
"-------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
lines.append(line.rstrip())
self.results = parse_dumpsys_appops("\n".join(lines))
self.log.info("Identified %d applications in AppOps Manager", len(self.results))
@@ -78,13 +78,12 @@ class DumpsysPackages(AndroidQFModule):
self.log.info("Dumpsys file not found")
return
with open(dumpsys_file[0]) as handle:
data = handle.read().split("\n")
data = self._get_file_content(dumpsys_file[0])
package = []
in_service = False
in_package_list = False
for line in data:
for line in data.decode("utf-8").split("\n"):
if line.strip().startswith("DUMP OF SERVICE package:"):
in_service = True
continue
@@ -86,21 +86,21 @@ class DumpsysReceivers(AndroidQFModule):
in_receivers = False
lines = []
with open(dumpsys_file[0]) as handle:
for line in handle:
if line.strip() == "DUMP OF SERVICE package:":
in_receivers = True
continue
data = self._get_file_content(dumpsys_file[0])
for line in data.decode("utf-8").split("\n"):
if line.strip() == "DUMP OF SERVICE package:":
in_receivers = True
continue
if not in_receivers:
continue
if not in_receivers:
continue
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
lines.append(line.rstrip())
self.results = parse_dumpsys_receiver_resolver_table("\n".join(lines))
+1 -2
View File
@@ -39,8 +39,7 @@ class Getprop(GetPropArtifact, AndroidQFModule):
self.log.info("getprop.txt file not found")
return
with open(getprop_files[0]) as f:
data = f.read()
data = self._get_file_content(getprop_files[0]).decode("utf-8")
self.parse(data)
self.log.info("Extracted a total of %d properties", len(self.results))
+1 -3
View File
@@ -37,7 +37,5 @@ class Processes(ProcessesArtifact, AndroidQFModule):
if not ps_files:
return
with open(ps_files[0]) as handle:
self.parse(handle.read())
self.parse(self._get_file_content(ps_files[0]).decode("utf-8"))
self.log.info("Identified %d running processes", len(self.results))
+11 -12
View File
@@ -38,19 +38,18 @@ class Settings(SettingsArtifact, AndroidQFModule):
namespace = setting_file[setting_file.rfind("_") + 1 : -4]
self.results[namespace] = {}
data = self._get_file_content(setting_file)
for line in data.decode("utf-8").split("\n"):
line = line.strip()
try:
key, value = line.split("=", 1)
except ValueError:
continue
with open(setting_file) as handle:
for line in handle:
line = line.strip()
try:
key, value = line.split("=", 1)
except ValueError:
continue
try:
self.results[namespace][key] = value
except IndexError:
continue
try:
self.results[namespace][key] = value
except IndexError:
continue
self.log.info(
"Identified %d settings", sum([len(val) for val in self.results.values()])
+2 -5
View File
@@ -6,6 +6,7 @@
import logging
from typing import Optional
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from mvt.android.parsers.backup import (
AndroidBackupParsingError,
InvalidBackupPassword,
@@ -13,7 +14,6 @@ from mvt.android.parsers.backup import (
parse_backup_file,
parse_tar_for_sms,
)
from mvt.android.modules.backup.helpers import prompt_or_load_android_backup_password
from .base import AndroidQFModule
@@ -96,8 +96,5 @@ class SMS(AndroidQFModule):
self.log.info("No backup data found")
return
with open(files[0], "rb") as handle:
data = handle.read()
self.parse_backup(data)
self.parse_backup(self._get_file_content(files[0]))
self.log.info("Identified %d SMS in backup data", len(self.results))
-1
View File
@@ -7,7 +7,6 @@ import os
from rich.prompt import Prompt
MVT_ANDROID_BACKUP_PASSWORD = "MVT_ANDROID_BACKUP_PASSWORD"