1
mirror of https://github.com/mvt-project/mvt synced 2025-10-21 22:42:15 +02:00

Compare commits

...

53 Commits

Author SHA1 Message Date
Nex
99e14ad8b0 Bumped version 2022-11-13 01:11:52 +01:00
tek
deaa68a2e0 Adds iOS 16.1.1 in iOS versions 2022-11-11 12:11:46 +01:00
tek
07f819bf5f Adds new iPhone hardware 2022-11-02 10:41:33 +01:00
tek
51fdfce7f4 Adds iOS 16.1 to iOS versions 2022-10-31 11:17:25 +01:00
Nex
41e05a107e Merge branch 'main' of github.com:mvt-project/mvt 2022-10-15 11:26:55 +02:00
Nex
e559fb223b Upgraded dependencies 2022-10-15 11:26:40 +02:00
Nex
b69bb92f3d Merge pull request #279 from Niek/main
Dockerfile improvements, support arm64 builds
2022-10-15 11:14:40 +02:00
Nex
42e8e41b7d Merge branch 'besendorf-patch-1' 2022-10-15 11:11:57 +02:00
Nex
00b7314395 Added quotes 2022-10-15 11:11:47 +02:00
Nex
39a8bf236d Merge branch 'patch-1' of github.com:besendorf/mvt into besendorf-patch-1 2022-10-15 11:11:29 +02:00
tek
d268b17284 Adds missing module in androidqf module list 2022-10-14 15:01:08 +02:00
tek
66c015bc23 Improves check-androidqf tests 2022-10-11 13:07:24 +02:00
tek
ba0106c476 Adds SMS androidqf module and improves tests 2022-10-11 12:41:42 +02:00
tek
41826d7951 Fixes PEP8 syntax issue 2022-10-05 15:30:39 +02:00
Nex
4e0a393a02 Bumped version 2022-10-01 12:40:04 +02:00
Tek
c3dc4174fc Adds detection for disabled security packages in Android (#306)
* Adds detection for disabled security packages in Android

* Update detection of disabled security packages
2022-09-26 12:17:09 +02:00
tek
e1d1b6c5de Fixes a minor issue in the iOS manifest module 2022-09-26 12:07:52 +02:00
tek
d0a893841b Adds new iOS versions 2022-09-12 23:49:33 +02:00
Nex
d4e99661c7 Merge pull request #300 from andefined/fix-idstatuscache-error
Fixed missing root_paths check for ios/idstatuscache module
2022-09-07 09:29:09 +02:00
Nex
6a00d3a14d Closing handle to ZipFile 2022-09-05 12:21:11 +02:00
Nex
a863209abb Added check-androidqf command 2022-09-05 12:12:36 +02:00
Nex
4c7db02da4 Bumped version 2022-09-01 09:42:03 +02:00
Nex
92dfefbdeb Added some support for patterns in backups' relative paths 2022-08-31 19:34:59 +02:00
Nex
8988adcf77 Warnings should be reserved for detections ideally 2022-08-25 17:22:24 +02:00
andefined
91667b0ded Fixed missing root_paths check for ios/idstatuscache module 2022-08-24 18:54:45 +03:00
tek
2365175dbd Adds check of process name in paths in indicators 2022-08-23 13:18:42 +02:00
Nex
528d43b914 Merge branch 'main' of github.com:mvt-project/mvt 2022-08-22 21:13:22 +02:00
Nex
f952ba5119 Removed comment with odd char 2022-08-22 21:12:59 +02:00
besendorf
d61b2751f1 Add pip command for update
Adds the pip comman for updating mvt. I think this would be helpfull for novice users as it already has been asked here: https://github.com/mvt-project/mvt/discussions/261
Also I sometimes forget the command too ;)
2022-08-22 12:20:56 +02:00
Nex
b4ed2c6ed4 Added commented backup ID 2022-08-22 10:40:36 +02:00
Nex
3eed1d6edf Sorted imports 2022-08-22 10:30:58 +02:00
Nex
83ef545cd1 Merge pull request #298 from jons44/patch-1
Fixed idevicebackup2 syntax
2022-08-20 16:29:57 +02:00
jons44
5d4fbec62b Fixed idevicebackup2 syntax 2022-08-19 19:34:12 +02:00
Nex
fa7d6166f4 Removed legacy print 2022-08-19 15:19:46 +02:00
Nex
429b223555 Bumped version 2022-08-18 18:31:32 +02:00
tek
e4b9a9652a Adds ios 15.6.1 version 2022-08-18 14:42:26 +02:00
Nex
134581c000 Merge pull request #297 from mvt-project/feature/dumpsys-packages-parsing
Improves Android dumpsys package parsing
2022-08-18 13:58:59 +02:00
tek
5356a399c9 Moves dumpsys parsing to android parsers and use the same parser for adb and bugreport modules 2022-08-17 18:24:51 +02:00
Nex
e0f563596d Setting a default value for list of ioc files in case none was specified 2022-08-17 15:58:53 +02:00
Nex
ea5de0203a Changed default for Optional[str] 2022-08-17 15:52:17 +02:00
Nex
ace965ee8a Changed default value for optional lists to None 2022-08-17 15:37:12 +02:00
Nex
ad8f455209 Sorted imports 2022-08-17 11:34:58 +02:00
tek
ae67b41374 Merge branch 'main' of github.com:mvt-project/mvt 2022-08-16 18:57:37 +02:00
tek
5fe88098b9 Improves dumpsys battery history parsing 2022-08-16 18:57:18 +02:00
Nex
d578c240f9 Added additional missing space in inline comment 2022-08-16 18:26:34 +02:00
Nex
427a29c2b6 Pylint notes to ignore some lines too long 2022-08-16 16:09:59 +02:00
Nex
5e6f6faa9c Sorted imports 2022-08-16 16:02:32 +02:00
Nex
74a3ecaa4e Linted code 2022-08-16 16:02:17 +02:00
Nex
f536af1124 Not using bare except and removed unused var 2022-08-16 15:55:29 +02:00
Nex
631354c131 Properly checking any potential domains in Manifest.db records (fixes: #293) 2022-08-16 15:40:28 +02:00
Nex
7ad7782b51 Merge branch 'main' of github.com:mvt-project/mvt 2022-08-16 13:40:14 +02:00
Nex
f04f91e1e3 Improved type hints and code style enforcement 2022-08-16 13:39:55 +02:00
Niek van der Maas
067402831a Dockerfile improvements, support arm64 builds 2022-06-02 09:22:07 +02:00
132 changed files with 3188 additions and 896 deletions

View File

@@ -16,4 +16,4 @@ When contributing code to
- **Quotes**: we use double quotes (`"`) as a default. Single quotes (`'`) can be favored with nested strings instead of escaping (`\"`), or when using f-formatting.
- **Maximum line length**: we strongly encourage to respect a 80 characters long lines and to follow [PEP8 indentation guidelines](https://peps.python.org/pep-0008/#indentation) when having to wrap. However, if breaking at 80 is not possible or is detrimental to the readability of the code, exceptions are tolerated so long as they remain within a hard maximum length of 100 characters.
- **Maximum line length**: we strongly encourage to respect a 80 characters long lines and to follow [PEP8 indentation guidelines](https://peps.python.org/pep-0008/#indentation) when having to wrap. However, if breaking at 80 is not possible or is detrimental to the readability of the code, exceptions are tolerated. For example, long log lines, or long strings can be extended to 100 characters long. Please hard wrap anything beyond 100 characters.

View File

@@ -1,4 +1,4 @@
FROM ubuntu:20.04
FROM ubuntu:22.04
# Ref. https://github.com/mvt-project/mvt
@@ -7,13 +7,12 @@ LABEL vcs-url="https://github.com/mvt-project/mvt"
LABEL description="MVT is a forensic tool to look for signs of infection in smartphone devices."
ENV PIP_NO_CACHE_DIR=1
ENV DEBIAN_FRONTEND=noninteractive
# Fixing major OS dependencies
# ----------------------------
RUN apt update \
&& apt install -y python3 python3-pip libusb-1.0-0-dev \
&& apt install -y wget unzip\
&& DEBIAN_FRONTEND=noninteractive apt-get -y install default-jre-headless \
&& apt install -y python3 python3-pip libusb-1.0-0-dev wget unzip default-jre-headless adb \
# Install build tools for libimobiledevice
# ----------------------------------------
@@ -67,18 +66,9 @@ RUN mkdir /opt/abe \
# Create alias for abe
&& echo 'alias abe="java -jar /opt/abe/abe.jar"' >> ~/.bashrc
# Install Android Platform Tools
# ------------------------------
RUN mkdir /opt/android \
&& wget -q https://dl.google.com/android/repository/platform-tools-latest-linux.zip \
&& unzip platform-tools-latest-linux.zip -d /opt/android \
# Create alias for adb
&& echo 'alias adb="/opt/android/platform-tools/adb"' >> ~/.bashrc
# Generate adb key folder
# ------------------------------
RUN mkdir /root/.android && /opt/android/platform-tools/adb keygen /root/.android/adbkey
RUN mkdir /root/.android && adb keygen /root/.android/adbkey
# Setup investigations environment
# --------------------------------

View File

@@ -1,4 +1,4 @@
Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed.
Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed. Note that this requires a Linux host, as Docker for Windows and Mac [doesn't support passing through USB devices](https://docs.docker.com/desktop/faqs/#can-i-pass-through-a-usb-device-to-a-container).
Install Docker following the [official documentation](https://docs.docker.com/get-docker/).
@@ -10,11 +10,6 @@ cd mvt
docker build -t mvt .
```
Optionally, you may need to specify your platform to Docker in order to build successfully (Apple M1)
```bash
docker build --platform amd64 -t mvt .
```
Test if the image was created successfully:
```bash

View File

@@ -3,10 +3,10 @@
If you have correctly [installed libimobiledevice](../install.md) you can easily generate an iTunes backup using the `idevicebackup2` tool included in the suite. First, you might want to ensure that backup encryption is enabled (**note: encrypted backup contain more data than unencrypted backups**):
```bash
idevicebackup2 -i backup encryption on
idevicebackup2 -i encryption on
```
Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i backup changepw`, or by turning off encryption (`idevicebackup2 -i backup encryption off`) and turning it back on again.
Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i changepw`, or by turning off encryption (`idevicebackup2 -i encryption off`) and turning it back on again.
If you are not able to recover or change the password, you should try to disable encryption and obtain an unencrypted backup.

View File

@@ -16,6 +16,7 @@ from mvt.common.logo import logo
from mvt.common.updates import IndicatorsUpdates
from .cmd_check_adb import CmdAndroidCheckADB
from .cmd_check_androidqf import CmdAndroidCheckAndroidQF
from .cmd_check_backup import CmdAndroidCheckBackup
from .cmd_check_bugreport import CmdAndroidCheckBugreport
from .cmd_download_apks import DownloadAPKs
@@ -121,9 +122,9 @@ def check_adb(ctx, serial, iocs, output, fast, list_modules, module):
cmd.run()
if len(cmd.timeline_detected) > 0:
if cmd.detected_count > 0:
log.warning("The analysis of the Android device produced %d detections!",
len(cmd.timeline_detected))
cmd.detected_count)
#==============================================================================
@@ -151,9 +152,9 @@ def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path):
cmd.run()
if len(cmd.timeline_detected) > 0:
if cmd.detected_count > 0:
log.warning("The analysis of the Android bug report produced %d detections!",
len(cmd.timeline_detected))
cmd.detected_count)
#==============================================================================
@@ -179,9 +180,39 @@ def check_backup(ctx, iocs, output, list_modules, backup_path):
cmd.run()
if len(cmd.timeline_detected) > 0:
if cmd.detected_count > 0:
log.warning("The analysis of the Android backup produced %d detections!",
len(cmd.timeline_detected))
cmd.detected_count)
#==============================================================================
# Command: check-androidqf
#==============================================================================
@cli.command("check-androidqf", help="Check data collected with AndroidQF")
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
default=[], help=HELP_MSG_IOC)
@click.option("--output", "-o", type=click.Path(exists=False),
help=HELP_MSG_OUTPUT)
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.argument("ANDROIDQF_PATH", type=click.Path(exists=True))
@click.pass_context
def check_androidqf(ctx, iocs, output, list_modules, module, androidqf_path):
cmd = CmdAndroidCheckAndroidQF(target_path=androidqf_path,
results_path=output, ioc_files=iocs,
module_name=module)
if list_modules:
cmd.list_modules()
return
log.info("Checking AndroidQF acquisition at path: %s", androidqf_path)
cmd.run()
if cmd.detected_count > 0:
log.warning("The analysis of the AndroidQF acquisition produced %d detections!",
cmd.detected_count)
#==============================================================================

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.common.command import Command
@@ -14,9 +15,15 @@ log = logging.getLogger(__name__)
class CmdAndroidCheckADB(Command):
def __init__(self, target_path: str = None, results_path: str = None,
ioc_files: list = [], module_name: str = None,
serial: str = None, fast_mode: bool = False):
def __init__(
self,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
ioc_files: Optional[list] = None,
module_name: Optional[str] = None,
serial: Optional[str] = None,
fast_mode: Optional[bool] = False,
) -> None:
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)

