mirror of
https://github.com/mvt-project/mvt
synced 2025-11-13 01:37:36 +01:00
Compare commits
260 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e4e1716729 | ||
|
|
083bc12351 | ||
|
|
cf6d392460 | ||
|
|
95205d8e17 | ||
|
|
1460828c30 | ||
|
|
fa84b3f296 | ||
|
|
e1efaa5467 | ||
|
|
696d42fc6e | ||
|
|
a0e1662726 | ||
|
|
51645bdbc0 | ||
|
|
bb1b108fd7 | ||
|
|
92f9dcb8a5 | ||
|
|
a6fd5fe1f3 | ||
|
|
3e0ef20fcd | ||
|
|
01f3acde2e | ||
|
|
b697874f56 | ||
|
|
41d699f457 | ||
|
|
6fcd40f6b6 | ||
|
|
38bb583a9e | ||
|
|
48ec2d8fa8 | ||
|
|
798805c583 | ||
|
|
24be9e9570 | ||
|
|
adbd95c559 | ||
|
|
8a707c288a | ||
|
|
4c906ad52e | ||
|
|
a2f8030cce | ||
|
|
737007afdb | ||
|
|
33efeda90a | ||
|
|
146f2ae57d | ||
|
|
11bc916854 | ||
|
|
3084876f31 | ||
|
|
f63cb585b2 | ||
|
|
637aebcd89 | ||
|
|
16a0de3af4 | ||
|
|
15fbedccc9 | ||
|
|
e0514b20dd | ||
|
|
b2e9f0361b | ||
|
|
e85c70c603 | ||
|
|
3f8dade610 | ||
|
|
54963b0b59 | ||
|
|
513e2cc704 | ||
|
|
28d57e7178 | ||
|
|
dc8eeb618e | ||
|
|
c282d4341d | ||
|
|
681bae2f66 | ||
|
|
b079246c8a | ||
|
|
82b57f1997 | ||
|
|
8f88f872df | ||
|
|
2d16218489 | ||
|
|
3215e797ec | ||
|
|
e65a598903 | ||
|
|
e80c02451c | ||
|
|
5df50f864c | ||
|
|
45b31bb718 | ||
|
|
e10f1767e6 | ||
|
|
d64277c0bf | ||
|
|
3f3261511a | ||
|
|
4cfe75e2d4 | ||
|
|
cdd90332f7 | ||
|
|
d9b29b3739 | ||
|
|
79bb7d1d4b | ||
|
|
a653cb3cfc | ||
|
|
b25cc48be0 | ||
|
|
40bd9ddc1d | ||
|
|
deb95297da | ||
|
|
02014b414b | ||
|
|
7dd5fe7831 | ||
|
|
11d1a3dcee | ||
|
|
74f9db2bf2 | ||
|
|
356bddc3af | ||
|
|
512f40dcb4 | ||
|
|
b3a464ba58 | ||
|
|
529df85f0f | ||
|
|
19a6da8fe7 | ||
|
|
34c997f923 | ||
|
|
02bf903411 | ||
|
|
7019375767 | ||
|
|
34dd27c5d2 | ||
|
|
a4d6a08a8b | ||
|
|
635d3a392d | ||
|
|
2d78bddbba | ||
|
|
c1938d2ead | ||
|
|
104b01e5cd | ||
|
|
7087e8adb2 | ||
|
|
67608ac02b | ||
|
|
6d8de5b461 | ||
|
|
b0177d6104 | ||
|
|
e0c9a44b10 | ||
|
|
ef8c1ae895 | ||
|
|
3165801e2b | ||
|
|
1aa371a398 | ||
|
|
f8e380baa1 | ||
|
|
35559b09a8 | ||
|
|
daf5c1f3de | ||
|
|
f601db2174 | ||
|
|
3ce9641c23 | ||
|
|
9be393e3f6 | ||
|
|
5f125974b8 | ||
|
|
aa0f152ba1 | ||
|
|
169f5fbc26 | ||
|
|
5ea3460c09 | ||
|
|
c38df37967 | ||
|
|
7f29b522fa | ||
|
|
40b0da9885 | ||
|
|
94a8d9dd91 | ||
|
|
963d3db51a | ||
|
|
660e208473 | ||
|
|
01e68ccc6a | ||
|
|
fba0fa1f2c | ||
|
|
1cbf55e50e | ||
|
|
8fcc79ebfa | ||
|
|
423462395a | ||
|
|
1f08572a6a | ||
|
|
94e3c0ce7b | ||
|
|
904daad935 | ||
|
|
eb2a8b8b41 | ||
|
|
60a17381a2 | ||
|
|
ef2bb93dc4 | ||
|
|
f68b7e7089 | ||
|
|
a22241ec32 | ||
|
|
8ad1bc7a2b | ||
|
|
c6b3509ed4 | ||
|
|
75b5b296a5 | ||
|
|
2d62e31eaa | ||
|
|
1bfc683e4b | ||
|
|
7ab09669b5 | ||
|
|
757bd8618e | ||
|
|
f1d039346d | ||
|
|
ccdfd92d4a | ||
|
|
032b229eb8 | ||
|
|
93936976c7 | ||
|
|
f3a4e9d108 | ||
|
|
93a9735b5e | ||
|
|
7b0e2d4564 | ||
|
|
725a99bcd5 | ||
|
|
35a6f6ec9a | ||
|
|
f4ba29f1ef | ||
|
|
3f9809f36c | ||
|
|
6da6595108 | ||
|
|
35dfeaccee | ||
|
|
e5f2aa3c3d | ||
|
|
3236c1b390 | ||
|
|
80a670273d | ||
|
|
969b5cc506 | ||
|
|
ef8622d4c3 | ||
|
|
e39e9e6f92 | ||
|
|
7b32ed3179 | ||
|
|
315317863e | ||
|
|
08d35b056a | ||
|
|
3e679312d1 | ||
|
|
be4f1afed6 | ||
|
|
0dea25d86e | ||
|
|
505d3c7e60 | ||
|
|
8f04c09b75 | ||
|
|
595b7e2066 | ||
|
|
d3941bb5d3 | ||
|
|
194c8a0ac1 | ||
|
|
bef190fe50 | ||
|
|
cacf027051 | ||
|
|
da97f5ca30 | ||
|
|
a774577940 | ||
|
|
7252cc82a7 | ||
|
|
b34d80fd11 | ||
|
|
0347dfa3c9 | ||
|
|
28647b8493 | ||
|
|
c2ec26fd75 | ||
|
|
856a6fb895 | ||
|
|
62f3c535df | ||
|
|
34c64af815 | ||
|
|
ea4da71277 | ||
|
|
94fe3c90e0 | ||
|
|
f78332aa71 | ||
|
|
0c4eb0bb34 |
14
.github/workflows/python-package.yml
vendored
14
.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:
|
||||||
@@ -16,7 +16,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.7, 3.8, 3.9]
|
# python-version: [3.7, 3.8, 3.9]
|
||||||
|
python-version: [3.8, 3.9]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -27,8 +28,9 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install flake8 pytest safety
|
python -m pip install flake8 pytest safety stix2
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
python -m pip install .
|
||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
@@ -37,7 +39,5 @@ jobs:
|
|||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
- name: Safety checks
|
- name: Safety checks
|
||||||
run: safety check
|
run: safety check
|
||||||
|
- name: Test with pytest
|
||||||
# - name: Test with pytest
|
run: pytest
|
||||||
# run: |
|
|
||||||
# pytest
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -131,3 +131,6 @@ dmypy.json
|
|||||||
|
|
||||||
# Temporal files
|
# Temporal files
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# IDEA Dev Environment
|
||||||
|
.idea
|
||||||
@@ -38,12 +38,15 @@ RUN apt update \
|
|||||||
# Build libimobiledevice
|
# Build libimobiledevice
|
||||||
# ----------------------
|
# ----------------------
|
||||||
RUN git clone https://github.com/libimobiledevice/libplist \
|
RUN git clone https://github.com/libimobiledevice/libplist \
|
||||||
|
&& git clone https://github.com/libimobiledevice/libimobiledevice-glue \
|
||||||
&& git clone https://github.com/libimobiledevice/libusbmuxd \
|
&& git clone https://github.com/libimobiledevice/libusbmuxd \
|
||||||
&& git clone https://github.com/libimobiledevice/libimobiledevice \
|
&& git clone https://github.com/libimobiledevice/libimobiledevice \
|
||||||
&& git clone https://github.com/libimobiledevice/usbmuxd \
|
&& git clone https://github.com/libimobiledevice/usbmuxd \
|
||||||
|
|
||||||
&& cd libplist && ./autogen.sh && make && make install && ldconfig \
|
&& cd libplist && ./autogen.sh && make && make install && ldconfig \
|
||||||
|
|
||||||
|
&& cd ../libimobiledevice-glue && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr && make && make install && ldconfig \
|
||||||
|
|
||||||
&& cd ../libusbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh && make && make install && ldconfig \
|
&& cd ../libusbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh && make && make install && ldconfig \
|
||||||
|
|
||||||
&& cd ../libimobiledevice && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --enable-debug && make && make install && ldconfig \
|
&& cd ../libimobiledevice && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --enable-debug && make && make install && ldconfig \
|
||||||
@@ -51,7 +54,7 @@ RUN git clone https://github.com/libimobiledevice/libplist \
|
|||||||
&& cd ../usbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --runstatedir=/run && make && make install \
|
&& cd ../usbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --runstatedir=/run && make && make install \
|
||||||
|
|
||||||
# Clean up.
|
# Clean up.
|
||||||
&& cd .. && rm -rf libplist libusbmuxd libimobiledevice usbmuxd
|
&& cd .. && rm -rf libplist libimobiledevice-glue libusbmuxd libimobiledevice usbmuxd
|
||||||
|
|
||||||
# Installing MVT
|
# Installing MVT
|
||||||
# --------------
|
# --------------
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -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.
|
||||||
|
|
||||||
@@ -15,15 +17,15 @@ It has been developed and released by the [Amnesty International Security Lab](h
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
MVT can be installed from sources or from [PyPi](https://pypi.org/project/mvt/) (you will need some dependencies, check the [documentation](https://docs.mvt.re/en/latest/install.html)):
|
MVT can be installed from sources or from [PyPi](https://pypi.org/project/mvt/) (you will need some dependencies, check the [documentation](https://docs.mvt.re/en/latest/install/)):
|
||||||
|
|
||||||
```
|
```
|
||||||
pip3 install mvt
|
pip3 install mvt
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://docs.mvt.re/en/latest/docker.html).
|
Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://docs.mvt.re/en/latest/docker/).
|
||||||
|
|
||||||
**Please note:** MVT is best run on Linux or Mac systems. [It does not currently support running natively on Windows.](https://docs.mvt.re/en/latest/install.html#mvt-on-windows)
|
**Please note:** MVT is best run on Linux or Mac systems. [It does not currently support running natively on Windows.](https://docs.mvt.re/en/latest/install/#mvt-on-windows)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -31,4 +33,4 @@ MVT provides two commands `mvt-ios` and `mvt-android`. [Check out the documentat
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The purpose of MVT is to facilitate the ***consensual forensic analysis*** of devices of those who might be targets of sophisticated mobile spyware attacks, especially members of civil society and marginalized communities. We do not want MVT to enable privacy violations of non-consenting individuals. In order to achieve this, MVT is released under its own license. [Read more here.](https://docs.mvt.re/en/latest/license.html)
|
The purpose of MVT is to facilitate the ***consensual forensic analysis*** of devices of those who might be targets of sophisticated mobile spyware attacks, especially members of civil society and marginalized communities. We do not want MVT to enable privacy violations of non-consenting individuals. In order to achieve this, MVT is released under its own license. [Read more here.](https://docs.mvt.re/en/latest/license/)
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ adb backup -all
|
|||||||
|
|
||||||
## Unpack the backup
|
## Unpack the backup
|
||||||
|
|
||||||
In order to reliable unpack th [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:
|
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:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar ~/path/to/abe.jar unpack backup.ab backup.tar
|
java -jar ~/path/to/abe.jar unpack backup.ab backup.tar
|
||||||
@@ -31,6 +31,8 @@ tar xvf backup.tar
|
|||||||
|
|
||||||
If the backup is encrypted, ABE will prompt you to enter the password.
|
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.
|
||||||
|
|
||||||
## Check the backup
|
## Check the backup
|
||||||
|
|
||||||
You can then extract SMSs containing links with MVT:
|
You can then extract SMSs containing links with MVT:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ However, not all is lost.
|
|||||||
|
|
||||||
Because malware attacks over Android typically take the form of malicious or backdoored apps, the very first thing you might want to do is to extract and verify all installed Android packages and triage quickly if there are any which stand out as malicious or which might be atypical.
|
Because malware attacks over Android typically take the form of malicious or backdoored apps, the very first thing you might want to do is to extract and verify all installed Android packages and triage quickly if there are any which stand out as malicious or which might be atypical.
|
||||||
|
|
||||||
While it is out of the scope of this documentation to dwell into details on how to analyze Android apps, MVT does allow to easily and automatically extract information about installed apps, download copies of them, and quickly lookup services such as [VirusTotal](https://www.virustotal.com) or [Koodous](https://www.koodous.com) which might quickly indicate known bad apps.
|
While it is out of the scope of this documentation to dwell into details on how to analyze Android apps, MVT does allow to easily and automatically extract information about installed apps, download copies of them, and quickly lookup services such as [VirusTotal](https://www.virustotal.com) or [Koodous](https://koodous.com) which might quickly indicate known bad apps.
|
||||||
|
|
||||||
|
|
||||||
## Check the device over Android Debug Bridge
|
## Check the device over Android Debug Bridge
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
10
docs/iocs.md
10
docs/iocs.md
@@ -28,9 +28,19 @@ The `--iocs` option can be invoked multiple times to let MVT import multiple STI
|
|||||||
mvt-ios check-backup --iocs ~/iocs/malware1.stix --iocs ~/iocs/malware2.stix2 /path/to/backup
|
mvt-ios check-backup --iocs ~/iocs/malware1.stix --iocs ~/iocs/malware2.stix2 /path/to/backup
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is also possible to load STIX2 files automatically from the environment variable `MVT_STIX2`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MVT_STIX2="/home/user/IOC1.stix2:/home/user/IOC2.stix2"
|
||||||
|
```
|
||||||
|
|
||||||
## Known repositories of STIX2 IOCs
|
## Known repositories of STIX2 IOCs
|
||||||
|
|
||||||
- The [Amnesty International investigations repository](https://github.com/AmnestyTech/investigations) contains STIX-formatted IOCs for:
|
- The [Amnesty International investigations repository](https://github.com/AmnestyTech/investigations) contains STIX-formatted IOCs for:
|
||||||
- [Pegasus](https://en.wikipedia.org/wiki/Pegasus_(spyware)) ([STIX2](https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-07-18_nso/pegasus.stix2))
|
- [Pegasus](https://en.wikipedia.org/wiki/Pegasus_(spyware)) ([STIX2](https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-07-18_nso/pegasus.stix2))
|
||||||
|
- [Predator from Cytrox](https://citizenlab.ca/2021/12/pegasus-vs-predator-dissidents-doubly-infected-iphone-reveals-cytrox-mercenary-spyware/) ([STIX2](https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-12-16_cytrox/cytrox.stix2))
|
||||||
|
- [This repository](https://github.com/Te-k/stalkerware-indicators) contains IOCs for Android stalkerware including [a STIX MVT-compatible file](https://raw.githubusercontent.com/Te-k/stalkerware-indicators/master/stalkerware.stix2).
|
||||||
|
|
||||||
|
You can automaticallly download the latest public indicator files with the command `mvt-ios download-iocs` or `mvt-android download-iocs`. These commands download the list of indicators listed [here](https://github.com/mvt-project/mvt/blob/main/public_indicators.json) and store them in the [appdir](https://pypi.org/project/appdirs/) folder. They are then loaded automatically by mvt.
|
||||||
|
|
||||||
Please [open an issue](https://github.com/mvt-project/mvt/issues/) to suggest new sources of STIX-formatted IOCs.
|
Please [open an issue](https://github.com/mvt-project/mvt/issues/) to suggest new sources of STIX-formatted IOCs.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
mkdocs==1.2.1
|
mkdocs==1.2.3
|
||||||
mkdocs-autorefs
|
mkdocs-autorefs
|
||||||
mkdocs-material
|
mkdocs-material
|
||||||
mkdocs-material-extensions
|
mkdocs-material-extensions
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
@@ -7,17 +7,16 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from mvt.common.module import InsufficientPrivileges
|
from mvt.common.module import InsufficientPrivileges
|
||||||
from mvt.common.utils import get_sha256_from_file_path
|
|
||||||
|
|
||||||
from .modules.adb.base import AndroidExtraction
|
from .modules.adb.base import AndroidExtraction
|
||||||
from .modules.adb.packages import Packages
|
from .modules.adb.packages import Packages
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Would be better to replace tqdm with rich.progress to reduce
|
# TODO: Would be better to replace tqdm with rich.progress to reduce
|
||||||
# the number of dependencies. Need to investigate whether
|
# the number of dependencies. Need to investigate whether
|
||||||
# it's possible to have a similar callback system.
|
# it's possible to have a similar callback system.
|
||||||
@@ -32,7 +31,10 @@ class PullProgress(tqdm):
|
|||||||
|
|
||||||
class DownloadAPKs(AndroidExtraction):
|
class DownloadAPKs(AndroidExtraction):
|
||||||
"""DownloadAPKs is the main class operating the download of APKs
|
"""DownloadAPKs is the main class operating the download of APKs
|
||||||
from the device."""
|
from the device.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, output_folder=None, all_apks=False, log=None,
|
def __init__(self, output_folder=None, all_apks=False, log=None,
|
||||||
packages=None):
|
packages=None):
|
||||||
@@ -51,17 +53,21 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json_path):
|
def from_json(cls, json_path):
|
||||||
"""Initialize this class from an existing apks.json file.
|
"""Initialize this class from an existing apks.json file.
|
||||||
|
|
||||||
: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)
|
||||||
|
|
||||||
def pull_package_file(self, package_name, remote_path):
|
def pull_package_file(self, package_name, remote_path):
|
||||||
"""Pull files related to specific package from the device.
|
"""Pull files related to specific package from the device.
|
||||||
|
|
||||||
:param package_name: Name of the package to download
|
:param package_name: Name of the package to download
|
||||||
:param remote_path: Path to the file to download
|
:param remote_path: Path to the file to download
|
||||||
:returns: Path to the local copy
|
:returns: Path to the local copy
|
||||||
|
|
||||||
"""
|
"""
|
||||||
log.info("Downloading %s ...", remote_path)
|
log.info("Downloading %s ...", remote_path)
|
||||||
|
|
||||||
@@ -101,6 +107,8 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
def get_packages(self):
|
def get_packages(self):
|
||||||
"""Use the Packages adb module to retrieve the list of packages.
|
"""Use the Packages adb module to retrieve the list of packages.
|
||||||
We reuse the same extraction logic to then download the APKs.
|
We reuse the same extraction logic to then download the APKs.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.log.info("Retrieving list of installed packages...")
|
self.log.info("Retrieving list of installed packages...")
|
||||||
|
|
||||||
@@ -111,8 +119,7 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
self.packages = m.results
|
self.packages = m.results
|
||||||
|
|
||||||
def pull_packages(self):
|
def pull_packages(self):
|
||||||
"""Download all files of all selected packages from the device.
|
"""Download all files of all selected packages from the device."""
|
||||||
"""
|
|
||||||
log.info("Starting extraction of installed APKs at folder %s", self.output_folder)
|
log.info("Starting extraction of installed APKs at folder %s", self.output_folder)
|
||||||
|
|
||||||
if not os.path.exists(self.output_folder):
|
if not os.path.exists(self.output_folder):
|
||||||
@@ -131,7 +138,7 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
packages_selection.append(package)
|
packages_selection.append(package)
|
||||||
|
|
||||||
log.info("Selected only %d packages which are not marked as system",
|
log.info("Selected only %d packages which are not marked as system",
|
||||||
len(packages_selection))
|
len(packages_selection))
|
||||||
|
|
||||||
if len(packages_selection) == 0:
|
if len(packages_selection) == 0:
|
||||||
log.info("No packages were selected for download")
|
log.info("No packages were selected for download")
|
||||||
@@ -150,50 +157,27 @@ class DownloadAPKs(AndroidExtraction):
|
|||||||
log.info("[%d/%d] Package: %s", counter, len(packages_selection),
|
log.info("[%d/%d] Package: %s", counter, len(packages_selection),
|
||||||
package["package_name"])
|
package["package_name"])
|
||||||
|
|
||||||
# Get the file path for the specific package.
|
|
||||||
try:
|
|
||||||
output = self._adb_command(f"pm path {package['package_name']}")
|
|
||||||
output = output.strip().replace("package:", "")
|
|
||||||
if not output:
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
|
||||||
log.exception("Failed to get path of package %s: %s",
|
|
||||||
package["package_name"], e)
|
|
||||||
self._adb_reconnect()
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Sometimes the package path contains multiple lines for multiple apks.
|
# Sometimes the package path contains multiple lines for multiple apks.
|
||||||
# We loop through each line and download each file.
|
# We loop through each line and download each file.
|
||||||
for path in output.split("\n"):
|
for package_file in package["files"]:
|
||||||
device_path = path.strip()
|
device_path = package_file["path"]
|
||||||
file_path = self.pull_package_file(package["package_name"],
|
local_path = self.pull_package_file(package["package_name"],
|
||||||
device_path)
|
device_path)
|
||||||
if not file_path:
|
if not local_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
file_info = {
|
package_file["local_path"] = local_path
|
||||||
"path": device_path,
|
|
||||||
"local_name": file_path,
|
|
||||||
"sha256": get_sha256_from_file_path(file_path),
|
|
||||||
}
|
|
||||||
|
|
||||||
if "files" not in package:
|
|
||||||
package["files"] = [file_info,]
|
|
||||||
else:
|
|
||||||
package["files"].append(file_info)
|
|
||||||
|
|
||||||
log.info("Download of selected packages completed")
|
log.info("Download of selected packages completed")
|
||||||
|
|
||||||
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):
|
||||||
"""Run all steps of fetch-apk.
|
"""Run all steps of fetch-apk."""
|
||||||
"""
|
|
||||||
self.get_packages()
|
self.get_packages()
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
self.pull_packages()
|
self.pull_packages()
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ from rich.text import Text
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def koodous_lookup(packages):
|
def koodous_lookup(packages):
|
||||||
log.info("Looking up all extracted files on Koodous (www.koodous.com)")
|
log.info("Looking up all extracted files on Koodous (www.koodous.com)")
|
||||||
log.info("This might take a while...")
|
log.info("This might take a while...")
|
||||||
@@ -32,7 +33,7 @@ def koodous_lookup(packages):
|
|||||||
res = requests.get(url)
|
res = requests.get(url)
|
||||||
report = res.json()
|
report = res.json()
|
||||||
|
|
||||||
row = [package["package_name"], file["local_name"]]
|
row = [package["package_name"], file["path"]]
|
||||||
|
|
||||||
if "package_name" in report:
|
if "package_name" in report:
|
||||||
trusted = "no"
|
trusted = "no"
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ from rich.text import Text
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_virustotal_report(hashes):
|
def get_virustotal_report(hashes):
|
||||||
apikey = "233f22e200ca5822bd91103043ccac138b910db79f29af5616a9afe8b6f215ad"
|
apikey = "233f22e200ca5822bd91103043ccac138b910db79f29af5616a9afe8b6f215ad"
|
||||||
url = f"https://www.virustotal.com/partners/sysinternals/file-reports?apikey={apikey}"
|
url = f"https://www.virustotal.com/partners/sysinternals/file-reports?apikey={apikey}"
|
||||||
@@ -36,7 +37,12 @@ def get_virustotal_report(hashes):
|
|||||||
log.error("Unexpected response from VirusTotal: %s", res.status_code)
|
log.error("Unexpected response from VirusTotal: %s", res.status_code)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
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 = []
|
||||||
@@ -48,6 +54,7 @@ def virustotal_lookup(packages):
|
|||||||
total_unique_hashes = len(unique_hashes)
|
total_unique_hashes = len(unique_hashes)
|
||||||
|
|
||||||
detections = {}
|
detections = {}
|
||||||
|
|
||||||
def virustotal_query(batch):
|
def virustotal_query(batch):
|
||||||
report = get_virustotal_report(batch)
|
report = get_virustotal_report(batch)
|
||||||
if not report:
|
if not report:
|
||||||
@@ -75,7 +82,7 @@ def virustotal_lookup(packages):
|
|||||||
|
|
||||||
for package in packages:
|
for package in packages:
|
||||||
for file in package.get("files", []):
|
for file in package.get("files", []):
|
||||||
row = [package["package_name"], file["local_name"]]
|
row = [package["package_name"], file["path"]]
|
||||||
|
|
||||||
if file["sha256"] in detections:
|
if file["sha256"] in detections:
|
||||||
detection = detections[file["sha256"]]
|
detection = detections[file["sha256"]]
|
||||||
|
|||||||
@@ -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,18 +1,29 @@
|
|||||||
# 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_batterystats import DumpsysBatterystats
|
from .dumpsys_accessibility import DumpsysAccessibility
|
||||||
from .dumpsys_packages import DumpsysPackages
|
from .dumpsys_activities import DumpsysActivities
|
||||||
from .dumpsys_procstats import DumpsysProcstats
|
from .dumpsys_battery_daily import DumpsysBatteryDaily
|
||||||
|
from .dumpsys_battery_history import DumpsysBatteryHistory
|
||||||
|
from .dumpsys_dbinfo import DumpsysDBInfo
|
||||||
|
from .dumpsys_full import DumpsysFull
|
||||||
|
from .dumpsys_receivers import DumpsysReceivers
|
||||||
|
from .files import Files
|
||||||
|
from .getprop import Getprop
|
||||||
|
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,
|
||||||
DumpsysBatterystats, DumpsysProcstats,
|
SELinuxStatus, DumpsysBatteryHistory, DumpsysBatteryDaily,
|
||||||
DumpsysPackages, Packages, RootBinaries]
|
DumpsysReceivers, DumpsysActivities, DumpsysAccessibility,
|
||||||
|
DumpsysDBInfo, DumpsysFull, Packages, Logcat, RootBinaries,
|
||||||
|
Files]
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ 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.common.module import InsufficientPrivileges, MVTModule
|
from mvt.common.module import InsufficientPrivileges, MVTModule
|
||||||
@@ -25,6 +25,7 @@ log = logging.getLogger(__name__)
|
|||||||
ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey")
|
ADB_KEY_PATH = os.path.expanduser("~/.android/adbkey")
|
||||||
ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub")
|
ADB_PUB_KEY_PATH = os.path.expanduser("~/.android/adbkey.pub")
|
||||||
|
|
||||||
|
|
||||||
class AndroidExtraction(MVTModule):
|
class AndroidExtraction(MVTModule):
|
||||||
"""This class provides a base for all Android extraction modules."""
|
"""This class provides a base for all Android extraction modules."""
|
||||||
|
|
||||||
@@ -37,9 +38,12 @@ class AndroidExtraction(MVTModule):
|
|||||||
self.device = None
|
self.device = None
|
||||||
self.serial = None
|
self.serial = None
|
||||||
|
|
||||||
def _adb_check_keys(self):
|
@staticmethod
|
||||||
"""Make sure Android adb keys exist.
|
def _adb_check_keys():
|
||||||
"""
|
"""Make sure Android adb keys exist."""
|
||||||
|
if not os.path.isdir(os.path.dirname(ADB_KEY_PATH)):
|
||||||
|
os.makedirs(os.path.dirname(ADB_KEY_PATH))
|
||||||
|
|
||||||
if not os.path.exists(ADB_KEY_PATH):
|
if not os.path.exists(ADB_KEY_PATH):
|
||||||
keygen(ADB_KEY_PATH)
|
keygen(ADB_KEY_PATH)
|
||||||
|
|
||||||
@@ -47,19 +51,25 @@ class AndroidExtraction(MVTModule):
|
|||||||
write_public_keyfile(ADB_KEY_PATH, ADB_PUB_KEY_PATH)
|
write_public_keyfile(ADB_KEY_PATH, ADB_PUB_KEY_PATH)
|
||||||
|
|
||||||
def _adb_connect(self):
|
def _adb_connect(self):
|
||||||
"""Connect to the device over adb.
|
"""Connect to the device over adb."""
|
||||||
"""
|
|
||||||
self._adb_check_keys()
|
self._adb_check_keys()
|
||||||
|
|
||||||
with open(ADB_KEY_PATH, "rb") as handle:
|
with open(ADB_KEY_PATH, "rb") as handle:
|
||||||
priv_key = handle.read()
|
priv_key = handle.read()
|
||||||
|
|
||||||
signer = PythonRSASigner("", priv_key)
|
with open(ADB_PUB_KEY_PATH, "rb") as handle:
|
||||||
|
pub_key = handle.read()
|
||||||
|
|
||||||
|
signer = PythonRSASigner(pub_key, priv_key)
|
||||||
|
|
||||||
# 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(":")
|
||||||
@@ -84,53 +94,59 @@ class AndroidExtraction(MVTModule):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == 113 and self.serial:
|
if e.errno == 113 and self.serial:
|
||||||
log.critical("Unable to connect to the device %s: did you specify the correct IP addres?",
|
log.critical("Unable to connect to the device %s: did you specify the correct IP addres?",
|
||||||
self.serial)
|
self.serial)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
def _adb_disconnect(self):
|
def _adb_disconnect(self):
|
||||||
"""Close adb connection to the device.
|
"""Close adb connection to the device."""
|
||||||
"""
|
|
||||||
self.device.close()
|
self.device.close()
|
||||||
|
|
||||||
def _adb_reconnect(self):
|
def _adb_reconnect(self):
|
||||||
"""Reconnect to device using adb.
|
"""Reconnect to device using adb."""
|
||||||
"""
|
|
||||||
log.info("Reconnecting ...")
|
log.info("Reconnecting ...")
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
def _adb_command(self, command):
|
def _adb_command(self, command):
|
||||||
"""Execute an adb shell command.
|
"""Execute an adb shell command.
|
||||||
|
|
||||||
:param command: Shell command to execute
|
:param command: Shell command to execute
|
||||||
:returns: Output of command
|
:returns: Output of command
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.device.shell(command)
|
return self.device.shell(command, read_timeout_s=200.0)
|
||||||
|
|
||||||
def _adb_check_if_root(self):
|
def _adb_check_if_root(self):
|
||||||
"""Check if we have a `su` binary on the Android device.
|
"""Check if we have a `su` binary on the Android device.
|
||||||
|
|
||||||
|
|
||||||
:returns: Boolean indicating whether a `su` binary is present or not
|
:returns: Boolean indicating whether a `su` binary is present or not
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return bool(self._adb_command("command -v su"))
|
return bool(self._adb_command("command -v su"))
|
||||||
|
|
||||||
def _adb_root_or_die(self):
|
def _adb_root_or_die(self):
|
||||||
"""Check if we have a `su` binary, otherwise raise an Exception.
|
"""Check if we have a `su` binary, otherwise raise an Exception."""
|
||||||
"""
|
|
||||||
if not self._adb_check_if_root():
|
if not self._adb_check_if_root():
|
||||||
raise InsufficientPrivileges("This module is optionally available in case the device is already rooted. Do NOT root your own device!")
|
raise InsufficientPrivileges("This module is optionally available in case the device is already rooted. Do NOT root your own device!")
|
||||||
|
|
||||||
def _adb_command_as_root(self, command):
|
def _adb_command_as_root(self, command):
|
||||||
"""Execute an adb shell command.
|
"""Execute an adb shell command.
|
||||||
|
|
||||||
:param command: Shell command to execute as root
|
:param command: Shell command to execute as root
|
||||||
:returns: Output of command
|
:returns: Output of command
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self._adb_command(f"su -c {command}")
|
return self._adb_command(f"su -c {command}")
|
||||||
|
|
||||||
def _adb_check_file_exists(self, file):
|
def _adb_check_file_exists(self, file):
|
||||||
"""Verify that a file exists.
|
"""Verify that a file exists.
|
||||||
|
|
||||||
:param file: Path of the file
|
:param file: Path of the file
|
||||||
:returns: Boolean indicating whether the file exists or not
|
:returns: Boolean indicating whether the file exists or not
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Need to support checking files without root privileges as well.
|
# TODO: Need to support checking files without root privileges as well.
|
||||||
@@ -144,9 +160,12 @@ class AndroidExtraction(MVTModule):
|
|||||||
|
|
||||||
def _adb_download(self, remote_path, local_path, progress_callback=None, retry_root=True):
|
def _adb_download(self, remote_path, local_path, progress_callback=None, retry_root=True):
|
||||||
"""Download a file form the device.
|
"""Download a file form the device.
|
||||||
|
|
||||||
:param remote_path: Path to download from the device
|
:param remote_path: Path to download from the device
|
||||||
:param local_path: Path to where to locally store the copy of the file
|
:param local_path: Path to where to locally store the copy of the file
|
||||||
:param progress_callback: Callback for download progress bar
|
:param progress_callback: Callback for download progress bar (Default value = None)
|
||||||
|
:param retry_root: Default value = True)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.device.pull(remote_path, local_path, progress_callback)
|
self.device.pull(remote_path, local_path, progress_callback)
|
||||||
@@ -155,7 +174,7 @@ class AndroidExtraction(MVTModule):
|
|||||||
self._adb_download_root(remote_path, local_path, progress_callback)
|
self._adb_download_root(remote_path, local_path, progress_callback)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unable to download file {remote_path}: {e}")
|
raise Exception(f"Unable to download file {remote_path}: {e}")
|
||||||
|
|
||||||
def _adb_download_root(self, remote_path, local_path, progress_callback=None):
|
def _adb_download_root(self, remote_path, local_path, progress_callback=None):
|
||||||
try:
|
try:
|
||||||
# Check if we have root, if not raise an Exception.
|
# Check if we have root, if not raise an Exception.
|
||||||
@@ -180,16 +199,18 @@ class AndroidExtraction(MVTModule):
|
|||||||
|
|
||||||
# Delete the copy on /sdcard/.
|
# Delete the copy on /sdcard/.
|
||||||
self._adb_command(f"rm -rf {new_remote_path}")
|
self._adb_command(f"rm -rf {new_remote_path}")
|
||||||
|
|
||||||
except AdbCommandFailureException as e:
|
except AdbCommandFailureException as e:
|
||||||
raise Exception(f"Unable to download file {remote_path}: {e}")
|
raise Exception(f"Unable to download file {remote_path}: {e}")
|
||||||
|
|
||||||
def _adb_process_file(self, remote_path, process_routine):
|
def _adb_process_file(self, remote_path, process_routine):
|
||||||
"""Download a local copy of a file which is only accessible as root.
|
"""Download a local copy of a file which is only accessible as root.
|
||||||
This is a wrapper around process_routine.
|
This is a wrapper around process_routine.
|
||||||
|
|
||||||
:param remote_path: Path of the file on the device to process
|
:param remote_path: Path of the file on the device to process
|
||||||
:param process_routine: Function to be called on the local copy of the
|
:param process_routine: Function to be called on the local copy of the
|
||||||
downloaded file
|
downloaded file
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Connect to the device over adb.
|
# Connect to the device over adb.
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
@@ -223,6 +244,5 @@ class AndroidExtraction(MVTModule):
|
|||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
|
|
||||||
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/
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History"
|
CHROME_HISTORY_PATH = "data/data/com.android.chrome/app_chrome/Default/History"
|
||||||
|
|
||||||
|
|
||||||
class ChromeHistory(AndroidExtraction):
|
class ChromeHistory(AndroidExtraction):
|
||||||
"""This module extracts records from Android's Chrome browsing history."""
|
"""This module extracts records from Android's Chrome browsing history."""
|
||||||
|
|
||||||
@@ -33,9 +34,19 @@ class ChromeHistory(AndroidExtraction):
|
|||||||
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
"data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def check_indicators(self):
|
||||||
|
if not self.indicators:
|
||||||
|
return
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
if self.indicators.check_domain(result["url"]):
|
||||||
|
self.detected.append(result)
|
||||||
|
|
||||||
def _parse_db(self, db_path):
|
def _parse_db(self, db_path):
|
||||||
"""Parse a Chrome History database file.
|
"""Parse a Chrome History database file.
|
||||||
|
|
||||||
:param db_path: Path to the History database to process.
|
:param db_path: Path to the History database to process.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
conn = sqlite3.connect(db_path)
|
conn = sqlite3.connect(db_path)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
@@ -57,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],
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -67,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)
|
||||||
|
|||||||
45
mvt/android/modules/adb/dumpsys_accessibility.py
Normal file
45
mvt/android/modules/adb/dumpsys_accessibility.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_accessibility
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysAccessibility(AndroidExtraction):
|
||||||
|
"""This module extracts stats on accessibility."""
|
||||||
|
|
||||||
|
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 accessibility")
|
||||||
|
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))
|
||||||
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,45 +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/
|
||||||
|
|
||||||
@@ -10,8 +10,9 @@ from .base import AndroidExtraction
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class DumpsysPackages(AndroidExtraction):
|
|
||||||
"""This module extracts stats on installed packages."""
|
class DumpsysFull(AndroidExtraction):
|
||||||
|
"""This module extracts stats on battery consumption by processes."""
|
||||||
|
|
||||||
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
def __init__(self, file_path=None, base_folder=None, output_folder=None,
|
||||||
serial=None, fast_mode=False, log=None, results=[]):
|
serial=None, fast_mode=False, log=None, results=[]):
|
||||||
@@ -22,14 +23,12 @@ class DumpsysPackages(AndroidExtraction):
|
|||||||
def run(self):
|
def run(self):
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
|
|
||||||
output = self._adb_command("dumpsys package")
|
output = self._adb_command("dumpsys")
|
||||||
if self.output_folder:
|
if self.output_folder:
|
||||||
packages_path = os.path.join(self.output_folder,
|
output_path = os.path.join(self.output_folder, "dumpsys.txt")
|
||||||
"dumpsys_packages.txt")
|
with open(output_path, "w", encoding="utf-8") as handle:
|
||||||
with open(packages_path, "w") as handle:
|
|
||||||
handle.write(output)
|
handle.write(output)
|
||||||
|
|
||||||
log.info("Records from dumpsys package stored at %s",
|
log.info("Full dumpsys output stored at %s", output_path)
|
||||||
packages_path)
|
|
||||||
|
|
||||||
self._adb_disconnect()
|
self._adb_disconnect()
|
||||||
66
mvt/android/modules/adb/dumpsys_receivers.py
Normal file
66
mvt/android/modules/adb/dumpsys_receivers.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
|
||||||
|
|
||||||
|
from mvt.android.parsers import parse_dumpsys_receiver_resolver_table
|
||||||
|
|
||||||
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
INTENT_NEW_OUTGOING_SMS = "android.provider.Telephony.NEW_OUTGOING_SMS"
|
||||||
|
INTENT_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"
|
||||||
|
INTENT_DATA_SMS_RECEIVED = "android.intent.action.DATA_SMS_RECEIVED"
|
||||||
|
INTENT_PHONE_STATE = "android.intent.action.PHONE_STATE"
|
||||||
|
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=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, 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):
|
||||||
|
self._adb_connect()
|
||||||
|
|
||||||
|
output = self._adb_command("dumpsys package")
|
||||||
|
self.results = parse_dumpsys_receiver_resolver_table(output)
|
||||||
|
|
||||||
|
self._adb_disconnect()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user