mirror of
https://github.com/mvt-project/mvt
synced 2025-10-21 22:42:15 +02:00
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff41efba72 | ||
|
|
26e6a00bf5 | ||
|
|
9d61b9048c | ||
|
|
9950b3d6c2 | ||
|
|
e0d30ea990 | ||
|
|
293752f90a | ||
|
|
ac1e5c29d3 | ||
|
|
d868e6d9f0 | ||
|
|
f5cb7f06e1 | ||
|
|
5ce8035820 | ||
|
|
e3a8bde150 | ||
|
|
d6af7c8cca | ||
|
|
6584d8232c | ||
|
|
3487078c03 | ||
|
|
bc5d386be7 | ||
|
|
03efc8494b | ||
|
|
0b3f529cfa | ||
|
|
9bdef6ede4 | ||
|
|
fc9a27d030 | ||
|
|
f5f3660d82 | ||
|
|
712f5bcb9b | ||
|
|
ac26aa964a | ||
|
|
be511dcb51 | ||
|
|
b44c67e699 | ||
|
|
a4d08f8f35 | ||
|
|
6cc67f3c1d | ||
|
|
0d5377597f | ||
|
|
86c79075ff | ||
|
|
9940b1d145 | ||
|
|
b07fb092aa | ||
|
|
639c163297 | ||
|
|
8eb30e3a02 | ||
|
|
cd0e7d9879 | ||
|
|
bdaaf15434 | ||
|
|
699824d9ff | ||
|
|
8cca78d222 | ||
|
|
57cbb0ed56 | ||
|
|
e9cc6b3928 | ||
|
|
6d47d4d416 | ||
|
|
ed54761747 | ||
|
|
71c4ba799f | ||
|
|
09a6f291c0 | ||
|
|
b50be69dd4 | ||
|
|
6fc6102b73 | ||
|
|
3fe5d8dc8d | ||
|
|
fec6210d1b | ||
|
|
6a723e533f | ||
|
|
ed8a5a3845 | ||
|
|
04225a4455 | ||
|
|
5987f218be | ||
|
|
748780476e | ||
|
|
c522b54326 | ||
|
|
0e0e346916 | ||
|
|
69daf3c3cd | ||
|
|
998d87900d | ||
|
|
230f81879a | ||
|
|
df42efb7cb | ||
|
|
0922e569b0 | ||
|
|
03092cf3b7 | ||
|
|
ab63a02c9f | ||
|
|
a833dda581 | ||
|
|
189b1d7fc6 | ||
|
|
b1b282ac20 | ||
|
|
512c349c2c | ||
|
|
b94ba28873 | ||
|
|
564efc3629 | ||
|
|
9c62e6e4d6 | ||
|
|
153f6cce02 | ||
|
|
47f9a0104c | ||
|
|
bdad23feee | ||
|
|
5416b66915 | ||
|
|
e2936c3d33 | ||
|
|
3483ca1584 | ||
|
|
7b107edf1f | ||
|
|
b97ce7651a | ||
|
|
52a204cab6 | ||
|
|
1b335fda1d | ||
|
|
2ad175eae2 | ||
|
|
2d00dca5bd | ||
|
|
c8e50eb958 | ||
|
|
1f049fc8ba | ||
|
|
434738a306 | ||
|
|
06cd640c5e | ||
|
|
fb8a7ca104 | ||
|
|
8d15ff58dd | ||
|
|
eb5f07a75d | ||
|
|
ececf1a6b2 | ||
|
|
851cd52602 | ||
|
|
8db04fc991 | ||
|
|
3d0ba56e1f | ||
|
|
c48a4e8f50 | ||
|
|
001c2998a5 | ||
|
|
5e7c5727af | ||
|
|
883fbaeb88 | ||
|
|
6f0012cede | ||
|
|
458e80ccbb | ||
|
|
c8185fdbd8 | ||
|
|
67eea3edec | ||
|
|
bc86d159b8 | ||
|
|
43b1612dfe | ||
|
|
156f1084f1 | ||
|
|
49e34f6299 | ||
|
|
d88a66dd54 | ||
|
|
d3ed778ae4 | ||
|
|
4c3306c272 | ||
|
|
1c912f68fe | ||
|
|
10a640d3f7 | ||
|
|
c3acc95e9e | ||
|
|
90d05336da | ||
|
|
5513e6e9e3 | ||
|
|
38116f8405 | ||
|
|
59b069f006 | ||
|
|
28e1348aa7 | ||
|
|
034338d1f4 | ||
|
|
09d5eabf2f | ||
|
|
a425d6c511 | ||
|
|
f8897a4f8c | ||
|
|
86eae68bdb | ||
|
|
d2bf348b03 | ||
|
|
25c6c03075 | ||
|
|
cf88740f6a | ||
|
|
eb4810b0ad | ||
|
|
cce9159eda | ||
|
|
e1211991aa | ||
|
|
8ae9ca328c | ||
|
|
0e2eb51732 | ||
|
|
b35cd4bc73 | ||
|
|
1b4f99a31d |
10
.flake8
Normal file
10
.flake8
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[flake8]
|
||||||
|
max-complexit = 10
|
||||||
|
max-line-length = 1000
|
||||||
|
ignore =
|
||||||
|
C901,
|
||||||
|
E265,
|
||||||
|
E127,
|
||||||
|
F401,
|
||||||
|
W503,
|
||||||
|
E226
|
||||||
2
.github/workflows/python-package.yml
vendored
2
.github/workflows/python-package.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
name: Python package
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -131,3 +131,9 @@ dmypy.json
|
|||||||
|
|
||||||
# Temporal files
|
# Temporal files
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# IDEA Dev Environment
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Sublime Text project files
|
||||||
|
*.sublime*
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
[](https://pypi.org/project/mvt/)
|
[](https://pypi.org/project/mvt/)
|
||||||
[](https://docs.mvt.re/en/latest/?badge=latest)
|
[](https://docs.mvt.re/en/latest/?badge=latest)
|
||||||
|
[](https://github.com/mvt-project/mvt/actions/workflows/python-package.yml)
|
||||||
|
[](https://pepy.tech/project/mvt)
|
||||||
|
|
||||||
Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices.
|
Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
|||||||
@@ -11,18 +11,37 @@ That said, most versions of Android should still allow to locally backup SMS mes
|
|||||||
Because `mvt-android check-backup` currently only supports checking SMS messages, you can indicate to backup only those:
|
Because `mvt-android check-backup` currently only supports checking SMS messages, you can indicate to backup only those:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb backup com.android.providers.telephony
|
adb backup -nocompress com.android.providers.telephony
|
||||||
```
|
```
|
||||||
|
|
||||||
In case you nonetheless wish to take a full backup, you can do so with
|
In case you nonetheless wish to take a full backup, you can do so with
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb backup -all
|
adb backup -nocompress -all
|
||||||
```
|
```
|
||||||
|
|
||||||
## Unpack the backup
|
Some recent phones will enforce the utilisation of a password to encrypt the backup archive. In that case, the password will obviously be needed to extract and analyse the data later on.
|
||||||
|
|
||||||
In order to unpack the backup, use [Android Backup Extractor (ABE)](https://github.com/nelenkov/android-backup-extractor) to convert it to a readable file format. Make sure that java is installed on your system and use the following command:
|
## Unpack and check the backup
|
||||||
|
|
||||||
|
MVT includes a partial implementation of the Android Backup parsing, because of the implementation difference in the compression algorithm between Java and Python. The `-nocompress` option passed to adb in the section above allows to avoid this issue. You can analyse and extract SMSs containing links from the backup directly with MVT:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ mvt-android check-backup --output /path/to/results/ /path/to/backup.ab
|
||||||
|
14:09:45 INFO [mvt.android.cli] Checking ADB backup located at: backup.ab
|
||||||
|
INFO [mvt.android.modules.backup.sms] Running module SMS...
|
||||||
|
INFO [mvt.android.modules.backup.sms] Processing SMS backup file at
|
||||||
|
apps/com.android.providers.telephony/d_f/000000_sms_backup
|
||||||
|
INFO [mvt.android.modules.backup.sms] Extracted a total of 64 SMS messages containing links
|
||||||
|
```
|
||||||
|
|
||||||
|
If the backup is encrypted, MVT will prompt you to enter the password.
|
||||||
|
|
||||||
|
Through the `--iocs` argument you can specify a [STIX2](https://oasis-open.github.io/cti-documentation/stix/intro) file defining a list of malicious indicators to check against the records extracted from the backup by MVT. Any matches will be highlighted in the terminal output.
|
||||||
|
|
||||||
|
## Alternative ways to unpack and check the backup
|
||||||
|
|
||||||
|
If you encounter an issue during the analysis of the backup, you can alternatively use [Android Backup Extractor (ABE)](https://github.com/nelenkov/android-backup-extractor) to convert it to a readable file format. Make sure that java is installed on your system and use the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar ~/path/to/abe.jar unpack backup.ab backup.tar
|
java -jar ~/path/to/abe.jar unpack backup.ab backup.tar
|
||||||
@@ -33,17 +52,4 @@ If the backup is encrypted, ABE will prompt you to enter the password.
|
|||||||
|
|
||||||
Alternatively, [ab-decrypt](https://github.com/joernheissler/ab-decrypt) can be used for that purpose.
|
Alternatively, [ab-decrypt](https://github.com/joernheissler/ab-decrypt) can be used for that purpose.
|
||||||
|
|
||||||
## Check the backup
|
You can then extract SMSs containing links with MVT by passing the folder path as parameter instead of the `.ab` file: `mvt-android check-backup --output /path/to/results/ /path/to/backup/` (the path to backup given should be the folder containing the `apps` folder).
|
||||||
|
|
||||||
You can then extract SMSs containing links with MVT:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mvt-android check-backup --output /path/to/results/ /path/to/backup/
|
|
||||||
16:18:38 INFO [mvt.android.cli] Checking ADB backup located at: .
|
|
||||||
INFO [mvt.android.modules.backup.sms] Running module SMS...
|
|
||||||
INFO [mvt.android.modules.backup.sms] Processing SMS backup file at /path/to/backup/apps/com.android.providers.telephony/d_f/000000_sms_backup
|
|
||||||
16:18:39 INFO [mvt.android.modules.backup.sms] Extracted a total of
|
|
||||||
64 SMS messages containing links
|
|
||||||
```
|
|
||||||
|
|
||||||
Through the `--iocs` argument you can specify a [STIX2](https://oasis-open.github.io/cti-documentation/stix/intro) file defining a list of malicious indicators to check against the records extracted from the backup by MVT. Any matches will be highlighted in the terminal output.
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ cd mvt
|
|||||||
docker build -t 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:
|
Test if the image was created successfully:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
:param json_path: Path to the apks.json file to parse.
|
:param json_path: Path to the apks.json file to parse.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with open(json_path, "r") as handle:
|
with open(json_path, "r", encoding="utf-8") as handle:
|
||||||
packages = json.load(handle)
|
packages = json.load(handle)
|
||||||
return cls(packages=packages)
|
return cls(packages=packages)
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
def save_json(self):
|
def save_json(self):
|
||||||
"""Save the results to the package.json file."""
|
"""Save the results to the package.json file."""
|
||||||
json_path = os.path.join(self.output_folder, "apks.json")
|
json_path = os.path.join(self.output_folder, "apks.json")
|
||||||
with open(json_path, "w") as handle:
|
with open(json_path, "w", encoding="utf-8") as handle:
|
||||||
json.dump(self.packages, handle, indent=4)
|
json.dump(self.packages, handle, indent=4)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
@@ -39,6 +39,10 @@ def get_virustotal_report(hashes):
|
|||||||
|
|
||||||
|
|
||||||
def virustotal_lookup(packages):
|
def virustotal_lookup(packages):
|
||||||
|
# NOTE: This is temporary, until we resolved the issue.
|
||||||
|
log.error("Unfortunately VirusTotal lookup is disabled until further notice, due to unresolved issues with the API service.")
|
||||||
|
return
|
||||||
|
|
||||||
log.info("Looking up all extracted files on VirusTotal (www.virustotal.com)")
|
log.info("Looking up all extracted files on VirusTotal (www.virustotal.com)")
|
||||||
|
|
||||||
unique_hashes = []
|
unique_hashes = []
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
from .chrome_history import ChromeHistory
|
from .chrome_history import ChromeHistory
|
||||||
from .dumpsys_accessibility import DumpsysAccessibility
|
from .dumpsys_accessibility import DumpsysAccessibility
|
||||||
from .dumpsys_batterystats import DumpsysBatterystats
|
from .dumpsys_activities import DumpsysActivities
|
||||||
|
from .dumpsys_appops import DumpsysAppOps
|
||||||
|
from .dumpsys_battery_daily import DumpsysBatteryDaily
|
||||||
|
from .dumpsys_battery_history import DumpsysBatteryHistory
|
||||||
|
from .dumpsys_dbinfo import DumpsysDBInfo
|
||||||
from .dumpsys_full import DumpsysFull
|
from .dumpsys_full import DumpsysFull
|
||||||
from .dumpsys_packages import DumpsysPackages
|
|
||||||
from .dumpsys_procstats import DumpsysProcstats
|
|
||||||
from .dumpsys_receivers import DumpsysReceivers
|
from .dumpsys_receivers import DumpsysReceivers
|
||||||
from .files import Files
|
from .files import Files
|
||||||
|
from .getprop import Getprop
|
||||||
from .logcat import Logcat
|
from .logcat import Logcat
|
||||||
from .packages import Packages
|
from .packages import Packages
|
||||||
from .processes import Processes
|
from .processes import Processes
|
||||||
from .rootbinaries import RootBinaries
|
from .root_binaries import RootBinaries
|
||||||
|
from .selinux_status import SELinuxStatus
|
||||||
|
from .settings import Settings
|
||||||
from .sms import SMS
|
from .sms import SMS
|
||||||
from .whatsapp import Whatsapp
|
from .whatsapp import Whatsapp
|
||||||
|
|
||||||
ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes,
|
ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes, Getprop, Settings,
|
||||||
DumpsysAccessibility, DumpsysBatterystats, DumpsysProcstats,
|
SELinuxStatus, DumpsysBatteryHistory, DumpsysBatteryDaily,
|
||||||
DumpsysPackages, DumpsysReceivers, DumpsysFull,
|
DumpsysReceivers, DumpsysActivities, DumpsysAccessibility,
|
||||||
Packages, RootBinaries, Logcat, Files]
|
DumpsysDBInfo, DumpsysFull, DumpsysAppOps, Packages, Logcat,
|
||||||
|
RootBinaries, Files]
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import getpass
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
@@ -15,9 +17,11 @@ from adb_shell.adb_device import AdbDeviceTcp, AdbDeviceUsb
|
|||||||
from adb_shell.auth.keygen import keygen, write_public_keyfile
|
from adb_shell.auth.keygen import keygen, write_public_keyfile
|
||||||
from adb_shell.auth.sign_pythonrsa import PythonRSASigner
|
from adb_shell.auth.sign_pythonrsa import PythonRSASigner
|
||||||
from adb_shell.exceptions import (AdbCommandFailureException, DeviceAuthError,
|
from adb_shell.exceptions import (AdbCommandFailureException, DeviceAuthError,
|
||||||
UsbReadFailedError)
|
UsbDeviceNotFoundError, UsbReadFailedError)
|
||||||
from usb1 import USBErrorAccess, USBErrorBusy
|
from usb1 import USBErrorAccess, USBErrorBusy
|
||||||
|
|
||||||
|
from mvt.android.parsers.backup import (InvalidBackupPassword, parse_ab_header,
|
||||||
|
parse_backup_file)
|
||||||
from mvt.common.module import InsufficientPrivileges, MVTModule
|
from mvt.common.module import InsufficientPrivileges, MVTModule
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -65,7 +69,11 @@ class AndroidExtraction(MVTModule):
|
|||||||
# If no serial was specified or if the serial does not seem to be
|
# If no serial was specified or if the serial does not seem to be
|
||||||
# a HOST:PORT definition, we use the USB transport.
|
# a HOST:PORT definition, we use the USB transport.
|
||||||
if not self.serial or ":" not in self.serial:
|
if not self.serial or ":" not in self.serial:
|
||||||
self.device = AdbDeviceUsb(serial=self.serial)
|
try:
|
||||||
|
self.device = AdbDeviceUsb(serial=self.serial)
|
||||||
|
except UsbDeviceNotFoundError:
|
||||||
|
log.critical("No device found. Make sure it is connected and unlocked.")
|
||||||
|
sys.exit(-1)
|
||||||
# Otherwise we try to use the TCP transport.
|
# Otherwise we try to use the TCP transport.
|
||||||
else:
|
else:
|
||||||
addr = self.serial.split(":")
|
addr = self.serial.split(":")
|
||||||
@@ -239,6 +247,32 @@ class AndroidExtraction(MVTModule):
|
|||||||
# Disconnect from the device.
|
# Disconnect from the device.
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
def _generate_backup(self, package_name):
|
||||||
|
self.log.warning("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...
|
||||||
|
backup_output_b64 = self._adb_command("/system/bin/bu backup -nocompress '{}' | base64".format(
|
||||||
|
package_name))
|
||||||
|
backup_output = base64.b64decode(backup_output_b64)
|
||||||
|
header = parse_ab_header(backup_output)
|
||||||
|
|
||||||
|
if not header["backup"]:
|
||||||
|
self.log.error("Extracting SMS via Android backup failed. No valid backup data found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if header["encryption"] == "none":
|
||||||
|
return parse_backup_file(backup_output, password=None)
|
||||||
|
|
||||||
|
for password_retry in range(0, 3):
|
||||||
|
backup_password = getpass.getpass(prompt="Backup password: ", stream=None)
|
||||||
|
try:
|
||||||
|
decrypted_backup_tar = parse_backup_file(backup_output, backup_password)
|
||||||
|
return decrypted_backup_tar
|
||||||
|
except InvalidBackupPassword:
|
||||||
|
self.log.error("You provided the wrong password! Please try again...")
|
||||||
|
|
||||||
|
self.log.warn("All attempts to decrypt backup with password failed!")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the main procedure."""
|
"""Run the main procedure."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class ChromeHistory(AndroidExtraction):
|
|||||||
"url": item[1],
|
"url": item[1],
|
||||||
"visit_id": item[2],
|
"visit_id": item[2],
|
||||||
"timestamp": item[3],
|
"timestamp": item[3],
|
||||||
"isodate": convert_timestamp_to_iso(convert_chrometime_to_unix[item[3]]),
|
"isodate": convert_timestamp_to_iso(convert_chrometime_to_unix(item[3])),
|
||||||
"redirect_source": item[4],
|
"redirect_source": item[4],
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -78,5 +78,8 @@ class ChromeHistory(AndroidExtraction):
|
|||||||
log.info("Extracted a total of %d history items", len(self.results))
|
log.info("Extracted a total of %d history items", len(self.results))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._adb_process_file(os.path.join("/", CHROME_HISTORY_PATH),
|
try:
|
||||||
self._parse_db)
|
self._adb_process_file(os.path.join("/", CHROME_HISTORY_PATH),
|
||||||
|
self._parse_db)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error(e)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import io
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
|
from mvt.android.parsers import parse_dumpsys_accessibility
|
||||||
|
|
||||||
from .base import AndroidExtraction
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
@@ -21,33 +21,25 @@ class DumpsysAccessibility(AndroidExtraction):
|
|||||||
output_folder=output_folder, fast_mode=fast_mode,
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys accessibility")
|
||||||
stats = self._adb_command("dumpsys accessibility")
|
|
||||||
|
|
||||||
in_services = False
|
|
||||||
for line in stats.split("\n"):
|
|
||||||
if line.strip().startswith("installed services:"):
|
|
||||||
in_services = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_services:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if line.strip() == "}":
|
|
||||||
break
|
|
||||||
|
|
||||||
service = line.split(":")[1].strip()
|
|
||||||
log.info("Found installed accessibility service \"%s\"", service)
|
|
||||||
|
|
||||||
if self.output_folder:
|
|
||||||
acc_path = os.path.join(self.output_folder,
|
|
||||||
"dumpsys_accessibility.txt")
|
|
||||||
with io.open(acc_path, "w", encoding="utf-8") as handle:
|
|
||||||
handle.write(stats)
|
|
||||||
|
|
||||||
log.info("Records from dumpsys accessibility stored at %s",
|
|
||||||
acc_path)
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_accessibility(output)
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
log.info("Found installed accessibility service \"%s\"", result.get("service"))
|
||||||
|
|
||||||
|
self.log.info("Identified a total of %d accessibility services", len(self.results))
|
||||||
|
|||||||
45
mvt/android/modules/adb/dumpsys_activities.py
Normal file
45
mvt/android/modules/adb/dumpsys_activities.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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 mvt.android.parsers import parse_dumpsys_activity_resolver_table
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysActivities(AndroidExtraction):
|
||||||
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
self.results = results if results else {}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for intent, activities in self.results.items():
|
||||||
|
for activity in activities:
|
||||||
|
ioc = self.indicators.check_app_id(activity["package_name"])
|
||||||
|
if ioc:
|
||||||
|
activity["matched_indicator"] = ioc
|
||||||
|
self.detected.append({intent: activity})
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys package")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_activity_resolver_table(output)
|
||||||
|
|
||||||
|
self.log.info("Extracted activities for %d intents", len(self.results))
|
||||||
66
mvt/android/modules/adb/dumpsys_appops.py
Normal file
66
mvt/android/modules/adb/dumpsys_appops.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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
|
||||||
|
import re
|
||||||
|
|
||||||
|
from mvt.android.parsers.dumpsys import parse_dumpsys_appops
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysAppOps(AndroidExtraction):
|
||||||
|
"""This module extracts records from App-op Manager."""
|
||||||
|
|
||||||
|
slug = "dumpsys_appops"
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def serialize(self, record):
|
||||||
|
records = []
|
||||||
|
for perm in record["permissions"]:
|
||||||
|
if "entries" not in perm:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for entry in perm["entries"]:
|
||||||
|
if "timestamp" in entry:
|
||||||
|
records.append({
|
||||||
|
"timestamp": entry["timestamp"],
|
||||||
|
"module": self.__class__.__name__,
|
||||||
|
"event": entry["access"],
|
||||||
|
"data": f"{record['package_name']} access to {perm['name']} : {entry['access']}",
|
||||||
|
})
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
for result in self.results:
|
||||||
|
if self.indicators:
|
||||||
|
ioc = self.indicators.check_app_id(result.get("package_name"))
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for perm in result["permissions"]:
|
||||||
|
if perm["name"] == "REQUEST_INSTALL_PACKAGES" and perm["access"] == "allow":
|
||||||
|
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission",
|
||||||
|
result["package_name"])
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys appops")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_appops(output)
|
||||||
|
|
||||||
|
self.log.info("Extracted a total of %d records from app-ops manager",
|
||||||
|
len(self.results))
|
||||||
50
mvt/android/modules/adb/dumpsys_battery_daily.py
Normal file
50
mvt/android/modules/adb/dumpsys_battery_daily.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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 mvt.android.parsers import parse_dumpsys_battery_daily
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysBatteryDaily(AndroidExtraction):
|
||||||
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def serialize(self, record):
|
||||||
|
return {
|
||||||
|
"timestamp": record["from"],
|
||||||
|
"module": self.__class__.__name__,
|
||||||
|
"event": "battery_daily",
|
||||||
|
"data": f"Recorded update of package {record['package_name']} with vers {record['vers']}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys batterystats --daily")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_battery_daily(output)
|
||||||
|
|
||||||
|
self.log.info("Extracted %d records from battery daily stats", len(self.results))
|
||||||
42
mvt/android/modules/adb/dumpsys_battery_history.py
Normal file
42
mvt/android/modules/adb/dumpsys_battery_history.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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 mvt.android.parsers import parse_dumpsys_battery_history
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysBatteryHistory(AndroidExtraction):
|
||||||
|
"""This module extracts records from battery history events."""
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
ioc = self.indicators.check_app_id(result["package_name"])
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys batterystats --history")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_battery_history(output)
|
||||||
|
|
||||||
|
self.log.info("Extracted %d records from battery history", len(self.results))
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
|
||||||
# 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
|
|
||||||
import os
|
|
||||||
|
|
||||||
from .base import AndroidExtraction
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class DumpsysBatterystats(AndroidExtraction):
|
|
||||||
"""This module extracts stats on battery consumption by processes."""
|
|
||||||
|
|
||||||
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
|
||||||
super().__init__(file_path=file_path, base_folder=base_folder,
|
|
||||||
output_folder=output_folder, fast_mode=fast_mode,
|
|
||||||
log=log, results=results)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self._adb_connect()
|
|
||||||
|
|
||||||
stats = self._adb_command("dumpsys batterystats")
|
|
||||||
if self.output_folder:
|
|
||||||
stats_path = os.path.join(self.output_folder,
|
|
||||||
"dumpsys_batterystats.txt")
|
|
||||||
with open(stats_path, "w") as handle:
|
|
||||||
handle.write(stats)
|
|
||||||
|
|
||||||
log.info("Records from dumpsys batterystats stored at %s",
|
|
||||||
stats_path)
|
|
||||||
|
|
||||||
history = self._adb_command("dumpsys batterystats --history")
|
|
||||||
if self.output_folder:
|
|
||||||
history_path = os.path.join(self.output_folder,
|
|
||||||
"dumpsys_batterystats_history.txt")
|
|
||||||
with open(history_path, "w") as handle:
|
|
||||||
handle.write(history)
|
|
||||||
|
|
||||||
log.info("History records from dumpsys batterystats stored at %s",
|
|
||||||
history_path)
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
|
||||||
48
mvt/android/modules/adb/dumpsys_dbinfo.py
Normal file
48
mvt/android/modules/adb/dumpsys_dbinfo.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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
|
||||||
|
import re
|
||||||
|
|
||||||
|
from mvt.android.parsers import parse_dumpsys_dbinfo
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysDBInfo(AndroidExtraction):
|
||||||
|
"""This module extracts records from battery daily updates."""
|
||||||
|
|
||||||
|
slug = "dumpsys_dbinfo"
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
path = result.get("path", "")
|
||||||
|
for part in path.split("/"):
|
||||||
|
ioc = self.indicators.check_app_id(part)
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("dumpsys dbinfo")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_dumpsys_dbinfo(output)
|
||||||
|
|
||||||
|
self.log.info("Extracted a total of %d records from database information",
|
||||||
|
len(self.results))
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
@@ -23,14 +23,12 @@ class DumpsysFull(AndroidExtraction):
|
|||||||
def run(self):
|
def run(self):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
stats = self._adb_command("dumpsys")
|
output = self._adb_command("dumpsys")
|
||||||
if self.output_folder:
|
if self.output_folder:
|
||||||
stats_path = os.path.join(self.output_folder,
|
output_path = os.path.join(self.output_folder, "dumpsys.txt")
|
||||||
"dumpsys.txt")
|
with open(output_path, "w", encoding="utf-8") as handle:
|
||||||
with open(stats_path, "w") as handle:
|
handle.write(output)
|
||||||
handle.write(stats)
|
|
||||||
|
|
||||||
log.info("Full dumpsys output stored at %s",
|
log.info("Full dumpsys output stored at %s", output_path)
|
||||||
stats_path)
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
|
||||||
# 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
|
|
||||||
import os
|
|
||||||
|
|
||||||
from .base import AndroidExtraction
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class DumpsysProcstats(AndroidExtraction):
|
|
||||||
"""This module extracts stats on memory consumption by processes."""
|
|
||||||
|
|
||||||
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
|
||||||
super().__init__(file_path=file_path, base_folder=base_folder,
|
|
||||||
output_folder=output_folder, fast_mode=fast_mode,
|
|
||||||
log=log, results=results)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self._adb_connect()
|
|
||||||
|
|
||||||
output = self._adb_command("dumpsys procstats")
|
|
||||||
if self.output_folder:
|
|
||||||
procstats_path = os.path.join(self.output_folder,
|
|
||||||
"dumpsys_procstats.txt")
|
|
||||||
with open(procstats_path, "w") as handle:
|
|
||||||
handle.write(output)
|
|
||||||
|
|
||||||
log.info("Records from dumpsys procstats stored at %s",
|
|
||||||
procstats_path)
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from mvt.android.parsers import parse_dumpsys_receiver_resolver_table
|
||||||
|
|
||||||
from .base import AndroidExtraction
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
ACTION_NEW_OUTGOING_SMS = "android.provider.Telephony.NEW_OUTGOING_SMS"
|
INTENT_NEW_OUTGOING_SMS = "android.provider.Telephony.NEW_OUTGOING_SMS"
|
||||||
ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"
|
INTENT_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"
|
||||||
ACTION_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED"
|
INTENT_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED"
|
||||||
ACTION_PHONE_STATE = "android.intent.action.PHONE_STATE"
|
INTENT_PHONE_STATE = "android.intent.action.PHONE_STATE"
|
||||||
|
INTENT_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"
|
||||||
|
|
||||||
|
|
||||||
class DumpsysReceivers(AndroidExtraction):
|
class DumpsysReceivers(AndroidExtraction):
|
||||||
@@ -24,64 +27,40 @@ class DumpsysReceivers(AndroidExtraction):
|
|||||||
output_folder=output_folder, fast_mode=fast_mode,
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
|
|
||||||
|
self.results = results if results else {}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
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\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
elif intent == INTENT_SMS_RECEIVED:
|
||||||
|
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\"",
|
||||||
|
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\"",
|
||||||
|
receiver["receiver"])
|
||||||
|
|
||||||
|
ioc = self.indicators.check_app_id(receiver["package_name"])
|
||||||
|
if ioc:
|
||||||
|
receiver["matched_indicator"] = ioc
|
||||||
|
self.detected.append({intent: receiver})
|
||||||
|
continue
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("dumpsys package")
|
output = self._adb_command("dumpsys package")
|
||||||
if not output:
|
self.results = parse_dumpsys_receiver_resolver_table(output)
|
||||||
return
|
|
||||||
|
|
||||||
activity = None
|
|
||||||
for line in output.split("\n"):
|
|
||||||
# Find activity block markers.
|
|
||||||
if line.strip().startswith(ACTION_NEW_OUTGOING_SMS):
|
|
||||||
activity = ACTION_NEW_OUTGOING_SMS
|
|
||||||
continue
|
|
||||||
elif line.strip().startswith(ACTION_SMS_RECEIVED):
|
|
||||||
activity = ACTION_SMS_RECEIVED
|
|
||||||
continue
|
|
||||||
elif line.strip().startswith(ACTION_PHONE_STATE):
|
|
||||||
activity = ACTION_PHONE_STATE
|
|
||||||
continue
|
|
||||||
elif line.strip().startswith(ACTION_DATA_SMS_RECEIVED):
|
|
||||||
activity = ACTION_DATA_SMS_RECEIVED
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If we are not in an activity block yet, skip.
|
|
||||||
if not activity:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If we are in a block but the line does not start with 8 spaces
|
|
||||||
# it means the block ended a new one started, so we reset and
|
|
||||||
# continue.
|
|
||||||
if not line.startswith(" " * 8):
|
|
||||||
activity = None
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If we got this far, we are processing receivers for the
|
|
||||||
# activities we are interested in.
|
|
||||||
receiver = line.strip().split(" ")[1]
|
|
||||||
package_name = receiver.split("/")[0]
|
|
||||||
if package_name == "com.google.android.gms":
|
|
||||||
continue
|
|
||||||
|
|
||||||
if activity == ACTION_NEW_OUTGOING_SMS:
|
|
||||||
self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"",
|
|
||||||
receiver)
|
|
||||||
elif activity == ACTION_SMS_RECEIVED:
|
|
||||||
self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"",
|
|
||||||
receiver)
|
|
||||||
elif activity == ACTION_DATA_SMS_RECEIVED:
|
|
||||||
self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"",
|
|
||||||
receiver)
|
|
||||||
elif activity == ACTION_PHONE_STATE:
|
|
||||||
self.log.info("Found a receiver monitoring telephony state: \"%s\"",
|
|
||||||
receiver)
|
|
||||||
|
|
||||||
self.results.append({
|
|
||||||
"activity": activity,
|
|
||||||
"package_name": package_name,
|
|
||||||
"receiver": receiver,
|
|
||||||
})
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Mobile Verification Toolkit (MVT)
|
# Mobile Verification Toolkit (MVT)
|
||||||
# Copyright (c) 2021 The MVT Project Authors.
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
@@ -22,30 +22,16 @@ class Files(AndroidExtraction):
|
|||||||
super().__init__(file_path=file_path, base_folder=base_folder,
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
output_folder=output_folder, fast_mode=fast_mode,
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
log=log, results=results)
|
||||||
self.full_find = None
|
self.full_find = False
|
||||||
|
|
||||||
def find_path(self, file_path):
|
def find_files(self, folder):
|
||||||
"""Checks if Android system supports full find command output"""
|
if self.full_find:
|
||||||
# Check find command params on first run
|
output = self._adb_command(f"find '{folder}' -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
||||||
# Run find command with correct args and parse results.
|
|
||||||
|
|
||||||
# Check that full file printf options are suppported on first run.
|
|
||||||
if self.full_find is None:
|
|
||||||
output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
|
||||||
if not (output or output.strip().splitlines()):
|
|
||||||
# Full find command failed to generate output, fallback to basic file arguments
|
|
||||||
self.full_find = False
|
|
||||||
else:
|
|
||||||
self.full_find = True
|
|
||||||
|
|
||||||
found_files = []
|
|
||||||
if self.full_find is True:
|
|
||||||
# Run full file command and collect additonal file information.
|
|
||||||
output = self._adb_command(f"find '{file_path}' -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
|
||||||
for file_line in output.splitlines():
|
for file_line in output.splitlines():
|
||||||
[unix_timestamp, mode, size, owner, group, full_path] = file_line.rstrip().split(" ", 5)
|
[unix_timestamp, mode, size, owner, group, full_path] = file_line.rstrip().split(" ", 5)
|
||||||
mod_time = convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(int(float(unix_timestamp))))
|
mod_time = convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(int(float(unix_timestamp))))
|
||||||
found_files.append({
|
self.results.append({
|
||||||
"path": full_path,
|
"path": full_path,
|
||||||
"modified_time": mod_time,
|
"modified_time": mod_time,
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
@@ -56,14 +42,9 @@ class Files(AndroidExtraction):
|
|||||||
"group": group,
|
"group": group,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
# Run a basic listing of file paths.
|
output = self._adb_command(f"find '{folder}' 2> /dev/null")
|
||||||
output = self._adb_command(f"find '{file_path}' 2> /dev/null")
|
|
||||||
for file_line in output.splitlines():
|
for file_line in output.splitlines():
|
||||||
found_files.append({
|
self.results.append({"path": file_line.rstrip()})
|
||||||
"path": file_line.rstrip()
|
|
||||||
})
|
|
||||||
|
|
||||||
return found_files
|
|
||||||
|
|
||||||
def serialize(self, record):
|
def serialize(self, record):
|
||||||
if "modified_time" in record:
|
if "modified_time" in record:
|
||||||
@@ -85,6 +66,7 @@ class Files(AndroidExtraction):
|
|||||||
def check_indicators(self):
|
def check_indicators(self):
|
||||||
"""Check file list for known suspicious files or suspicious properties"""
|
"""Check file list for known suspicious files or suspicious properties"""
|
||||||
self.check_suspicious()
|
self.check_suspicious()
|
||||||
|
|
||||||
if not self.indicators:
|
if not self.indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -95,25 +77,21 @@ class Files(AndroidExtraction):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
found_file_paths = []
|
|
||||||
|
|
||||||
DATA_PATHS = ["/data/local/tmp/", "/sdcard/", "/tmp/"]
|
output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
|
||||||
for path in DATA_PATHS:
|
if output or output.strip().splitlines():
|
||||||
file_info = self.find_path(path)
|
self.full_find = True
|
||||||
found_file_paths.extend(file_info)
|
|
||||||
|
|
||||||
# Store results
|
for data_path in ["/data/local/tmp/", "/sdcard/", "/tmp/"]:
|
||||||
self.results.extend(found_file_paths)
|
self.find_files(data_path)
|
||||||
self.log.info("Found %s files in primary Android data directories.", len(found_file_paths))
|
|
||||||
|
self.log.info("Found %s files in primary Android data directories", len(self.results))
|
||||||
|
|
||||||
if self.fast_mode:
|
if self.fast_mode:
|
||||||
self.log.info("Flag --fast was enabled: skipping full file listing")
|
self.log.info("Flag --fast was enabled: skipping full file listing")
|
||||||
else:
|
else:
|
||||||
self.log.info("Flag --fast was not enabled: processing full file listing. "
|
self.log.info("Processing full file listing. This may take a while...")
|
||||||
"This may take a while...")
|
self.find_files("/")
|
||||||
output = self.find_path("/")
|
self.log.info("Found %s total files", len(self.results))
|
||||||
if output and self.output_folder:
|
|
||||||
self.results.extend(output)
|
|
||||||
log.info("List of visible files stored in files.json")
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|||||||
43
mvt/android/modules/adb/getprop.py
Normal file
43
mvt/android/modules/adb/getprop.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Mobile Verification Toolkit (MVT)
|
||||||
|
# Copyright (c) 2021-2022 The MVT Project Authors.
|
||||||
|
# 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
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from mvt.android.parsers import parse_getprop
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Getprop(AndroidExtraction):
|
||||||
|
"""This module extracts device properties from getprop command."""
|
||||||
|
|
||||||
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
|
super().__init__(file_path=file_path, base_folder=base_folder,
|
||||||
|
output_folder=output_folder, fast_mode=fast_mode,
|
||||||
|
log=log, results=results)
|
||||||
|
|
||||||
|
self.results = {} if not results else results
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._adb_connect()
|
||||||
|
output = self._adb_command("getprop")
|
||||||
|
self._adb_disconnect()
|
||||||
|
|
||||||
|
self.results = parse_getprop(output)
|
||||||
|
|
||||||
|
# Alert if phone is outdated.
|
||||||
|
security_patch = self.results.get("ro.build.version.security_patch", "")
|
||||||
|
if security_patch:
|
||||||
|
patch_date = datetime.strptime(security_patch, "%Y-%m-%d")
|
||||||
|
if (datetime.now() - patch_date) > timedelta(days=6*30):
|
||||||
|
self.log.warning("This phone has not received security updates for more than "
|
||||||
|
"six months (last update: %s)", security_patch)
|
||||||
|
|
||||||
|
self.log.info("Extracted %d Android system properties", len(self.results))
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user