refactor dumpsys package activity code

This commit is contained in:
tek
2023-07-31 18:38:41 +02:00
parent ae0e470c56
commit f96f2fe34a
10 changed files with 182 additions and 146 deletions

View File

@@ -0,0 +1,4 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/

View File

@@ -0,0 +1,83 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
from .artifact import AndroidArtifact
class DumpsysPackageActivities(AndroidArtifact):
def check_indicators(self) -> None:
if not self.indicators:
return
for activity in self.results:
ioc = self.indicators.check_app_id(activity["package_name"])
if ioc:
activity["matched_indicator"] = ioc
self.detected.append(activity)
continue
def parse(self, content: str):
"""
Parse the Dumpsys Package section for activities
Adds results to self.results
:param content: content of the package section (string)
"""
self.results = []
in_activity_resolver_table = False
in_non_data_actions = False
intent = None
for line in content.splitlines():
if line.startswith("Activity Resolver Table:"):
in_activity_resolver_table = True
continue
if not in_activity_resolver_table:
continue
if line.startswith(" Non-Data Actions:"):
in_non_data_actions = True
continue
if not in_non_data_actions:
continue
# If we hit an empty line, the Non-Data Actions section should be
# finished.
if line.strip() == "":
break
# We detect the action name.
if (
line.startswith(" " * 6)
and not line.startswith(" " * 8)
and ":" in line
):
intent = line.strip().replace(":", "")
continue
# If we are not in an intent block yet, skip.
if not intent:
continue
# If we are in a block but the line does not start with 8 spaces
# it means the block ended a new one started, so we reset and
# continue.
if not line.startswith(" " * 8):
intent = None
continue
# If we got this far, we are processing receivers for the
# activities we are interested in.
activity = line.strip().split(" ")[1]
package_name = activity.split("/")[0]
self.results.append(
{
"intent": intent,
"package_name": package_name,
"activity": activity,
}
)

View File

@@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_activity_resolver_table
from mvt.android.artifacts.dumpsys_package_activities import DumpsysPackageActivities
from .base import AndroidExtraction
class DumpsysActivities(AndroidExtraction):
class DumpsysActivities(DumpsysPackageActivities, AndroidExtraction):
"""This module extracts details on receivers for risky activities."""
def __init__(
@@ -32,25 +32,12 @@ class DumpsysActivities(AndroidExtraction):
results=results,
)
self.results = results if results else {}
def check_indicators(self) -> None:
if not self.indicators:
return
for intent, activities in self.results.items():
for activity in activities:
ioc = self.indicators.check_app_id(activity["package_name"])
if ioc:
activity["matched_indicator"] = ioc
self.detected.append({intent: activity})
continue
self.results = results if results else []
def run(self) -> None:
self._adb_connect()
output = self._adb_command("dumpsys package")
self._adb_disconnect()
self.parse(output)
self.results = parse_dumpsys_activity_resolver_table(output)
self.log.info("Extracted activities for %d intents", len(self.results))
self.log.info("Extracted %d package activities", len(self.results))

View File

@@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_activity_resolver_table
from mvt.android.artifacts.dumpsys_package_activities import DumpsysPackageActivities
from .base import AndroidQFModule
class DumpsysActivities(AndroidQFModule):
class DumpsysActivities(DumpsysPackageActivities, AndroidQFModule):
"""This module extracts details on receivers for risky activities."""
def __init__(
@@ -32,42 +32,17 @@ class DumpsysActivities(AndroidQFModule):
results=results,
)
self.results = results if results else {}
def check_indicators(self) -> None:
if not self.indicators:
return
for intent, activities in self.results.items():
for activity in activities:
ioc = self.indicators.check_app_id(activity["package_name"])
if ioc:
activity["matched_indicator"] = ioc
self.detected.append({intent: activity})
self.results = results if results else []
def run(self) -> None:
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
if not dumpsys_file:
return
lines = []
in_package = False
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
# Get data and extract the dumpsys section
data = self._get_file_content(dumpsys_file[0]).decode("utf-8", errors="replace")
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE package:")
# Parse it
self.parse(content)
if not in_package:
continue
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
self.results = parse_dumpsys_activity_resolver_table("\n".join(lines))
self.log.info("Extracted activities for %d intents", len(self.results))
self.log.info("Extracted %d package activities", len(self.results))

View File

@@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_activity_resolver_table
from mvt.android.artifacts.dumpsys_package_activities import DumpsysPackageActivities
from .base import BugReportModule
class Activities(BugReportModule):
class Activities(DumpsysPackageActivities, BugReportModule):
"""This module extracts details on receivers for risky activities."""
def __init__(
@@ -32,19 +32,7 @@ class Activities(BugReportModule):
results=results,
)
self.results = results if results else {}
def check_indicators(self) -> None:
if not self.indicators:
return
for intent, activities in self.results.items():
for activity in activities:
ioc = self.indicators.check_app_id(activity["package_name"])
if ioc:
activity["matched_indicator"] = ioc
self.detected.append({intent: activity})
continue
self.results = results if results else []
def run(self) -> None:
content = self._get_dumpstate_file()
@@ -55,23 +43,12 @@ class Activities(BugReportModule):
)
return
lines = []
in_package = False
for line in content.decode(errors="ignore").splitlines():
if line.strip() == "DUMP OF SERVICE package:":
in_package = True
continue
# Extract package section
section = self.extract_dumpsys_section(
content.decode("utf-8", errors="ignore"), "DUMP OF SERVICE package:"
)
if not in_package:
continue
# Parse
self.parse(section)
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line)
self.results = parse_dumpsys_activity_resolver_table("\n".join(lines))
self.log.info("Extracted activities for %d intents", len(self.results))
self.log.info("Extracted %d package activities", len(self.results))

