Compare commits

..

11 Commits

Author SHA1 Message Date
besendorf
e507c3ecbc Merge branch 'main' into fix/vt 2025-09-19 07:30:14 +02:00
github-actions[bot]
f020655a1a Add new iOS versions and build numbers (#693)
Co-authored-by: DonnchaC <DonnchaC@users.noreply.github.com>
2025-09-16 15:52:32 +02:00
github-actions[bot]
91c34e6664 Add new iOS versions and build numbers (#692)
Co-authored-by: DonnchaC <DonnchaC@users.noreply.github.com>
2025-09-15 20:13:40 +02:00
dependabot[bot]
b4a8dd226a Bump mkdocs-material from 9.6.18 to 9.6.20 (#691)
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.6.18 to 9.6.20.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.18...9.6.20)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-version: 9.6.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 19:40:29 +02:00
dependabot[bot]
88213e12c9 Bump mkdocs-autorefs from 1.4.2 to 1.4.3 (#686)
Bumps [mkdocs-autorefs](https://github.com/mkdocstrings/autorefs) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/mkdocstrings/autorefs/releases)
- [Changelog](https://github.com/mkdocstrings/autorefs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/autorefs/compare/1.4.2...1.4.3)

---
updated-dependencies:
- dependency-name: mkdocs-autorefs
  dependency-version: 1.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 18:30:36 +02:00
Janik Besendorf
197c89b08f make virustotal check also work with androidqf extractions 2025-09-01 17:40:11 +02:00
r-tx
f75b8e186a add iOS 18.6.2 (#682)
* iOS 18.6.2

* iOS 18.6.2

---------

Co-authored-by: r-tx <r-tx@users.noreply.github.com>
Co-authored-by: Tek <tek@randhome.io>
2025-08-26 13:52:55 +02:00
dependabot[bot]
5babc1fcf3 Bump mkdocs-material from 9.6.17 to 9.6.18 (#683)
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.6.17 to 9.6.18.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.17...9.6.18)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-version: 9.6.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-26 11:25:22 +02:00
besendorf
b723ebf28e move test dependencies to dev dependency group (#679) 2025-08-21 16:10:03 +02:00
dependabot[bot]
616e870212 Bump mkdocs-material from 9.6.16 to 9.6.17 (#678)
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.6.16 to 9.6.17.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.16...9.6.17)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-version: 9.6.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tek <tek@randhome.io>
2025-08-20 11:13:59 +02:00
Tek
847b0e087b Adds iOS 18.6.1 (#681) 2025-08-20 11:10:20 +02:00
9 changed files with 74 additions and 92 deletions

View File

@@ -23,7 +23,7 @@ install:
python3 -m pip install --upgrade -e . python3 -m pip install --upgrade -e .
test-requirements: test-requirements:
python3 -m pip install --upgrade -r test-requirements.txt python3 -m pip install --upgrade --group dev
generate-proto-parsers: generate-proto-parsers:
# Generate python parsers for protobuf files # Generate python parsers for protobuf files

View File

@@ -1,5 +1,5 @@
mkdocs==1.6.1 mkdocs==1.6.1
mkdocs-autorefs==1.4.2 mkdocs-autorefs==1.4.3
mkdocs-material==9.6.16 mkdocs-material==9.6.20
mkdocs-material-extensions==1.3.1 mkdocs-material-extensions==1.3.1
mkdocstrings==0.30.0 mkdocstrings==0.30.0

View File

@@ -1,13 +1,11 @@
[project] [project]
name = "mvt" name = "mvt"
dynamic = ["version"] dynamic = ["version"]
authors = [ authors = [{ name = "Claudio Guarnieri", email = "nex@nex.sx" }]
{name = "Claudio Guarnieri", email = "nex@nex.sx"}
]
maintainers = [ maintainers = [
{name = "Etienne Maynier", email = "tek@randhome.io"}, { name = "Etienne Maynier", email = "tek@randhome.io" },
{name = "Donncha Ó Cearbhaill", email = "donncha.ocearbhaill@amnesty.org"}, { name = "Donncha Ó Cearbhaill", email = "donncha.ocearbhaill@amnesty.org" },
{name = "Rory Flynn", email = "rory.flynn@amnesty.org"} { name = "Rory Flynn", email = "rory.flynn@amnesty.org" },
] ]
description = "Mobile Verification Toolkit" description = "Mobile Verification Toolkit"
readme = "README.md" readme = "README.md"
@@ -16,7 +14,7 @@ classifiers = [
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Intended Audience :: Information Technology", "Intended Audience :: Information Technology",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Programming Language :: Python" "Programming Language :: Python",
] ]
dependencies = [ dependencies = [
"click==8.2.1", "click==8.2.1",
@@ -45,20 +43,31 @@ homepage = "https://docs.mvt.re/en/latest/"
repository = "https://github.com/mvt-project/mvt" repository = "https://github.com/mvt-project/mvt"
[project.scripts] [project.scripts]
mvt-ios = "mvt.ios:cli" mvt-ios = "mvt.ios:cli"
mvt-android = "mvt.android:cli" mvt-android = "mvt.android:cli"
[dependency-groups]
dev = [
"requests>=2.31.0",
"pytest>=7.4.3",
"pytest-cov>=4.1.0",
"pytest-github-actions-annotate-failures>=0.2.0",
"pytest-mock>=3.14.0",
"stix2>=3.0.1",
"ruff>=0.1.6",
"mypy>=1.7.1",
"betterproto[compiler]",
]
[build-system] [build-system]
requires = ["setuptools>=61.0"] requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.coverage.run] [tool.coverage.run]
omit = [ omit = ["tests/*"]
"tests/*",
]
[tool.coverage.html] [tool.coverage.html]
directory= "htmlcov" directory = "htmlcov"
[tool.mypy] [tool.mypy]
install_types = true install_types = true
@@ -68,15 +77,13 @@ packages = "src"
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report=term-missing:skip-covered" addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report=term-missing:skip-covered"
testpaths = [ testpaths = ["tests"]
"tests"
]
[tool.ruff.lint] [tool.ruff.lint]
select = ["C90", "E", "F", "W"] # flake8 default set select = ["C90", "E", "F", "W"] # flake8 default set
ignore = [ ignore = [
"E501", # don't enforce line length violations "E501", # don't enforce line length violations
"C901", # complex-structure "C901", # complex-structure
# These were previously ignored but don't seem to be required: # These were previously ignored but don't seem to be required:
# "E265", # no-space-after-block-comment # "E265", # no-space-after-block-comment
@@ -88,14 +95,14 @@ ignore = [
] ]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # unused-import "__init__.py" = ["F401"] # unused-import
[tool.ruff.lint.mccabe] [tool.ruff.lint.mccabe]
max-complexity = 10 max-complexity = 10
[tool.setuptools] [tool.setuptools]
include-package-data = true include-package-data = true
package-dir = {"" = "src"} package-dir = { "" = "src" }
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["src"] where = ["src"]
@@ -104,4 +111,4 @@ where = ["src"]
mvt = ["ios/data/*.json"] mvt = ["ios/data/*.json"]
[tool.setuptools.dynamic] [tool.setuptools.dynamic]
version = {attr = "mvt.common.version.MVT_VERSION"} version = { attr = "mvt.common.version.MVT_VERSION" }

View File

@@ -21,22 +21,12 @@ class DumpsysADBArtifact(AndroidArtifact):
stack = [res] stack = [res]
cur_indent = 0 cur_indent = 0
in_multiline = False in_multiline = False
# Normalize line endings to handle both Unix (\n) and Windows (\r\n) for line in dump_data.strip(b"\n").split(b"\n"):
normalized_data = dump_data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
for line in normalized_data.strip(b"\n").split(b"\n"):
# Skip completely empty lines
if not line.strip():
continue
# Track the level of indentation # Track the level of indentation
indent = len(line) - len(line.lstrip()) indent = len(line) - len(line.lstrip())
if indent < cur_indent: if indent < cur_indent:
# If the current line is less indented than the previous one, back out # If the current line is less indented than the previous one, back out
while len(stack) > 1 and indent < cur_indent: stack.pop()
stack.pop()
# Check if we were in multiline mode and need to exit it
if in_multiline and not isinstance(stack[-1], list):
in_multiline = False
cur_indent = indent cur_indent = indent
else: else:
cur_indent = indent cur_indent = indent
@@ -48,30 +38,12 @@ class DumpsysADBArtifact(AndroidArtifact):
# Annoyingly, some values are multiline and don't have a key on each line # Annoyingly, some values are multiline and don't have a key on each line
if in_multiline: if in_multiline:
if key == "" and len(vals) < 2: if key == "":
# If the line is empty, it's the terminator for the multiline value # If the line is empty, it's the terminator for the multiline value
in_multiline = False in_multiline = False
stack.pop() stack.pop()
current_dict = stack[-1]
elif len(vals) >= 2 and (key in self.multiline_fields or key == "}" or vals[1] == b"{"):
# If we encounter a new field while in multiline mode, exit multiline mode
# and process this line as a new field
in_multiline = False
stack.pop()
current_dict = stack[-1]
# Don't continue here - let the line be processed as a new field
else: else:
# When in multiline mode, the top of stack should be a list current_dict.append(line.lstrip())
if isinstance(stack[-1], list):
stack[-1].append(line.lstrip())
else:
# Something went wrong with the stack, exit multiline mode
in_multiline = False
current_dict = stack[-1]
continue
# Skip lines that don't have a value after '='
if len(vals) < 2:
continue continue
if key == "}": if key == "}":
@@ -161,16 +133,7 @@ class DumpsysADBArtifact(AndroidArtifact):
# TODO: Parse AdbDebuggingManager line in output. # TODO: Parse AdbDebuggingManager line in output.
start_of_json = content.find(b"\n{") + 2 start_of_json = content.find(b"\n{") + 2
end_of_json = content.rfind(b"}\n") - 2
# Handle both Unix (\n) and Windows (\r\n) line endings
end_of_json = content.rfind(b"}\n")
if end_of_json == -1:
end_of_json = content.rfind(b"}\r\n")
if end_of_json == -1:
self.log.error("Unable to find end of JSON block in dumpsys output")
return
end_of_json -= 2
json_content = content[start_of_json:end_of_json].rstrip() json_content = content[start_of_json:end_of_json].rstrip()
parsed = self.indented_dump_parser(json_content) parsed = self.indented_dump_parser(json_content)

View File

@@ -107,8 +107,7 @@ class Packages(AndroidExtraction):
result["matched_indicator"] = ioc result["matched_indicator"] = ioc
self.detected.append(result) self.detected.append(result)
@staticmethod def check_virustotal(self, packages: list) -> None:
def check_virustotal(packages: list) -> None:
hashes = [] hashes = []
for package in packages: for package in packages:
for file in package.get("files", []): for file in package.get("files", []):
@@ -143,8 +142,15 @@ class Packages(AndroidExtraction):
for package in packages: for package in packages:
for file in package.get("files", []): for file in package.get("files", []):
row = [package["package_name"], file["path"]] if "package_name" in package:
row = [package["package_name"], file["path"]]
elif "name" in package:
row = [package["name"], file["path"]]
else:
self.log.error(
f"Package {package} has no name or package_name. packages.json or apks.json is malformed"
)
continue
if file["sha256"] in detections: if file["sha256"] in detections:
detection = detections[file["sha256"]] detection = detections[file["sha256"]]
positives = detection.split("/")[0] positives = detection.split("/")[0]

View File

@@ -112,18 +112,10 @@ class Files(AndroidQFModule):
def run(self) -> None: def run(self) -> None:
if timezone := self._get_device_timezone(): if timezone := self._get_device_timezone():
try: device_timezone = zoneinfo.ZoneInfo(timezone)
device_timezone = zoneinfo.ZoneInfo(timezone)
except zoneinfo.ZoneInfoNotFoundError:
self.log.warning("Device timezone '%s' not found, using UTC", timezone)
device_timezone = datetime.timezone.utc
else: else:
self.log.warning("Unable to determine device timezone, using UTC") self.log.warning("Unable to determine device timezone, using UTC")
try: device_timezone = zoneinfo.ZoneInfo("UTC")
device_timezone = zoneinfo.ZoneInfo("UTC")
except zoneinfo.ZoneInfoNotFoundError:
# Fallback for Windows systems where zoneinfo might not have UTC
device_timezone = datetime.timezone.utc
for file in self._get_files_by_pattern("*/files.json"): for file in self._get_files_by_pattern("*/files.json"):
rawdata = self._get_file_content(file).decode("utf-8", errors="ignore") rawdata = self._get_file_content(file).decode("utf-8", errors="ignore")

View File

@@ -654,8 +654,7 @@ class Indicators:
return None return None
for ioc in self.get_iocs("processes"): for ioc in self.get_iocs("processes"):
# Use os-agnostic path splitting to handle both Windows (\) and Unix (/) separators parts = file_path.split("/")
parts = file_path.replace("\\", "/").split("/")
if ioc["value"] in parts: if ioc["value"] in parts:
self.log.warning( self.log.warning(
"Found known suspicious process name mentioned in file at " "Found known suspicious process name mentioned in file at "

View File

@@ -895,6 +895,10 @@
"version": "15.8.4", "version": "15.8.4",
"build": "19H390" "build": "19H390"
}, },
{
"version": "15.8.5",
"build": "19H394"
},
{ {
"build": "20A362", "build": "20A362",
"version": "16.0" "version": "16.0"
@@ -1000,6 +1004,10 @@
"version": "16.7.11", "version": "16.7.11",
"build": "20H360" "build": "20H360"
}, },
{
"version": "16.7.12",
"build": "20H364"
},
{ {
"version": "17.0", "version": "17.0",
"build": "21A327" "build": "21A327"
@@ -1135,5 +1143,21 @@
{ {
"version": "18.6", "version": "18.6",
"build": "22G86" "build": "22G86"
},
{
"version": "18.6.1",
"build": "22G90"
},
{
"version": "18.6.2",
"build": "22G100"
},
{
"version": "18.7",
"build": "22H20"
},
{
"version": "26",
"build": "23A341"
} }
] ]

View File

@@ -1,9 +0,0 @@
requests>=2.31.0
pytest>=7.4.3
pytest-cov>=4.1.0
pytest-github-actions-annotate-failures>=0.2.0
pytest-mock>=3.14.0
stix2>=3.0.1
ruff>=0.1.6
mypy>=1.7.1
betterproto[compiler]