View File

@@ -0,0 +1,32 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 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.common.command import Command
from .modules.androidqf import ANDROIDQF_MODULES
log = logging.getLogger(__name__)
class CmdAndroidCheckAndroidQF(Command):
def __init__(
self,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
ioc_files: Optional[list] = None,
module_name: Optional[str] = None,
serial: Optional[str] = None,
fast_mode: Optional[bool] = False,
) -> None:
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)
self.name = "check-androidqf"
self.modules = ANDROIDQF_MODULES

View File

@@ -9,7 +9,7 @@ import os
import sys
import tarfile
from pathlib import Path
from typing import Callable
from typing import Callable, Optional
from rich.prompt import Prompt
@@ -25,9 +25,15 @@ log = logging.getLogger(__name__)
class CmdAndroidCheckBackup(Command):
def __init__(self, target_path: str = None, results_path: str = None,
ioc_files: list = [], module_name: str = None,
serial: str = None, fast_mode: bool = False):
def __init__(
self,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
ioc_files: Optional[list] = None,
module_name: Optional[str] = None,
serial: Optional[str] = None,
fast_mode: Optional[bool] = False,
) -> None:
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)

View File

@@ -6,7 +6,7 @@
import logging
import os
from pathlib import Path
from typing import Callable
from typing import Callable, Optional
from zipfile import ZipFile
from mvt.common.command import Command
@@ -18,9 +18,15 @@ log = logging.getLogger(__name__)
class CmdAndroidCheckBugreport(Command):
def __init__(self, target_path: str = None, results_path: str = None,
ioc_files: list = [], module_name: str = None,
serial: str = None, fast_mode: bool = False):
def __init__(
self,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
ioc_files: Optional[list] = None,
module_name: Optional[str] = None,
serial: Optional[str] = None,
fast_mode: Optional[bool] = False,
) -> None:
super().__init__(target_path=target_path, results_path=results_path,
ioc_files=ioc_files, module_name=module_name,
serial=serial, fast_mode=fast_mode, log=log)
@@ -52,3 +58,7 @@ class CmdAndroidCheckBugreport(Command):
module.from_zip(self.bugreport_archive, self.bugreport_files)
else:
module.from_folder(self.target_path, self.bugreport_files)
def finish(self) -> None:
if self.bugreport_archive:
self.bugreport_archive.close()