View File

@@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/
from .dumpsys import (
parse_dumpsys_activity_resolver_table,
parse_dumpsys_appops,
parse_dumpsys_battery_daily,
parse_dumpsys_battery_history,

View File

@@ -10,64 +10,6 @@ from typing import Any, Dict, List
from mvt.common.utils import convert_datetime_to_iso
def parse_dumpsys_activity_resolver_table(output: str) -> Dict[str, Any]:
results = {}
in_activity_resolver_table = False
in_non_data_actions = False
intent = None
for line in output.splitlines():
if line.startswith("Activity Resolver Table:"):
in_activity_resolver_table = True
continue
if not in_activity_resolver_table:
continue
if line.startswith(" Non-Data Actions:"):
in_non_data_actions = True
continue
if not in_non_data_actions:
continue
# If we hit an empty line, the Non-Data Actions section should be
# finished.
if line.strip() == "":
break
# We detect the action name.
if line.startswith(" " * 6) and not line.startswith(" " * 8) and ":" in line:
intent = line.strip().replace(":", "")
results[intent] = []
continue
# If we are not in an intent block yet, skip.
if not intent:
continue
# If we are in a block but the line does not start with 8 spaces
# it means the block ended a new one started, so we reset and
# continue.
if not line.startswith(" " * 8):
intent = None
continue
# If we got this far, we are processing receivers for the
# activities we are interested in.
activity = line.strip().split(" ")[1]
package_name = activity.split("/")[0]
results[intent].append(
{
"package_name": package_name,
"activity": activity,
}
)
return results
def parse_dumpsys_battery_daily(output: str) -> list:
results = []
daily = None

View File

@@ -0,0 +1,42 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from mvt.android.artifacts.dumpsys_package_activities import DumpsysPackageActivities
from mvt.common.indicators import Indicators
from ..utils import get_artifact
class TestDumpsysPackageActivitiesArtifact:
def test_parsing(self):
dpa = DumpsysPackageActivities()
file = get_artifact("android_data/dumpsys_packages.txt")
with open(file) as f:
data = f.read()
assert len(dpa.results) == 0
dpa.parse(data)
assert len(dpa.results) == 4
assert dpa.results[0]["package_name"] == "com.samsung.android.app.social"
assert (
dpa.results[0]["activity"]
== "com.samsung.android.app.social/.feed.FeedsActivity"
)
def test_ioc_check(self, indicator_file):
dpa = DumpsysPackageActivities()
file = get_artifact("android_data/dumpsys_packages.txt")
with open(file) as f:
data = f.read()
dpa.parse(data)
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.google.android.gms")
dpa.indicators = ind
assert len(dpa.detected) == 0
dpa.check_indicators()
assert len(dpa.detected) == 1

View File

@@ -9,6 +9,32 @@ Libraries:
android.test.mock -> (jar) /system/framework/android.test.mock.jar
Activity Resolver Table:
Full MIME Types:
text/comma-separated-values:
18d43d com.samsung.android.messaging/.ui.RcsTransferContent
72c6194 com.samsung.android.scloud/.app.ui.drive.activity.UploadToDriveActivity2 (2 filters)
a8101e7 com.samsung.android.scloud/.app.ui.drive.activity.UploadToDriveActivity (2 filters)
application/com.google.android.gms.car:
f6b2432 com.google.android.gms/.car.FirstActivity
slide/*:
651d583 com.android.bluetooth/.opp.BluetoothOppLauncherActivity (2 filters)
application/pkix-cert:
6380300 com.android.certinstaller/.CertInstallerMain
Non-Data Actions:
com.samsung.android.intent.SOCIAL_UPDATES_FEED_VIEW:
b928288 com.samsung.android.app.social/.feed.FeedsActivity
android.settings.FINGERPRINT_SETUP:
e573421 com.android.settings/.biometrics.fingerprint.SetupFingerprintEnrollIntroduction
com.msc.action.samsungaccount.myinfowebview_external:
f897046 com.osp.app.signin/com.samsung.android.samsungaccount.authentication.ui.check.user.MyProfileWebView
com.google.android.gms.autofill.ACTION_SETTINGS:
9de5207 com.google.android.gms/.autofill.ui.AutofillSettingsPrivacyHubActivity
Packages:
Package [com.samsung.android.provider.filterprovider] (d64f8e0):

View File

@@ -2,14 +2,15 @@
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import pytest
import logging
import pytest
from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.ios.modules.fs.filesystem import Filesystem
from ..utils import get_ios_backup_folder, delete_tmp_db_files
from ..utils import delete_tmp_db_files, get_ios_backup_folder
@pytest.fixture()