mirror of
https://github.com/mvt-project/mvt.git
synced 2026-09-03 00:21:07 +02:00
Add bugreport mountinfo parser
This commit is contained in:
@@ -117,6 +117,51 @@ class Mounts(AndroidArtifact):
|
||||
# Skip lines that don't match expected format
|
||||
continue
|
||||
|
||||
@staticmethod
|
||||
def parse_mountinfo(entry: str, process_id: int) -> list[dict[str, Any]]:
|
||||
"""Parse Linux /proc/PID/mountinfo records."""
|
||||
results = []
|
||||
for line in entry.splitlines():
|
||||
fields = line.split()
|
||||
if "-" not in fields:
|
||||
continue
|
||||
separator = fields.index("-")
|
||||
if separator < 6 or len(fields) < separator + 4:
|
||||
continue
|
||||
try:
|
||||
mount_id = int(fields[0])
|
||||
parent_id = int(fields[1])
|
||||
except ValueError:
|
||||
continue
|
||||
mount_options = fields[5].split(",")
|
||||
super_options = fields[separator + 3].split(",")
|
||||
options = list(dict.fromkeys(mount_options + super_options))
|
||||
mount_point = fields[4].replace("\\040", " ")
|
||||
device = fields[separator + 2].replace("\\040", " ")
|
||||
filesystem_type = fields[separator + 1]
|
||||
is_system = mount_point in SUSPICIOUS_MOUNT_POINTS or any(
|
||||
mount_point.startswith(f"{prefix}/")
|
||||
for prefix in SUSPICIOUS_MOUNT_POINTS
|
||||
)
|
||||
results.append(
|
||||
{
|
||||
"mount_id": mount_id,
|
||||
"parent_id": parent_id,
|
||||
"major_minor": fields[2],
|
||||
"root": fields[3].replace("\\040", " "),
|
||||
"mount_point": mount_point,
|
||||
"device": device,
|
||||
"filesystem_type": filesystem_type,
|
||||
"mount_options": ",".join(options),
|
||||
"options_list": options,
|
||||
"optional_fields": fields[6:separator],
|
||||
"is_system_partition": is_system,
|
||||
"is_read_write": "rw" in options,
|
||||
"process_ids": [process_id],
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
"""
|
||||
Check for suspicious mount configurations that may indicate root access
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Mobile Verification Toolkit (MVT)
|
||||
# Copyright (c) 2021-2026 The MVT Authors.
|
||||
|
||||
import re
|
||||
|
||||
from mvt.android.artifacts.mounts import Mounts as MountsArtifact
|
||||
|
||||
from .base import BugReportModule
|
||||
|
||||
|
||||
class Mounts(MountsArtifact, BugReportModule):
|
||||
"""Extract and deduplicate process mount namespaces from mountinfo."""
|
||||
|
||||
def run(self) -> None:
|
||||
mount_files = self._get_files_by_pattern("FS/proc/*/mountinfo")
|
||||
if not mount_files:
|
||||
mount_files = self._get_files_by_pattern("*/proc/*/mountinfo")
|
||||
unique: dict[tuple, dict] = {}
|
||||
for file_path in mount_files:
|
||||
pid_match = re.search(r"/proc/(\d+)/mountinfo$", file_path)
|
||||
if not pid_match:
|
||||
continue
|
||||
entries = self.parse_mountinfo(
|
||||
self._get_file_content(file_path).decode("utf-8", errors="replace"),
|
||||
int(pid_match.group(1)),
|
||||
)
|
||||
for entry in entries:
|
||||
identity = (
|
||||
entry["major_minor"],
|
||||
entry["root"],
|
||||
entry["mount_point"],
|
||||
entry["device"],
|
||||
entry["filesystem_type"],
|
||||
entry["mount_options"],
|
||||
)
|
||||
if identity in unique:
|
||||
unique[identity]["process_ids"].extend(entry["process_ids"])
|
||||
else:
|
||||
unique[identity] = entry
|
||||
self.results = list(unique.values())
|
||||
self.log.info("Extracted %d unique mount records", len(self.results))
|
||||
@@ -13,6 +13,19 @@ from ..utils import get_android_androidqf, list_files
|
||||
|
||||
|
||||
class TestAndroidqfMountsArtifact:
|
||||
def test_parse_proc_mountinfo(self):
|
||||
from mvt.android.artifacts.mounts import Mounts as MountsArtifact
|
||||
|
||||
results = MountsArtifact.parse_mountinfo(
|
||||
"41 40 254:13 / / ro,relatime shared:1 - erofs /dev/block/dm-13 ro,seclabel\n",
|
||||
123,
|
||||
)
|
||||
|
||||
assert results[0]["mount_id"] == 41
|
||||
assert results[0]["mount_point"] == "/"
|
||||
assert results[0]["filesystem_type"] == "erofs"
|
||||
assert results[0]["process_ids"] == [123]
|
||||
|
||||
def test_parse_mounts_token_checks(self):
|
||||
"""
|
||||
Test the artifact-level `parse` method using tolerant token checks.
|
||||
|
||||
Reference in New Issue
Block a user