mirror of
https://github.com/mvt-project/mvt.git
synced 2026-10-23 07:38:22 +02:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6494088943 | ||
|
|
60bb1573f4 | ||
|
|
9bcbaac3a2 |
No files matched your search
@@ -5,9 +5,7 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "uv" # See documentation for possible values
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
@@ -12,14 +12,11 @@ jobs:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
uv sync --locked --group dev
|
||||
pip install mypy
|
||||
- name: mypy
|
||||
run: |
|
||||
make mypy
|
||||
@@ -14,16 +14,13 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
uv sync --locked --group dev
|
||||
pip install ruff
|
||||
- name: ruff
|
||||
run: |
|
||||
make ruff
|
||||
@@ -16,17 +16,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
uv sync --locked --group dev --python ${{ matrix.python-version }}
|
||||
make install
|
||||
make test-requirements
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
set -o pipefail
|
||||
|
||||
+1
-4
@@ -86,9 +86,6 @@ ipython_config.py
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# uv project Python version
|
||||
!.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
@@ -141,4 +138,4 @@ dmypy.json
|
||||
.idea
|
||||
|
||||
# Sublime Text project files
|
||||
*.sublime*
|
||||
*.sublime*
|
||||
@@ -1 +0,0 @@
|
||||
3.10
|
||||
+4
-6
@@ -6,16 +6,14 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: "ubuntu-24.04"
|
||||
os: "ubuntu-22.04"
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
mkdocs:
|
||||
configuration: mkdocs.yml
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- method: uv
|
||||
command: sync
|
||||
groups:
|
||||
- docs
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
+6
-3
@@ -104,7 +104,6 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
|
||||
|
||||
# Create main image
|
||||
FROM ubuntu:24.04 AS main
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.11.8 /uv /uvx /usr/local/bin/
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
@@ -134,8 +133,12 @@ COPY --from=build-usbmuxd /build /
|
||||
|
||||
# Install mvt using the locally checked out source
|
||||
COPY . mvt/
|
||||
RUN uv pip install --system --break-system-packages --no-cache ./mvt \
|
||||
&& rm -rf mvt
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git python3-pip \
|
||||
&& PIP_NO_CACHE_DIR=1 pip3 install --break-system-packages ./mvt \
|
||||
&& apt-get remove -y python3-pip git && apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -rf mvt
|
||||
|
||||
# Installing ABE
|
||||
ADD --checksum=sha256:a20e07f8b2ea47620aff0267f230c3f1f495f097081fd709eec51cf2a2e11632 \
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
# Create main image
|
||||
FROM python:3.10.14-alpine3.20 AS main
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.11.8 /uv /uvx /usr/local/bin/
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
@@ -21,7 +20,7 @@ RUN apk add --no-cache \
|
||||
# Install mvt
|
||||
COPY ./ mvt
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev \
|
||||
&& uv pip install --system --no-cache ./mvt \
|
||||
&& PIP_NO_CACHE_DIR=1 pip3 install ./mvt \
|
||||
&& apk del .build-deps gcc musl-dev && rm -rf ./mvt
|
||||
|
||||
# Installing ABE
|
||||
|
||||
+1
-2
@@ -105,7 +105,6 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
|
||||
# Main image
|
||||
# ----------
|
||||
FROM python:3.10.14-alpine3.20 AS main
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.11.8 /uv /uvx /usr/local/bin/
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
@@ -132,7 +131,7 @@ COPY --from=build-usbmuxd /build /
|
||||
# Install mvt using the locally checked out source
|
||||
COPY ./ mvt
|
||||
RUN apk add --no-cache --virtual .build-deps git gcc musl-dev \
|
||||
&& uv pip install --system --no-cache ./mvt \
|
||||
&& PIP_NO_CACHE_DIR=1 pip3 install ./mvt \
|
||||
&& apk del .build-deps git gcc musl-dev && rm -rf ./mvt
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/mvt-ios" ]
|
||||
@@ -1,39 +1,39 @@
|
||||
PWD = $(shell pwd)
|
||||
UV ?= uv
|
||||
|
||||
check: ruff mypy
|
||||
|
||||
ruff:
|
||||
$(UV) run ruff check .
|
||||
ruff check .
|
||||
|
||||
mypy:
|
||||
$(UV) run mypy
|
||||
mypy
|
||||
|
||||
test:
|
||||
$(UV) run pytest
|
||||
python3 -m pytest
|
||||
|
||||
test-ci:
|
||||
$(UV) run pytest -v
|
||||
python3 -m pytest -v
|
||||
|
||||
install:
|
||||
$(UV) sync
|
||||
python3 -m pip install --upgrade -e .
|
||||
|
||||
test-requirements:
|
||||
$(UV) sync --group dev
|
||||
python3 -m pip install --upgrade --group dev
|
||||
|
||||
generate-proto-parsers:
|
||||
# Generate python parsers for protobuf files
|
||||
PROTO_FILES=$$(find src/mvt/android/parsers/proto/ -iname "*.proto"); \
|
||||
$(UV) run protoc -Isrc/mvt/android/parsers/proto/ --python_betterproto2_out=src/mvt/android/parsers/proto/ $$PROTO_FILES
|
||||
protoc -Isrc/mvt/android/parsers/proto/ --python_betterproto2_out=src/mvt/android/parsers/proto/ $$PROTO_FILES
|
||||
|
||||
clean:
|
||||
rm -rf $(PWD)/build $(PWD)/dist $(PWD)/src/mvt.egg-info
|
||||
|
||||
dist:
|
||||
$(UV) build
|
||||
python3 -m pip install --upgrade build
|
||||
python3 -m build
|
||||
|
||||
upload:
|
||||
$(UV) tool run twine upload dist/*
|
||||
python3 -m twine upload dist/*
|
||||
|
||||
test-upload:
|
||||
$(UV) tool run twine upload --repository testpypi dist/*
|
||||
python3 -m twine upload --repository testpypi dist/*
|
||||
@@ -41,18 +41,6 @@ MVT can be installed from sources or from [PyPI](https://pypi.org/project/mvt/)
|
||||
pip3 install mvt
|
||||
```
|
||||
|
||||
You can also install MVT from PyPI with [uv](https://docs.astral.sh/uv/). First, install uv:
|
||||
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
```
|
||||
|
||||
Then install MVT as a command-line tool:
|
||||
|
||||
```bash
|
||||
uv tool install mvt
|
||||
```
|
||||
|
||||
For alternative installation options and known issues, please refer to the [documentation](https://docs.mvt.re/en/latest/install/) as well as [GitHub Issues](https://github.com/mvt-project/mvt/issues).
|
||||
|
||||
|
||||
|
||||
+2
-10
@@ -2,17 +2,9 @@
|
||||
|
||||
The Mobile Verification Toolkit team welcomes contributions of new forensic modules or other contributions which help improve the software.
|
||||
|
||||
## Local environment
|
||||
|
||||
MVT uses `uv` for dependency management. To install the project and development dependencies from the locked environment, run:
|
||||
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
MVT uses `pytest` for unit and integration tests. Code style consistency is maintained with `ruff` and `mypy`. All can
|
||||
MVT uses `pytest` for unit and integration tests. Code style consistency is maintained with `flake8`, `ruff` and `black`. All can
|
||||
be run automatically with:
|
||||
|
||||
```bash
|
||||
@@ -32,4 +24,4 @@ MVT modules can be profiled with Python built-in `cProfile` by setting the `MVT_
|
||||
MVT_PROFILE=1 dev/mvt-ios check-backup test_backup
|
||||
```
|
||||
|
||||
Open an issue or PR if you are encountering significant performance issues when analyzing a device with MVT.
|
||||
Open an issue or PR if you are encountering significant performance issues when analyzing a device with MVT.
|
||||
@@ -0,0 +1,5 @@
|
||||
mkdocs==1.6.1
|
||||
mkdocs-autorefs==1.4.4
|
||||
mkdocs-material==9.7.6
|
||||
mkdocs-material-extensions==1.3.1
|
||||
mkdocstrings==1.0.4
|
||||
+2
-11
@@ -58,16 +58,6 @@ dev = [
|
||||
"ruff>=0.1.6",
|
||||
"mypy>=1.7.1",
|
||||
"betterproto2-compiler",
|
||||
"types-PyYAML>=6.0.12.20250516",
|
||||
"types-python-dateutil>=2.9.0.20250822",
|
||||
"types-requests>=2.32.4.20250913",
|
||||
]
|
||||
docs = [
|
||||
"mkdocs==1.6.1",
|
||||
"mkdocs-autorefs==1.4.4",
|
||||
"mkdocs-material==9.7.6",
|
||||
"mkdocs-material-extensions==1.3.1",
|
||||
"mkdocstrings==1.0.4",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
@@ -81,7 +71,8 @@ omit = ["tests/*"]
|
||||
directory = "htmlcov"
|
||||
|
||||
[tool.mypy]
|
||||
install_types = false
|
||||
install_types = true
|
||||
non_interactive = true
|
||||
ignore_missing_imports = true
|
||||
packages = "src"
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ class DumpsysAccessibilityArtifact(AndroidArtifact):
|
||||
|
||||
def parse(self, content: str) -> None:
|
||||
"""
|
||||
Parse the Dumpsys Accessibility section/
|
||||
Adds results to self.results (List[Dict[str, str]])
|
||||
Parse the Dumpsys Accessibility section.
|
||||
Adds results to self.results (List[Dict[str, Any]])
|
||||
|
||||
:param content: content of the accessibility section (string)
|
||||
"""
|
||||
|
||||
# "Old" syntax
|
||||
# Parse installed services
|
||||
in_services = False
|
||||
for line in content.splitlines():
|
||||
if line.strip().startswith("installed services:"):
|
||||
@@ -40,7 +40,6 @@ class DumpsysAccessibilityArtifact(AndroidArtifact):
|
||||
continue
|
||||
|
||||
if line.strip() == "}":
|
||||
# At end of installed services
|
||||
break
|
||||
|
||||
service = line.split(":")[1].strip()
|
||||
@@ -49,21 +48,66 @@ class DumpsysAccessibilityArtifact(AndroidArtifact):
|
||||
{
|
||||
"package_name": service.split("/")[0],
|
||||
"service": service,
|
||||
"enabled": False,
|
||||
}
|
||||
)
|
||||
|
||||
# "New" syntax - AOSP >= 14 (?)
|
||||
# Looks like:
|
||||
# Enabled services:{{com.azure.authenticator/com.microsoft.brooklyn.module.accessibility.BrooklynAccessibilityService}, {com.agilebits.onepassword/com.agilebits.onepassword.filling.accessibility.FillingAccessibilityService}}
|
||||
# Parse enabled services from both old and new formats.
|
||||
#
|
||||
# Old format (multi-line block):
|
||||
# enabled services: {
|
||||
# 0 : com.example/.MyService
|
||||
# }
|
||||
#
|
||||
# New format (single line, AOSP >= 14):
|
||||
# Enabled services:{{com.example/com.example.MyService}, {com.other/com.other.Svc}}
|
||||
enabled_services = set()
|
||||
|
||||
in_enabled = False
|
||||
for line in content.splitlines():
|
||||
if line.strip().startswith("Enabled services:"):
|
||||
matches = re.finditer(r"{([^{]+?)}", line)
|
||||
stripped = line.strip()
|
||||
|
||||
if in_enabled:
|
||||
if stripped == "}":
|
||||
in_enabled = False
|
||||
continue
|
||||
service = line.split(":")[1].strip()
|
||||
enabled_services.add(service)
|
||||
continue
|
||||
|
||||
if re.match(r"enabled services:\s*\{\s*$", stripped, re.IGNORECASE):
|
||||
# Old multi-line format: "enabled services: {"
|
||||
in_enabled = True
|
||||
continue
|
||||
|
||||
if re.match(r"enabled services:\s*\{", stripped, re.IGNORECASE):
|
||||
# New single-line format: "Enabled services:{{pkg/svc}, {pkg2/svc2}}"
|
||||
matches = re.finditer(r"\{([^{}]+)\}", stripped)
|
||||
for match in matches:
|
||||
# Each match is in format: <package_name>/<service>
|
||||
package_name, _, service = match.group(1).partition("/")
|
||||
enabled_services.add(match.group(1).strip())
|
||||
|
||||
self.results.append(
|
||||
{"package_name": package_name, "service": service}
|
||||
)
|
||||
# Mark installed services that are enabled.
|
||||
# Installed service names may include trailing annotations like
|
||||
# "(A11yTool)" that are absent from the enabled services list,
|
||||
# so strip annotations before comparing.
|
||||
def _strip_annotation(s: str) -> str:
|
||||
return re.sub(r"\s+\(.*?\)\s*$", "", s)
|
||||
|
||||
installed_stripped = {
|
||||
_strip_annotation(r["service"]): r for r in self.results
|
||||
}
|
||||
for enabled in enabled_services:
|
||||
if enabled in installed_stripped:
|
||||
installed_stripped[enabled]["enabled"] = True
|
||||
|
||||
# Add enabled services not found in the installed list
|
||||
for service in enabled_services:
|
||||
if service not in installed_stripped:
|
||||
package_name, _, _ = service.partition("/")
|
||||
self.results.append(
|
||||
{
|
||||
"package_name": package_name,
|
||||
"service": service,
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
@@ -50,9 +50,14 @@ class DumpsysAccessibility(DumpsysAccessibilityArtifact, BugReportModule):
|
||||
|
||||
for result in self.results:
|
||||
self.log.info(
|
||||
'Found installed accessibility service "%s"', result.get("service")
|
||||
'Found installed accessibility service "%s" (enabled: %s)',
|
||||
result.get("service"),
|
||||
result.get("enabled", False),
|
||||
)
|
||||
|
||||
enabled_count = sum(1 for r in self.results if r.get("enabled"))
|
||||
self.log.info(
|
||||
"Identified a total of %d accessibility services", len(self.results)
|
||||
"Identified a total of %d accessibility services, %d enabled",
|
||||
len(self.results),
|
||||
enabled_count,
|
||||
)
|
||||
@@ -25,6 +25,9 @@ class TestDumpsysAccessibilityArtifact:
|
||||
da.results[0]["service"]
|
||||
== "com.android.settings/com.samsung.android.settings.development.gpuwatch.GPUWatchInterceptor"
|
||||
)
|
||||
# All services are installed but none enabled in this fixture
|
||||
for result in da.results:
|
||||
assert result["enabled"] is False
|
||||
|
||||
def test_parsing_v14_aosp_format(self):
|
||||
da = DumpsysAccessibilityArtifact()
|
||||
@@ -36,7 +39,32 @@ class TestDumpsysAccessibilityArtifact:
|
||||
da.parse(data)
|
||||
assert len(da.results) == 1
|
||||
assert da.results[0]["package_name"] == "com.malware.accessibility"
|
||||
assert da.results[0]["service"] == "com.malware.service.malwareservice"
|
||||
assert (
|
||||
da.results[0]["service"]
|
||||
== "com.malware.accessibility/com.malware.service.malwareservice"
|
||||
)
|
||||
assert da.results[0]["enabled"] is True
|
||||
|
||||
def test_parsing_installed_and_enabled(self):
|
||||
da = DumpsysAccessibilityArtifact()
|
||||
file = get_artifact("android_data/dumpsys_accessibility_enabled.txt")
|
||||
with open(file) as f:
|
||||
data = f.read()
|
||||
|
||||
assert len(da.results) == 0
|
||||
da.parse(data)
|
||||
assert len(da.results) == 5
|
||||
|
||||
enabled = [r for r in da.results if r["enabled"]]
|
||||
assert len(enabled) == 1
|
||||
assert enabled[0]["package_name"] == "com.samsung.accessibility"
|
||||
assert (
|
||||
enabled[0]["service"]
|
||||
== "com.samsung.accessibility/.universalswitch.UniversalSwitchService (A11yTool)"
|
||||
)
|
||||
|
||||
not_enabled = [r for r in da.results if not r["enabled"]]
|
||||
assert len(not_enabled) == 4
|
||||
|
||||
def test_ioc_check(self, indicator_file):
|
||||
da = DumpsysAccessibilityArtifact()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
ACCESSIBILITY MANAGER (dumpsys accessibility)
|
||||
|
||||
currentUserId=0
|
||||
User state[
|
||||
attributes:{id=0, touchExplorationEnabled=false, installedServiceCount=5}
|
||||
installed services: {
|
||||
0 : com.google.android.apps.accessibility.voiceaccess/.JustSpeakService (A11yTool)
|
||||
1 : com.microsoft.appmanager/com.microsoft.mmx.screenmirroringsrc.accessibility.ScreenMirroringAccessibilityService
|
||||
2 : com.samsung.accessibility/.assistantmenu.serviceframework.AssistantMenuService (A11yTool)
|
||||
3 : com.samsung.accessibility/.universalswitch.UniversalSwitchService (A11yTool)
|
||||
4 : com.samsung.android.accessibility.talkback/com.samsung.android.marvin.talkback.TalkBackService (A11yTool)
|
||||
}
|
||||
Bound services:{}
|
||||
Enabled services:{{com.samsung.accessibility/.universalswitch.UniversalSwitchService}}
|
||||
Binding services:{}
|
||||
Crashed services:{}
|
||||
Reference in new issue
Block a user