View File

@@ -6,7 +6,7 @@
import json
import logging
import os
from typing import Callable
from typing import Callable, Optional
from rich.progress import track
@@ -25,8 +25,12 @@ class DownloadAPKs(AndroidExtraction):
"""
def __init__(self, results_path: str = "", all_apks: bool = False,
packages: list = []):
def __init__(
self,
results_path: Optional[str] = None,
all_apks: Optional[bool] = False,
packages: Optional[list] = None
) -> None:
"""Initialize module.
:param results_path: Path to the folder where data should be stored
:param all_apks: Boolean indicating whether to download all packages
@@ -78,9 +82,8 @@ class DownloadAPKs(AndroidExtraction):
try:
self._adb_download(remote_path, local_path)
except InsufficientPrivileges:
log.error("Unable to pull package file from %s: insufficient "
"privileges, it might be a system app",
remote_path)
log.error("Unable to pull package file from %s: insufficient privileges, "
"it might be a system app", remote_path)
self._adb_reconnect()
return None
except Exception as exc:
@@ -122,8 +125,8 @@ class DownloadAPKs(AndroidExtraction):
if not package.get("system", False):
packages_selection.append(package)
log.info("Selected only %d packages which are not marked as "
"\"system\"", len(packages_selection))
log.info("Selected only %d packages which are not marked as \"system\"",
len(packages_selection))
if len(packages_selection) == 0:
log.info("No packages were selected for download")

