Refactor dumpsys accessibility in an artifact

This commit is contained in:
tek 2023-07-27 19:42:06 +02:00
parent ecf75447aa
commit 4c175530a8
11 changed files with 192 additions and 106 deletions

View File

@ -6,4 +6,30 @@ from mvt.common.artifact import Artifact
class AndroidArtifact(Artifact):
pass
@staticmethod
def extract_dumpsys_section(dumpsys: str, separator: str) -> str:
"""
Extract a section from a full dumpsys file.
:param dumpsys: content of the full dumpsys file (string)
:param separator: content of the first line separator (string)
:return: section extracted (string)
"""
lines = []
in_section = False
for line in dumpsys.splitlines():
if line.strip() == separator:
in_section = True
continue
if not in_section:
continue
if line.strip().startswith(
"------------------------------------------------------------------------------"
):
break
lines.append(line)
return "\n".join(lines)

View File

@ -0,0 +1,46 @@
# 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 DumpsysAccessibility(AndroidArtifact):
def check_indicators(self) -> None:
if not self.indicators:
return
for result in self.results:
ioc = self.indicators.check_app_id(result["package_name"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
continue
def parse(self, content: str):
"""
Parse the Dumpsys Accessibility section/
Adds results to self.results (List[Dict[str, str]])
:param content: content of the accessibility section (string)
"""
in_services = False
for line in content.splitlines():
if line.strip().startswith("installed services:"):
in_services = True
continue
if not in_services:
continue
if line.strip() == "}":
break
service = line.split(":")[1].strip()
self.results.append(
{
"package_name": service.split("/")[0],
"service": service,
}
)

View File

@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_accessibility
from mvt.android.artifacts.dumpsys_accessibility import DumpsysAccessibility as DAA
from .base import AndroidExtraction
class DumpsysAccessibility(AndroidExtraction):
class DumpsysAccessibility(DAA, AndroidExtraction):
"""This module extracts stats on accessibility."""
def __init__(
@ -32,23 +32,12 @@ class DumpsysAccessibility(AndroidExtraction):
results=results,
)
def check_indicators(self) -> None:
if not self.indicators:
return
for result in self.results:
ioc = self.indicators.check_app_id(result["package_name"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
continue
def run(self) -> None:
self._adb_connect()
output = self._adb_command("dumpsys accessibility")
self._adb_disconnect()
self.results = parse_dumpsys_accessibility(output)
self.parse(output)
for result in self.results:
self.log.info(

View File

@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_accessibility
from mvt.android.artifacts.dumpsys_accessibility import DumpsysAccessibility as DAA
from .base import AndroidQFModule
class DumpsysAccessibility(AndroidQFModule):
class DumpsysAccessibility(DAA, AndroidQFModule):
"""This module analyse dumpsys accessbility"""
def __init__(
@ -32,40 +32,14 @@ class DumpsysAccessibility(AndroidQFModule):
results=results,
)
def check_indicators(self) -> None:
if not self.indicators:
return
for result in self.results:
ioc = self.indicators.check_app_id(result["package_name"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
def run(self) -> None:
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
if not dumpsys_file:
return
lines = []
in_accessibility = False
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 line.strip().startswith(
"-------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line.rstrip())
self.results = parse_dumpsys_accessibility("\n".join(lines))
data = self._get_file_content(dumpsys_file[0]).decode("utf-8", errors="replace")
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE accessibility:")
self.parse(content)
for result in self.results:
self.log.info(

View File

@ -6,12 +6,12 @@
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_accessibility
from mvt.android.artifacts.dumpsys_accessibility import DumpsysAccessibility as DAA
from .base import BugReportModule
class Accessibility(BugReportModule):
class Accessibility(DAA, BugReportModule):
"""This module extracts stats on accessibility."""
def __init__(
@ -32,44 +32,21 @@ class Accessibility(BugReportModule):
results=results,
)
def check_indicators(self) -> None:
if not self.indicators:
return
for result in self.results:
ioc = self.indicators.check_app_id(result["package_name"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
continue
def run(self) -> None:
content = self._get_dumpstate_file()
if not content:
full_dumpsys = self._get_dumpstate_file()
if not full_dumpsys:
self.log.error(
"Unable to find dumpstate file. "
"Did you provide a valid bug report archive?"
)
return
lines = []
in_accessibility = False
for line in content.decode(errors="ignore").splitlines():
if line.strip() == "DUMP OF SERVICE accessibility:":
in_accessibility = True
continue
content = self.extract_dumpsys_section(
full_dumpsys.decode("utf-8", errors="ignore"),
"DUMP OF SERVICE accessibility:",
)
self.parse(content)
if not in_accessibility:
continue
if line.strip().startswith(
"------------------------------------------------------------------------------"
): # pylint: disable=line-too-long
break
lines.append(line)
self.results = parse_dumpsys_accessibility("\n".join(lines))
for result in self.results:
self.log.info(
'Found installed accessibility service "%s"', result.get("service")

View File

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

View File

@ -10,33 +10,6 @@ from typing import Any, Dict, List
from mvt.common.utils import convert_datetime_to_iso
def parse_dumpsys_accessibility(output: str) -> List[Dict[str, str]]:
results = []
in_services = False
for line in output.splitlines():
if line.strip().startswith("installed services:"):
in_services = True
continue
if not in_services:
continue
if line.strip() == "}":
break
service = line.split(":")[1].strip()
results.append(
{
"package_name": service.split("/")[0],
"service": service,
}
)
return results
def parse_dumpsys_activity_resolver_table(output: str) -> Dict[str, Any]:
results = {}

View File

@ -0,0 +1,20 @@
# 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 mvt.android.artifacts.artifact import AndroidArtifact
from ..utils import get_artifact
class TestAndroidArtifact:
def test_extract_dumpsys_section(self):
file = get_artifact("androidqf/dumpsys.txt")
with open(file) as f:
data = f.read()
section = AndroidArtifact.extract_dumpsys_section(
data, "DUMP OF SERVICE package:"
)
assert isinstance(section, str)
assert len(section) == 3907

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_accessibility import DumpsysAccessibility
from mvt.common.indicators import Indicators
from ..utils import get_artifact
class TestDumpsysAccessibilityArtifact:
def test_parsing(self):
da = DumpsysAccessibility()
file = get_artifact("android_data/dumpsys_accessibility.txt")
with open(file) as f:
data = f.read()
assert len(da.results) == 0
da.parse(data)
assert len(da.results) == 4
assert da.results[0]["package_name"] == "com.android.settings"
assert (
da.results[0]["service"]
== "com.android.settings/com.samsung.android.settings.development.gpuwatch.GPUWatchInterceptor"
)
def test_ioc_check(self, indicator_file):
da = DumpsysAccessibility()
file = get_artifact("android_data/dumpsys_accessibility.txt")
with open(file) as f:
data = f.read()
da.parse(data)
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.sec.android.app.camera")
da.indicators = ind
assert len(da.detected) == 0
da.check_indicators()
assert len(da.detected) == 1

View File

View File

@ -0,0 +1,40 @@
ACCESSIBILITY MANAGER (dumpsys accessibility)
User state[attributes:{id=0, currentUser=true
mIsNavBarMagnificationAssignedToAccessibilityButton = false
mIsNavBarMagnifierWindowAssignedToAccessibilityButton = false
mIsNavBarAmplifyAmbientSoundAssignedToAccessibilityButton = false
mIsAmplifyAmbientSoundEnabled = false
mIsBixbyRunning = false
mIsMagniferWindowEnabled = false
mIsFollowTypingFocusEnabled = false
mIsTapDurationEnabled = false
mIsTouchBlockingEnabled = false
mIsTextHighContrastEnabled = false
mIsDisplayMagnificationEnabled = false
mIsNavBarMagnificationEnabled = false
mIsAutoclickEnabled = false
mIsPerformGesturesEnabled = false
mIsFilterKeyEventsEnabled = false
mAccessibilityFocusOnlyInActiveWindow = true
mUserNonInteractiveUiTimeout = 0
mUserInteractiveUiTimeout = 0
mBindInstantServiceAllowed = false
mIsGestureNaviBar = false
}
installed services: {
0 : com.android.settings/com.samsung.android.settings.development.gpuwatch.GPUWatchInterceptor
1 : com.samsung.accessibility/.universalswitch.UniversalSwitchService
2 : com.samsung.accessibility/com.samsung.android.app.talkback.TalkBackService
3 : com.sec.android.app.camera/com.samsung.android.glview.AccessibilityGestureHandler
}
enabled services: {
}
binding services: {
}
bound services:{
}
AccessibilityInputFilter:{
}]