diff --git a/mvt/android/modules/bugreport/__init__.py b/mvt/android/modules/bugreport/__init__.py index 2a30aef..6982ee8 100644 --- a/mvt/android/modules/bugreport/__init__.py +++ b/mvt/android/modules/bugreport/__init__.py @@ -12,6 +12,8 @@ from .dbinfo import DBInfo from .getprop import Getprop from .packages import Packages from .receivers import Receivers +from .network_interfaces import NetworkInterfaces BUGREPORT_MODULES = [Accessibility, Activities, Appops, BatteryDaily, - BatteryHistory, DBInfo, Getprop, Packages, Receivers] + BatteryHistory, DBInfo, Getprop, Packages, Receivers, + NetworkInterfaces] diff --git a/mvt/android/modules/bugreport/network_interfaces.py b/mvt/android/modules/bugreport/network_interfaces.py new file mode 100644 index 0000000..0b9ae1c --- /dev/null +++ b/mvt/android/modules/bugreport/network_interfaces.py @@ -0,0 +1,58 @@ +# 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 typing import Optional + +from mvt.android.parsers import parse_dumpsys_network_interfaces + +from .base import BugReportModule + + +class NetworkInterfaces(BugReportModule): + """This module extracts network interfaces from 'ip link' command.""" + + def __init__( + self, + file_path: Optional[str] = None, + target_path: Optional[str] = None, + results_path: Optional[str] = None, + fast_mode: Optional[bool] = False, + log: logging.Logger = logging.getLogger(__name__), + results: Optional[list] = None + ) -> None: + super().__init__(file_path=file_path, target_path=target_path, + results_path=results_path, fast_mode=fast_mode, + log=log, results=results) + + self.results = {} if not results else results + + def run(self) -> None: + content = self._get_dumpstate_file() + if not content: + self.log.error("Unable to find dumpstate file. " + "Did you provide a valid bug report archive?") + return + + lines = [] + in_getprop = False + + for line in content.decode(errors="ignore").splitlines(): + if line.strip().startswith("------ NETWORK INTERFACES"): + in_getprop = True + continue + + if not in_getprop: + continue + + if line.strip().startswith("------"): + break + + lines.append(line) + + self.results = parse_dumpsys_network_interfaces("\n".join(lines)) + + self.log.info("Extracted information about %d Android network interfaces", + len(self.results)) diff --git a/mvt/android/parsers/__init__.py b/mvt/android/parsers/__init__.py index b83e0c7..b01d3b8 100644 --- a/mvt/android/parsers/__init__.py +++ b/mvt/android/parsers/__init__.py @@ -7,5 +7,7 @@ from .dumpsys import (parse_dumpsys_accessibility, parse_dumpsys_activity_resolver_table, parse_dumpsys_appops, parse_dumpsys_battery_daily, parse_dumpsys_battery_history, parse_dumpsys_dbinfo, - parse_dumpsys_receiver_resolver_table) + parse_dumpsys_receiver_resolver_table, + parse_dumpsys_network_interfaces, + ) from .getprop import parse_getprop diff --git a/mvt/android/parsers/dumpsys.py b/mvt/android/parsers/dumpsys.py index 04a3a66..d57c1a7 100644 --- a/mvt/android/parsers/dumpsys.py +++ b/mvt/android/parsers/dumpsys.py @@ -519,3 +519,39 @@ def parse_dumpsys_packages(output: str) -> List[Dict[str, Any]]: results.append(package) return results + + +def parse_dumpsys_network_interfaces(output: str) -> List[Dict[str, str]]: + """ + Parse network interfaces (output of the 'ip link' command) + """ + results = [] + interface_rxp = re.compile(r"(?P\d+): (?P[\S\d]+): (?P\<.*)") + mac_or_ip_line_rxp = re.compile(r"\W+ (?P[\S]+) (?P[a-f0-9\:\.\/]+) (.*)") + + interface = None + for line in output.splitlines(): + + interface_match = re.match(interface_rxp, line) + if interface_match: + interface = { + "interface_number": interface_match.group("if_number"), + "name": interface_match.group("if_name"), + "options": interface_match.group("if_options"), + } + continue + + elif interface: + mac_line_match = re.match(mac_or_ip_line_rxp, line) + mac_or_ip_address = mac_line_match.group("mac_or_ip_address") + if len(mac_or_ip_address) == 17: + interface["mac_address"] = mac_or_ip_address + else: + interface["address"] = mac_or_ip_address + interface["link_type"] = mac_line_match.group("link_type") + interface["link_line"] = line + + results.append(interface) + interface = None + + return results