View File

@@ -11,7 +11,7 @@ import string
import sys
import tempfile
import time
from typing import Callable
from typing import Callable, Optional
from adb_shell.adb_device import AdbDeviceTcp, AdbDeviceUsb
from adb_shell.auth.keygen import keygen, write_public_keyfile
@@ -32,10 +32,15 @@ ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub")
class AndroidExtraction(MVTModule):
"""This class provides a base for all Android extraction modules."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -73,15 +78,13 @@ class AndroidExtraction(MVTModule):
try:
self.device = AdbDeviceUsb(serial=self.serial)
except UsbDeviceNotFoundError:
self.log.critical("No device found. Make sure it is connected "
"and unlocked.")
self.log.critical("No device found. Make sure it is connected and unlocked.")
sys.exit(-1)
# Otherwise we try to use the TCP transport.
else:
addr = self.serial.split(":")
if len(addr) < 2:
raise ValueError("TCP serial number must follow the format: "
"`address:port`")
raise ValueError("TCP serial number must follow the format: `address:port`")
self.device = AdbDeviceTcp(addr[0], int(addr[1]),
default_transport_timeout_s=30.)
@@ -90,12 +93,11 @@ class AndroidExtraction(MVTModule):
try:
self.device.connect(rsa_keys=[signer], auth_timeout_s=5)
except (USBErrorBusy, USBErrorAccess):
self.log.critical("Device is busy, maybe run `adb kill-server` "
"and try again.")
self.log.critical("Device is busy, maybe run `adb kill-server` and try again.")
sys.exit(-1)
except DeviceAuthError:
self.log.error("You need to authorize this computer on the "
"Android device. Retrying in 5 seconds...")
self.log.error("You need to authorize this computer on the Android device. "
"Retrying in 5 seconds...")
time.sleep(5)
except UsbReadFailedError:
self.log.error("Unable to connect to the device over USB. "
@@ -104,7 +106,7 @@ class AndroidExtraction(MVTModule):
except OSError as exc:
if exc.errno == 113 and self.serial:
self.log.critical("Unable to connect to the device %s: "
"did you specify the correct IP addres?",
"did you specify the correct IP address?",
self.serial)
sys.exit(-1)
else:
@@ -169,9 +171,13 @@ class AndroidExtraction(MVTModule):
return bool(self._adb_command_as_root(f"[ ! -f {file} ] || echo 1"))
def _adb_download(self, remote_path: str, local_path: str,
progress_callback: Callable = None,
retry_root: bool = True) -> None:
def _adb_download(
self,
remote_path: str,
local_path: str,
progress_callback: Optional[Callable] = None,
retry_root: Optional[bool] = True
) -> None:
"""Download a file form the device.
:param remote_path: Path to download from the device
@@ -190,8 +196,12 @@ class AndroidExtraction(MVTModule):
else:
raise Exception(f"Unable to download file {remote_path}: {exc}") from exc
def _adb_download_root(self, remote_path: str, local_path: str,
progress_callback: Callable = None) -> None:
def _adb_download_root(
self,
remote_path: str,
local_path: str,
progress_callback: Optional[Callable] = None
) -> None:
try:
# Check if we have root, if not raise an Exception.
self._adb_root_or_die()
@@ -262,8 +272,8 @@ class AndroidExtraction(MVTModule):
self._adb_command(f"rm -f {new_remote_path}")
def _generate_backup(self, package_name: str) -> bytes:
self.log.warning("Please check phone and accept Android backup prompt. "
"You may need to set a backup password. \a")
self.log.info("Please check phone and accept Android backup prompt. "
"You may need to set a backup password. \a")
# TODO: Base64 encoding as temporary fix to avoid byte-mangling over
# the shell transport...
@@ -288,10 +298,9 @@ class AndroidExtraction(MVTModule):
backup_password)
return decrypted_backup_tar
except InvalidBackupPassword:
self.log.error("You provided the wrong password! "
"Please try again...")
self.log.error("You provided the wrong password! Please try again...")
self.log.warn("All attempts to decrypt backup with password failed!")
self.log.error("All attempts to decrypt backup with password failed!")
return None

