Donncha Ó Cearbhaill 30c11f68c7 Add WhatsApp contacts module and fix InteractionC contact resolution (#882)
* Add WhatsappContacts module to extract WhatsApp disappearing messages state

WhatsApp on iOS stores the disappearing messages timer for 1:1 chats on
the contact records in ContactsV2.sqlite, not in ChatStorage.sqlite. Add
a new WhatsappContacts module which extracts contact records from this
database, including phone numbers, WhatsApp and LID identifiers, and the
per-contact disappearing messages duration, and emits a timeline event
when a disappearing messages timer was set.

The database is often missing from incremental backups, so the module
logs a clear warning and returns no results instead of failing. Columns
are selected based on the actual table schema to tolerate changes across
WhatsApp versions, and if the disappearing messages column is absent the
state is reported as unknown rather than off.

The test fixture is a synthetic ContactsV2.sqlite with fictional
contacts, stored under the backup file ID derived from the WhatsApp
shared app group domain.

* Fix InteractionC contact resolution and resolve WhatsApp LIDs to contacts

The two primary InteractionC queries contained a SQL syntax error in
their direction CASE expression (a double column alias), so they always
failed and the module silently fell back to a reduced query without the
recipient join. As a result outgoing messages were serialized with no
counterpart at all ("from None (None)"). Fix the syntax so recipient
names and identifiers are extracted again, and normalize the raw 0/1
direction values from the fallback queries to INCOMING/OUTGOING.

WhatsApp identifies chat peers in interactionC.db by LID and stores the
peer LID in the domain identifier, which InteractionC could not map to a
person. Declare a dependency on the WhatsappContacts module and resolve
sender, recipient and domain identifiers (LID, JID or phone number)
against the WhatsApp contacts database, adding resolved phone number and
name fields to WhatsApp records.

Rewrite the timeline serialization to use the resolved values, fall back
to the chat peer from the domain identifier when no recipient was
recorded, label the local user instead of printing None, and include the
message direction and group name.

* Add timeline events for all WhatsApp contact timestamps

Extract ZABOUTEXPIRATIONTIMESTAMP and emit a timeline event for each
timestamp stored on a WhatsApp contact record: disappearing messages
timer changes, "about" text changes and scheduled expiry, and contact
record updates. ContactsV2.sqlite stores no other date attributes in
any released schema version.

* Add first and last interaction timeline events for WhatsApp chats

Extract one record per ZWACHATSESSION with the first and last stored
message dates, the session's own last-message date, the group creation
date and message counts. Each chat produces chat_first_message and
chat_last_message timeline events, and groups a group_created event.
The session last-message date is preferred over the newest stored
message because it survives message deletion.

* Resolve WhatsApp LID chat identifiers via the LID pair table

Recent WhatsApp versions key 1:1 chat sessions by an opaque LID rather
than the contact's phone number. Extract the ZWAPHONENUMBERLIDPAIR
table from the dedicated LID.sqlite database (or from ChatStorage
itself in versions that store it there) and use it to populate
partner_resolved_phone_number on chat session records and in timeline
events, without requiring the often-missing ContactsV2.sqlite. Each
pair is also extracted as a record and produces a lid_pair_recorded
timeline event marking when the association was learned.

* Reduce duplicate InteractionC timeline events

The interaction record's creation date normally trails its start date
by milliseconds, so serializing both nearly doubled the timeline with
duplicate entries. Only emit the creation date when it diverges from
the start date by more than an hour, with explicit wording, since a
record created long after its event indicates backfill by sync,
restore or tampering.

Per-contact aggregate dates from ZCONTACTS repeat on every interaction
row of the same contact and carried that row's message text. Serialize
them with contact-centric data strings instead, so timeline
de-duplication collapses them into one first/last-seen event per
contact.
2026-08-19 14:07:27 +02:00
2024-06-24 18:50:42 +02:00
2024-12-13 22:45:41 +01:00
2021-07-25 12:01:23 +02:00
2026-08-05 23:30:28 +02:00
2026-08-05 23:30:28 +02:00

Mobile Verification Toolkit

Important

We recently merged the "v3" branch. This introduced breaking changes. If you relied on mvt output in other scripts They might have broken. More details: https://github.com/mvt-project/mvt/issues/757

Documentation Status CI Downloads

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.

It has been developed and released by the Amnesty International Security Lab in July 2021 in the context of the Pegasus Project along with a technical forensic methodology. It continues to be maintained by Amnesty International and other contributors.

Note

MVT is a forensic research tool intended for technologists and investigators. It requires understanding digital forensics and using command-line tools. This is not intended for end-user self-assessment. If you are concerned with the security of your device please seek reputable expert assistance.

Indicators of Compromise

MVT supports using public indicators of compromise (IOCs) to scan mobile devices for potential traces of targeting or infection by known spyware campaigns. This includes IOCs published by Amnesty International and other research groups.

Warning

Public indicators of compromise are insufficient to determine that a device is "clean", and not targeted with a particular spyware tool. Reliance on public indicators alone can miss recent forensic traces and give a false sense of security.

Reliable and comprehensive digital forensic support and triage requires access to non-public indicators, research and threat intelligence.

Such support is available to civil society through Amnesty International's Security Lab or through our forensic partnership with Access Nows Digital Security Helpline.

More information about using indicators of compromise with MVT is available in the documentation.

Installation

MVT can be installed from sources or from PyPI (you will need some dependencies, check the documentation):

pip3 install mvt

You can also install MVT from PyPI with uv. First, install uv:

curl -LsSf https://astral.sh/uv/install.sh | sh

Then install MVT as a command-line tool:

uv tool install mvt

For alternative installation options and known issues, please refer to the documentation as well as GitHub Issues.

Usage

MVT provides two commands mvt-ios and mvt-android. Check out the documentation to learn how to use them!

Shell completion

MVT can generate shell completion scripts for Bash, Zsh, and Fish:

mvt-ios completion
mvt-android completion

The commands print setup instructions by default. To generate a completion script directly, pass the shell name:

mvt-ios completion bash
mvt-android completion zsh

MVT only writes completion files or shell configuration when --install is passed. See the command completion documentation for details. Module-running check-* commands can load custom Python modules with --load-module PATH or from a folder set in MVT_CUSTOM_MODULES. See the development documentation for details.

Users can also add top-level commands to mvt-ios and mvt-android from installed Python packages or local files and folders. See the custom CLI command documentation for the plugin entry points and --load-command interface.

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.

S
Description
No description provided
Readme MPL-2.0
6.5 MiB
Languages
Python 92%
PureBasic 7.4%
Dockerfile 0.5%