diff --git a/src/mvt/android/artifacts/settings.py b/src/mvt/android/artifacts/settings.py index 17c3bf2..e0cd5f0 100644 --- a/src/mvt/android/artifacts/settings.py +++ b/src/mvt/android/artifacts/settings.py @@ -3,6 +3,8 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ +import re + from .artifact import AndroidArtifact ANDROID_DANGEROUS_SETTINGS = [ @@ -60,6 +62,28 @@ ANDROID_DANGEROUS_SETTINGS = [ class Settings(AndroidArtifact): + def parse(self, content: str) -> None: + self.results: dict[str, dict[str, str]] = {} + namespace: str | None = None + for line in content.splitlines(): + heading = re.match( + r"^(CONFIG|GLOBAL|SECURE|SYSTEM) SETTINGS \(user (\d+)\)$", + line.strip(), + ) + if heading: + namespace = f"{heading.group(1).lower()}:user_{heading.group(2)}" + self.results[namespace] = {} + continue + if namespace is None or not line.startswith("_id:"): + continue + setting = re.match( + r"^_id:\S+\s+name:(.*?)\s+pkg:.*?\s+value:(.*?)" + r"(?:\s+default:.*\s+defaultSystemSet:(?:true|false))?$", + line, + ) + if setting: + self.results[namespace][setting.group(1)] = setting.group(2) + def check_indicators(self) -> None: for namespace, settings in self.results.items(): for key, value in settings.items(): diff --git a/src/mvt/android/modules/bugreport/settings.py b/src/mvt/android/modules/bugreport/settings.py new file mode 100644 index 0000000..20180ad --- /dev/null +++ b/src/mvt/android/modules/bugreport/settings.py @@ -0,0 +1,22 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2026 The MVT Authors. + +from mvt.android.artifacts.settings import Settings as SettingsArtifact + +from .base import BugReportModule + + +class Settings(SettingsArtifact, BugReportModule): + """Extract all SettingsProvider namespaces and users.""" + + def run(self) -> None: + data = self._get_dumpstate_file() + if not data: + self.log.error("Unable to find dumpstate file") + return + section = self.extract_dumpsys_section( + data.decode("utf-8", errors="replace"), "DUMP OF SERVICE settings:" + ) + self.parse(section) + count = sum(len(settings) for settings in self.results.values()) + self.log.info("Identified %d Android settings", count) diff --git a/tests/android_androidqf/test_settings.py b/tests/android_androidqf/test_settings.py index ce14460..6edfec8 100644 --- a/tests/android_androidqf/test_settings.py +++ b/tests/android_androidqf/test_settings.py @@ -6,12 +6,27 @@ from pathlib import Path from mvt.android.modules.androidqf.aqf_settings import AQFSettings +from mvt.android.artifacts.settings import Settings from mvt.common.module import run_module from ..utils import get_android_androidqf, list_files class TestSettingsModule: + def test_bugreport_settings_format(self): + settings = Settings() + settings.parse( + "GLOBAL SETTINGS (user 0)\n" + "_id:1 name:adb_wifi_enabled pkg:android value:0 default:0 defaultSystemSet:true\n" + "SECURE SETTINGS (user 10)\n" + "_id:2 name:accessibility_enabled pkg:android value:1\n" + ) + + assert settings.results == { + "global:user_0": {"adb_wifi_enabled": "0"}, + "secure:user_10": {"accessibility_enabled": "1"}, + } + def test_parsing(self): data_path = get_android_androidqf() m = AQFSettings(target_path=data_path)