diff --git a/src/mvt/android/artifacts/dumpsys_packages.py b/src/mvt/android/artifacts/dumpsys_packages.py index a18d4f4..341c57d 100644 --- a/src/mvt/android/artifacts/dumpsys_packages.py +++ b/src/mvt/android/artifacts/dumpsys_packages.py @@ -4,7 +4,7 @@ # https://license.mvt.re/1.1/ import re -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional from mvt.android.utils import ROOT_PACKAGES from mvt.common.module_types import ModuleAtomicResult, ModuleSerializedResult @@ -79,7 +79,14 @@ class DumpsysPackagesArtifact(AndroidArtifact): in_runtime_permissions = False in_declared_permissions = False in_requested_permissions = True + current_user: Optional[int] = None + first_install_times: Dict[Optional[int], str] = {} + runtime_permissions: Dict[Optional[int], List[Dict[str, Any]]] = {} for line in output.splitlines(): + user_match = re.match(r"User (\d+):", line.strip()) + if user_match: + current_user = int(user_match.group(1)) + if in_install_permissions: if line.startswith(" " * 4) and not line.startswith(" " * 6): in_install_permissions = False @@ -103,7 +110,7 @@ class DumpsysPackagesArtifact(AndroidArtifact): if "granted=" in lineinfo[1]: granted = "granted=true" in lineinfo[1] - details["permissions"].append( + runtime_permissions.setdefault(current_user, []).append( {"name": permission, "granted": granted, "type": "runtime"} ) if in_declared_permissions: @@ -130,7 +137,7 @@ class DumpsysPackagesArtifact(AndroidArtifact): elif line.strip().startswith("installerPackageName="): details["installer"] = line.split("=", 1)[1].strip() elif line.strip().startswith("firstInstallTime="): - details["first_install_time"] = line.split("=")[1].strip() + first_install_times[current_user] = line.split("=", 1)[1].strip() elif line.strip().startswith("lastUpdateTime="): details["last_update_time"] = line.split("=")[1].strip() elif line.strip() == "install permissions:": @@ -142,6 +149,19 @@ class DumpsysPackagesArtifact(AndroidArtifact): elif line.strip() == "requested permissions:": in_requested_permissions = True + if 0 in first_install_times: + details["first_install_time"] = first_install_times[0] + elif None in first_install_times: + details["first_install_time"] = first_install_times[None] + elif first_install_times: + details["first_install_time"] = next(iter(first_install_times.values())) + + if 0 in runtime_permissions: + details["permissions"].extend(runtime_permissions[0]) + elif None in runtime_permissions: + details["permissions"].extend(runtime_permissions[None]) + elif runtime_permissions: + details["permissions"].extend(next(iter(runtime_permissions.values()))) return details def parse_dumpsys_packages(self, output: str) -> List[Dict[str, Any]]: diff --git a/tests/android/test_artifact_dumpsys_packages.py b/tests/android/test_artifact_dumpsys_packages.py index 7b2ec0f..3978939 100644 --- a/tests/android/test_artifact_dumpsys_packages.py +++ b/tests/android/test_artifact_dumpsys_packages.py @@ -25,6 +25,7 @@ class TestDumpsysPackagesArtifact: == "com.samsung.android.provider.filterprovider" ) assert dpa.results[0]["version_name"] == "5.0.07" + assert dpa.results[0]["first_install_time"] == "2008-12-31 16:00:00" def test_ioc_check(self, indicator_file): dpa = DumpsysPackagesArtifact() @@ -40,3 +41,43 @@ class TestDumpsysPackagesArtifact: assert len(dpa.alertstore.alerts) == 0 dpa.check_indicators() assert len(dpa.alertstore.alerts) == 1 + + def test_per_user_fields_use_primary_user(self): + details = DumpsysPackagesArtifact.parse_dumpsys_package_for_details( + """ User 0: installed=true + firstInstallTime=2024-01-10 09:19:39 + runtime permissions: + android.permission.CAMERA: granted=true + User 95: installed=false + firstInstallTime=1970-01-01 01:00:00 + runtime permissions: + android.permission.CAMERA: granted=false + android.permission.RECORD_AUDIO: granted=false +""" + ) + + assert details["first_install_time"] == "2024-01-10 09:19:39" + runtime_permissions = [ + permission + for permission in details["permissions"] + if permission["type"] == "runtime" + ] + assert runtime_permissions == [ + { + "name": "android.permission.CAMERA", + "granted": True, + "type": "runtime", + } + ] + + def test_per_user_fields_fall_back_when_user_zero_is_missing(self): + details = DumpsysPackagesArtifact.parse_dumpsys_package_for_details( + """ User 10: installed=true + firstInstallTime=2024-02-10 09:19:39 + runtime permissions: + android.permission.CAMERA: granted=true +""" + ) + + assert details["first_install_time"] == "2024-02-10 09:19:39" + assert details["permissions"][-1]["name"] == "android.permission.CAMERA"