mirror of
https://github.com/mvt-project/mvt.git
synced 2026-02-12 16:42:45 +00:00
Refactor dumpsys accessibility in an artifact
This commit is contained in:
@@ -6,4 +6,30 @@ from mvt.common.artifact import Artifact
|
|||||||
|
|
||||||
|
|
||||||
class AndroidArtifact(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)
|
||||||
|
|||||||
46
mvt/android/artifacts/dumpsys_accessibility.py
Normal file
46
mvt/android/artifacts/dumpsys_accessibility.py
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
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
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
|
||||||
class DumpsysAccessibility(AndroidExtraction):
|
class DumpsysAccessibility(DAA, AndroidExtraction):
|
||||||
"""This module extracts stats on accessibility."""
|
"""This module extracts stats on accessibility."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -32,23 +32,12 @@ class DumpsysAccessibility(AndroidExtraction):
|
|||||||
results=results,
|
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:
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("dumpsys accessibility")
|
output = self._adb_command("dumpsys accessibility")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
||||||
self.results = parse_dumpsys_accessibility(output)
|
self.parse(output)
|
||||||
|
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
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
|
from .base import AndroidQFModule
|
||||||
|
|
||||||
|
|
||||||
class DumpsysAccessibility(AndroidQFModule):
|
class DumpsysAccessibility(DAA, AndroidQFModule):
|
||||||
"""This module analyse dumpsys accessbility"""
|
"""This module analyse dumpsys accessbility"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -32,40 +32,14 @@ class DumpsysAccessibility(AndroidQFModule):
|
|||||||
results=results,
|
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:
|
def run(self) -> None:
|
||||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||||
if not dumpsys_file:
|
if not dumpsys_file:
|
||||||
return
|
return
|
||||||
|
|
||||||
lines = []
|
data = self._get_file_content(dumpsys_file[0]).decode("utf-8", errors="replace")
|
||||||
in_accessibility = False
|
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE accessibility:")
|
||||||
data = self._get_file_content(dumpsys_file[0])
|
self.parse(content)
|
||||||
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))
|
|
||||||
|
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
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
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
|
||||||
class Accessibility(BugReportModule):
|
class Accessibility(DAA, BugReportModule):
|
||||||
"""This module extracts stats on accessibility."""
|
"""This module extracts stats on accessibility."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -32,44 +32,21 @@ class Accessibility(BugReportModule):
|
|||||||
results=results,
|
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:
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
full_dumpsys = self._get_dumpstate_file()
|
||||||
if not content:
|
if not full_dumpsys:
|
||||||
self.log.error(
|
self.log.error(
|
||||||
"Unable to find dumpstate file. "
|
"Unable to find dumpstate file. "
|
||||||
"Did you provide a valid bug report archive?"
|
"Did you provide a valid bug report archive?"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
lines = []
|
content = self.extract_dumpsys_section(
|
||||||
in_accessibility = False
|
full_dumpsys.decode("utf-8", errors="ignore"),
|
||||||
for line in content.decode(errors="ignore").splitlines():
|
"DUMP OF SERVICE accessibility:",
|
||||||
if line.strip() == "DUMP OF SERVICE accessibility:":
|
)
|
||||||
in_accessibility = True
|
self.parse(content)
|
||||||
continue
|
|
||||||
|
|
||||||
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:
|
for result in self.results:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
'Found installed accessibility service "%s"', result.get("service")
|
'Found installed accessibility service "%s"', result.get("service")
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
from .dumpsys import (
|
from .dumpsys import (
|
||||||
parse_dumpsys_accessibility,
|
|
||||||
parse_dumpsys_activity_resolver_table,
|
parse_dumpsys_activity_resolver_table,
|
||||||
parse_dumpsys_appops,
|
parse_dumpsys_appops,
|
||||||
parse_dumpsys_battery_daily,
|
parse_dumpsys_battery_daily,
|
||||||
|
|||||||
@@ -10,33 +10,6 @@ from typing import Any, Dict, List
|
|||||||
from mvt.common.utils import convert_datetime_to_iso
|
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]:
|
def parse_dumpsys_activity_resolver_table(output: str) -> Dict[str, Any]:
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
|
|||||||
20
tests/android/test_artifact.py
Normal file
20
tests/android/test_artifact.py
Normal 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
|
||||||
42
tests/android/test_artifact_dumpsys_accessibility.py
Normal file
42
tests/android/test_artifact_dumpsys_accessibility.py
Normal 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
|
||||||
0
tests/android_adb/__init__.py
Normal file
0
tests/android_adb/__init__.py
Normal file
40
tests/artifacts/android_data/dumpsys_accessibility.txt
Normal file
40
tests/artifacts/android_data/dumpsys_accessibility.txt
Normal 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:{
|
||||||
|
}]
|
||||||
|
|
||||||
Reference in New Issue
Block a user