View File

@@ -6,7 +6,7 @@
import logging
import os
import sqlite3
from typing import Union
from typing import Optional, Union
from mvt.common.utils import (convert_chrometime_to_datetime,
convert_datetime_to_iso)
@@ -19,10 +19,15 @@ CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History"
class ChromeHistory(AndroidExtraction):
"""This module extracts records from Android's Chrome browsing history."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -70,7 +75,8 @@ class ChromeHistory(AndroidExtraction):
"url": item[1],
"visit_id": item[2],
"timestamp": item[3],
"isodate": convert_datetime_to_iso(convert_chrometime_to_datetime(item[3])),
"isodate": convert_datetime_to_iso(
convert_chrometime_to_datetime(item[3])),
"redirect_source": item[4],
})

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_accessibility
@@ -13,10 +14,15 @@ from .base import AndroidExtraction
class DumpsysAccessibility(AndroidExtraction):
"""This module extracts stats on accessibility."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_activity_resolver_table
@@ -13,10 +14,15 @@ from .base import AndroidExtraction
class DumpsysActivities(AndroidExtraction):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,7 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Union
from typing import Optional, Union
from mvt.android.parsers.dumpsys import parse_dumpsys_appops
@@ -16,10 +16,15 @@ class DumpsysAppOps(AndroidExtraction):
slug = "dumpsys_appops"
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,7 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Union
from typing import Optional, Union
from mvt.android.parsers import parse_dumpsys_battery_daily
@@ -14,10 +14,15 @@ from .base import AndroidExtraction
class DumpsysBatteryDaily(AndroidExtraction):
"""This module extracts records from battery daily updates."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -49,4 +54,5 @@ class DumpsysBatteryDaily(AndroidExtraction):
self.results = parse_dumpsys_battery_daily(output)
self.log.info("Extracted %d records from battery daily stats", len(self.results))
self.log.info("Extracted %d records from battery daily stats",
len(self.results))

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_battery_history
@@ -13,10 +14,15 @@ from .base import AndroidExtraction
class DumpsysBatteryHistory(AndroidExtraction):
"""This module extracts records from battery history events."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_dbinfo
@@ -15,10 +16,15 @@ class DumpsysDBInfo(AndroidExtraction):
slug = "dumpsys_dbinfo"
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -5,6 +5,7 @@
import logging
import os
from typing import Optional
from .base import AndroidExtraction
@@ -12,10 +13,15 @@ from .base import AndroidExtraction
class DumpsysFull(AndroidExtraction):
"""This module extracts stats on battery consumption by processes."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_receiver_resolver_table
@@ -19,10 +20,15 @@ INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
class DumpsysReceivers(AndroidExtraction):
"""This module extracts details on receivers for risky activities."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -36,24 +42,20 @@ class DumpsysReceivers(AndroidExtraction):
for intent, receivers in self.results.items():
for receiver in receivers:
if intent == INTENT_NEW_OUTGOING_SMS:
self.log.info("Found a receiver to intercept "
"outgoing SMS messages: \"%s\"",
self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"",
receiver["receiver"])
elif intent == INTENT_SMS_RECEIVED:
self.log.info("Found a receiver to intercept "
"incoming SMS messages: \"%s\"",
self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"",
receiver["receiver"])
elif intent == INTENT_DATA_SMS_RECEIVED:
self.log.info("Found a receiver to intercept "
"incoming data SMS message: \"%s\"",
self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"",
receiver["receiver"])
elif intent == INTENT_PHONE_STATE:
self.log.info("Found a receiver monitoring "
"telephony state/incoming calls: \"%s\"",
receiver["receiver"])
elif intent == INTENT_NEW_OUTGOING_CALL:
self.log.info("Found a receiver monitoring "
"outgoing calls: \"%s\"",
self.log.info("Found a receiver monitoring outgoing calls: \"%s\"",
receiver["receiver"])
ioc = self.indicators.check_app_id(receiver["package_name"])

View File

@@ -6,7 +6,7 @@
import logging
import os
import stat
from typing import Union
from typing import Optional, Union
from mvt.common.utils import convert_unix_to_iso
@@ -25,10 +25,15 @@ ANDROID_MEDIA_FOLDERS = [
class Files(AndroidExtraction):
"""This module extracts the list of files on the device."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -48,8 +53,8 @@ class Files(AndroidExtraction):
def check_indicators(self) -> None:
for result in self.results:
if result.get("is_suid"):
self.log.warning("Found an SUID file in a non-standard "
"directory \"%s\".", result["path"])
self.log.warning("Found an SUID file in a non-standard directory \"%s\".",
result["path"])
if self.indicators and self.indicators.check_file_path(result["path"]):
self.log.warning("Found a known suspicous file at path: \"%s\"",
@@ -124,8 +129,7 @@ class Files(AndroidExtraction):
if self.fast_mode:
self.log.info("Flag --fast was enabled: skipping full file listing")
else:
self.log.info("Processing full file listing. "
"This may take a while...")
self.log.info("Processing full file listing. This may take a while...")
self.find_files("/")
self.log.info("Found %s total files", len(self.results))

View File

@@ -5,6 +5,7 @@
import logging
from datetime import datetime, timedelta
from typing import Optional
from mvt.android.parsers import parse_getprop
@@ -14,10 +15,15 @@ from .base import AndroidExtraction
class Getprop(AndroidExtraction):
"""This module extracts device properties from getprop command."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -5,6 +5,7 @@
import logging
import os
from typing import Optional
from .base import AndroidExtraction
@@ -12,10 +13,15 @@ from .base import AndroidExtraction
class Logcat(AndroidExtraction):
"""This module extracts details on installed packages."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,13 +4,14 @@
# https://license.mvt.re/1.1/
import logging
from typing import Union
from typing import Optional, Union
from rich.console import Console
from rich.progress import track
from rich.table import Table
from rich.text import Text
from mvt.android.parsers.dumpsys import parse_dumpsys_package_for_details
from mvt.common.virustotal import VTNoKey, VTQuotaExceeded, virustotal_lookup
from .base import AndroidExtraction
@@ -38,7 +39,6 @@ DANGEROUS_PERMISSIONS = [
"android.permission.USE_SIP",
"com.android.browser.permission.READ_HISTORY_BOOKMARKS",
]
ROOT_PACKAGES = [
"com.noshufou.android.su",
"com.noshufou.android.su.elite",
@@ -66,15 +66,37 @@ ROOT_PACKAGES = [
"com.kingouser.com",
"com.topjohnwu.magisk",
]
SECURITY_PACKAGES = [
"com.policydm",
"com.samsung.android.app.omcagent",
"com.samsung.android.securitylogagent",
"com.sec.android.soagent",
]
SYSTEM_UPDATE_PACKAGES = [
"com.android.updater",
"com.google.android.gms",
"com.huawei.android.hwouc",
"com.lge.lgdmsclient",
"com.motorola.ccc.ota",
"com.oneplus.opbackup",
"com.oppo.ota",
"com.transsion.systemupdate",
"com.wssyncmldm",
]
class Packages(AndroidExtraction):
"""This module extracts the list of installed packages."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -117,6 +139,14 @@ class Packages(AndroidExtraction):
self.detected.append(result)
continue
if result["package_name"] in SECURITY_PACKAGES and result["disabled"]:
self.log.warning("Found a security package disabled: \"%s\"",
result["package_name"])
if result["package_name"] in SYSTEM_UPDATE_PACKAGES and result["disabled"]:
self.log.warning("System OTA update package \"%s\" disabled on the phone",
result["package_name"])
if not self.indicators:
continue
@@ -187,43 +217,17 @@ class Packages(AndroidExtraction):
@staticmethod
def parse_package_for_details(output: str) -> dict:
details = {
"uid": "",
"version_name": "",
"version_code": "",
"timestamp": "",
"first_install_time": "",
"last_update_time": "",
"requested_permissions": [],
}
in_permissions = False
lines = []
in_packages = False
for line in output.splitlines():
if in_permissions:
if line.startswith(" " * 4) and not line.startswith(" " * 6):
in_permissions = False
continue
if in_packages:
if line.strip() == "":
break
lines.append(line)
if line.strip() == "Packages:":
in_packages = True
permission = line.strip().split(":")[0]
details["requested_permissions"].append(permission)
if line.strip().startswith("userId="):
details["uid"] = line.split("=")[1].strip()
elif line.strip().startswith("versionName="):
details["version_name"] = line.split("=")[1].strip()
elif line.strip().startswith("versionCode="):
details["version_code"] = line.split("=", 1)[1].strip()
elif line.strip().startswith("timeStamp="):
details["timestamp"] = line.split("=")[1].strip()
elif line.strip().startswith("firstInstallTime="):
details["first_install_time"] = line.split("=")[1].strip()
elif line.strip().startswith("lastUpdateTime="):
details["last_update_time"] = line.split("=")[1].strip()
elif line.strip() == "requested permissions:":
in_permissions = True
continue
return details
return parse_dumpsys_package_for_details("\n".join(lines))
def _get_files_for_package(self, package_name: str) -> list:
output = self._adb_command(f"pm path {package_name}")
@@ -235,10 +239,14 @@ class Packages(AndroidExtraction):
for file_path in output.splitlines():
file_path = file_path.strip()
md5 = self._adb_command(f"md5sum {file_path}").split(" ", maxsplit=1)[0]
sha1 = self._adb_command(f"sha1sum {file_path}").split(" ", maxsplit=1)[0]
sha256 = self._adb_command(f"sha256sum {file_path}").split(" ", maxsplit=1)[0]
sha512 = self._adb_command(f"sha512sum {file_path}").split(" ", maxsplit=1)[0]
md5 = self._adb_command(
f"md5sum {file_path}").split(" ", maxsplit=1)[0]
sha1 = self._adb_command(
f"sha1sum {file_path}").split(" ", maxsplit=1)[0]
sha256 = self._adb_command(
f"sha256sum {file_path}").split(" ", maxsplit=1)[0]
sha512 = self._adb_command(
f"sha512sum {file_path}").split(" ", maxsplit=1)[0]
package_files.append({
"path": file_path,
@@ -282,7 +290,8 @@ class Packages(AndroidExtraction):
"files": package_files,
}
dumpsys_package = self._adb_command(f"dumpsys package {package_name}")
dumpsys_package = self._adb_command(
f"dumpsys package {package_name}")
package_details = self.parse_package_for_details(dumpsys_package)
new_package.update(package_details)
@@ -326,9 +335,9 @@ class Packages(AndroidExtraction):
continue
packages_to_lookup.append(result)
self.log.info("Found non-system package with name \"%s\" installed "
"by \"%s\" on %s", result["package_name"],
result["installer"], result["timestamp"])
self.log.info("Found non-system package with name \"%s\" installed by \"%s\" on %s",
result["package_name"], result["installer"],
result["timestamp"])
if not self.fast_mode:
self.check_virustotal(packages_to_lookup)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from .base import AndroidExtraction
@@ -11,10 +12,15 @@ from .base import AndroidExtraction
class Processes(AndroidExtraction):
"""This module extracts details on running processes."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -24,7 +30,21 @@ class Processes(AndroidExtraction):
return
for result in self.results:
ioc = self.indicators.check_app_id(result.get("name", ""))
proc_name = result.get("proc_name", "")
if not proc_name:
continue
# Skipping this process because of false positives.
if result["proc_name"] == "gatekeeperd":
continue
ioc = self.indicators.check_app_id(proc_name)
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
continue
ioc = self.indicators.check_process(proc_name)
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
@@ -32,7 +52,7 @@ class Processes(AndroidExtraction):
def run(self) -> None:
self._adb_connect()
output = self._adb_command("ps -e")
output = self._adb_command("ps -A")
for line in output.splitlines()[1:]:
line = line.strip()

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from .base import AndroidExtraction
@@ -11,10 +12,15 @@ from .base import AndroidExtraction
class RootBinaries(AndroidExtraction):
"""This module extracts the list of installed packages."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from .base import AndroidExtraction
@@ -13,10 +14,15 @@ class SELinuxStatus(AndroidExtraction):
slug = "selinux_status"
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from .base import AndroidExtraction
@@ -59,10 +60,15 @@ ANDROID_DANGEROUS_SETTINGS = [
class Settings(AndroidExtraction):
"""This module extracts Android system settings."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)

View File

@@ -6,7 +6,7 @@
import logging
import os
import sqlite3
from typing import Union
from typing import Optional, Union
from mvt.android.parsers.backup import (AndroidBackupParsingError,
parse_tar_for_sms)
@@ -45,10 +45,15 @@ FROM sms;
class SMS(AndroidExtraction):
"""This module extracts all SMS messages containing links."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -153,7 +158,7 @@ class SMS(AndroidExtraction):
except InsufficientPrivileges:
pass
self.log.warn("No SMS database found. Trying extraction of SMS data "
self.log.info("No SMS database found. Trying extraction of SMS data "
"using Android backup feature.")
self._extract_sms_adb()

View File

@@ -7,7 +7,7 @@ import base64
import logging
import os
import sqlite3
from typing import Union
from typing import Optional, Union
from mvt.common.utils import check_for_links, convert_unix_to_iso
@@ -19,10 +19,15 @@ WHATSAPP_PATH = "data/data/com.whatsapp/databases/msgstore.db"
class Whatsapp(AndroidExtraction):
"""This module extracts all WhatsApp messages containing links."""
def __init__(self, file_path: str = None, target_path: str = None,
results_path: str = None, fast_mode: bool = False,
log: logging.Logger = logging.getLogger(__name__),
results: list = []) -> None:
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)
@@ -75,17 +80,19 @@ class Whatsapp(AndroidExtraction):
# If we find links in the messages or if they are empty we add them
# to the list.
if check_for_links(message["data"]) or message["data"].strip() == "":
if (check_for_links(message["data"])
or message["data"].strip() == ""):
if message.get("thumb_image"):
message["thumb_image"] = base64.b64encode(message["thumb_image"])
message["thumb_image"] = base64.b64encode(
message["thumb_image"])
messages.append(message)
cur.close()
conn.close()
self.log.info("Extracted a total of %d WhatsApp messages "
"containing links", len(messages))
self.log.info("Extracted a total of %d WhatsApp messages containing links",
len(messages))
self.results = messages
def run(self) -> None:

View File

@@ -0,0 +1,18 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 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 .dumpsys_accessibility import DumpsysAccessibility
from .dumpsys_activities import DumpsysActivities
from .dumpsys_appops import DumpsysAppops
from .dumpsys_packages import DumpsysPackages
from .dumpsys_receivers import DumpsysReceivers
from .getprop import Getprop
from .processes import Processes
from .settings import Settings
from .sms import SMS
ANDROIDQF_MODULES = [DumpsysActivities, DumpsysReceivers, DumpsysAccessibility,
DumpsysAppops, Processes, Getprop, Settings, SMS,
DumpsysPackages]

View File

@@ -0,0 +1,38 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 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 fnmatch
import logging
import os
from typing import Optional
from mvt.common.module import MVTModule
class AndroidQFModule(MVTModule):
"""This class provides a base for all Android Data analysis modules."""
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._path = target_path
self._files = []
for root, dirs, files in os.walk(target_path):
for name in files:
self._files.append(os.path.join(root, name))
def _get_files_by_pattern(self, pattern):
return fnmatch.filter(self._files, pattern)

Some files were not shown because too many files have changed in this diff Show More