Android Messaging Apps Forensics: WhatsApp to RCS
How to acquire and analyse WhatsApp, Signal, Telegram and Google Messages on Android: where each stores chats, what encryption blocks, and what survives.
On most phones seized today, the conversations that matter are not in the SMS database. They are in WhatsApp, Signal, Telegram or the RCS side of Google Messages, and each of those apps stores, encrypts and expires its data differently. This guide compares the four, explains what each extraction level reaches, and gives a workflow for building one conversation record out of several stores. Detailed schemas and queries are on the artifact pages: WhatsApp, Signal, Telegram and Google Messages.
The four apps at a glance
| App | Main store | Local encryption beyond FBE | Cloud copy | Best extraction |
|---|---|---|---|---|
msgstore.db, wa.db in /data/data/com.whatsapp/databases/ | None on the live database; local backups .crypt14/.crypt15 | Optional Google Drive backup (can be end-to-end encrypted) | Full file system, AFU | |
| Signal | signal.db in /data/data/org.thoughtcrime.securesms/databases/ | SQLCipher, key wrapped by the Android Keystore; attachments encrypted | None by default; optional encrypted backups | Full file system with on-device key unwrapping, AFU |
| Telegram | cache4.db in /data/data/org.telegram.messenger/files/ | None; content TL-serialized | Cloud chats live on Telegram's servers | Full file system, AFU, plus a cloud export with cooperation |
| Google Messages | bugle_db in /data/data/com.google.android.apps.messaging/databases/ | None | Google account backup of SMS/MMS; RCS on the network only in transit | Full file system, AFU |
Two constants run through the table. First, every main store is in the app sandbox, which is credential-encrypted storage: you need a full file system extraction on a phone that is AFU. Second, "end-to-end encrypted" describes the network path, not the database. Only Signal adds its own at-rest encryption.
Acquisition: what each level reaches
Logical extraction
A logical extraction over ADB, with or without an agent app, reaches only what these apps put in shared storage:
- WhatsApp: encrypted local backups and all media under
/sdcard/Android/media/com.whatsapp/WhatsApp/. - Telegram: downloaded media under
/sdcard/Android/data/org.telegram.messenger/files/Telegram/(and/sdcard/Telegram/on old versions). - Signal:
.backupfiles, if the user enabled local backups and chose a shared folder. - Google Messages: nothing directly. An agent reading
content://smsandcontent://mmsreturns SMS and MMS from the telephony provider, but not RCS.
That is still useful. WhatsApp media file names carry dates, a WhatsApp backup can be decrypted later if the key is obtained, and a Signal backup with its passphrase is a full copy of the history. But state clearly in the report that the live databases were not acquired.
ADB backup and downgrades
None of the four current apps allows adb backup of its data, and since Android 12 the system excludes apps targeting API 31 or higher anyway. The APK downgrade technique (installing an old WhatsApp that allowed backup, backing up, restoring the current version) still exists in commercial tools. It alters the device, can fail midway, and on Android 14 and later needs --bypass-low-target-sdk-block to install old APKs. Use it only when nothing else is possible, and document it in full. See ADB backup format for what the resulting file contains.
Full file system extraction
An AFU full file system extraction is the target for all four apps. Make sure the tool collects each database with its -wal and -shm files, the shared_prefs folder, and the files and cache folders:
- WhatsApp:
databases/,files/key(crypt14 backup key),shared_prefs/. - Telegram:
files/cache4.db,files/account*/,shared_prefs/,cache/. - Signal:
databases/,shared_prefs/,app_parts/, and the decrypted database if the tool unwrapped the key on the device. - Google Messages:
databases/bugle_db,shared_prefs/,files/andcache/.
If the phone is BFU, none of this is readable. The telephony provider (SMS and MMS) often is, because it sits in device-encrypted storage on most builds.
WhatsApp is the easiest of the four once you have the sandbox: msgstore.db is plain SQLite with a documented community schema, and wa.db resolves JIDs to names. The work is in the details.
- Schema generation. Current builds use
message,chatandjidtables; older databases and backups still use the legacymessagestable. Check.schemabefore running saved queries. - Backups as a second timeline. Local backups are written nightly. A message deleted today may still be in last night's
msgstore.db.crypt14, which the sandboxkeyfile decrypts. crypt15 end-to-end encrypted backups need the 64-digit key or the password. - Deleted for everyone. A revoked message leaves a placeholder row with its timestamp: evidence that something was sent even if the content is gone.
- Calls. WhatsApp calls are in
call_loginsidemsgstore.db, never in the system call log.
A decrypted msgstore.db with wa.db can be reviewed in Messaging Forensics directly in the browser, WAL included; ALEAPP and the commercial suites also parse it.
Signal
Signal is the hard case, and it is important to understand exactly why. The database is SQLCipher-encrypted with a random secret generated at install. The secret is stored in the app's preferences, encrypted by a key in the Android Keystore. On most phones that Keystore key is hardware-backed and cannot be exported, so the secret can only be unwrapped by code running on that phone, while it is AFU.
Your options, in order of completeness:
- A tool that supports Signal on your device and version, performing the unwrap during a full file system extraction.
- A local
.backupfile plus its 30-digit passphrase, which only the user knows.signalbackup-toolsdecrypts it into a SQLite database. - The counterpart's device, if it is available and lawfully examinable. It raises the same Signal problem, but its owner may cooperate or its model may be supported by your tools.
- Side channels on the same phone: notification history (if enabled, and if Signal's notification content was not hidden), recent tasks snapshots (blank when Screen security is on), usagestats for when Signal was in the foreground, and the keyboard app's own databases, which on some versions keep fragments of typed text (when Signal's Incognito keyboard option is on, it asks keyboards not to learn from its fields).
Also record whether disappearing messages were active: the thread table (after decryption) or the app's UI shows the timer, which explains gaps.
Telegram
Telegram looks easy because cache4.db is not encrypted, but it is a cache. Cloud chats, groups and channels are authoritative on Telegram's servers, and the phone holds only what the app loaded. Scrolling back in a chat downloads more history; clearing the cache removes it locally without touching the server.
Practical points:
- Message bodies are TL-serialized blobs in
messages_v2.data. Metadata (dialog, date, direction, read state) is plain SQL; content needs a parser that understands the current TL layer. - Times are Unix seconds, unlike most Android databases.
- Several logged-in accounts get separate
account1/,account2/folders. - Secret chats are device-bound and never in the cloud: the phone is the only source, and self-destruct timers may have removed messages.
- With the user's cooperation, Telegram Desktop can export an account's cloud history as JSON, which covers chats never cached on the phone.
Google Messages and RCS
Google Messages is the default SMS app on Pixels and many other phones, and it is the main RCS client. The trap is that RCS chats never reach the telephony provider. The system mmssms.db has SMS and MMS; bugle_db has SMS, MMS and RCS.
- Always collect
bugle_dbwhen Google Messages is installed, even if you already havemmssms.db. - Link the two stores through
messages.sms_message_uri. An SMS present in one and missing from the other signals deletion or a change of default SMS app. message_protocolandmessage_statuscodes are undocumented: calibrate them with messages whose type you know.- Samsung Messages and other OEM apps keep RCS in their own stores; verify on the build you have.
Building one conversation record
A single chat can leave traces in five or six places. Merge them before drawing conclusions:
- Primary database (msgstore.db, signal.db, cache4.db, bugle_db): content and timestamps.
- Backups and exports: older or deleted content.
- Notifications: previews of incoming messages with posting times.
- App usage from usagestats and Digital Wellbeing: was the app on screen when an outgoing message was created?
- Media in shared storage and MediaStore: files received and sent, with creator package.
- Contacts: contacts2.db names for numbers, which may differ from in-app profile names.
Normalise every timestamp to UTC first: WhatsApp, Signal and Google Messages use Unix milliseconds, Telegram Unix seconds, notification history milliseconds, and usagestats offsets from a file base time. The timeline analysis guide covers the conversions and merging.
Deleted messages
- SQLite residue: deleted rows often remain in free pages and in the WAL of WhatsApp, Telegram and Google Messages until a vacuum or checkpoint. Signal's SQLCipher configuration makes this much less productive.
- Placeholders: WhatsApp and Signal leave rows for messages deleted for everyone.
- Backups: WhatsApp's nightly local backups, Signal
.backupfiles. - Notifications and snapshots: content that reached the screen may survive there.
- Media: files can outlive the message that carried them.
Report deletion findings carefully: a missing row proves absence in this store at extraction time, not when or why it disappeared.
Media and attachments
Attachments are where the four apps differ most, and where examiners most often lose track of provenance:
- WhatsApp saves received and sent media in shared storage with names such as
IMG-20260914-WA0003.jpg, where the date is the save date and the counter resets daily.message_medialinks each file to its message. EXIF is stripped on sending. - Telegram keeps downloaded media in its own folders in shared storage and caches the rest in the sandbox;
media_v4and the message blobs reference them. Files not opened by the user may never have been downloaded. - Signal stores attachments encrypted in
app_parts/; they are readable only after the same key unwrapping as the database. Files the user explicitly saved to the gallery appear in shared storage with no link back to Signal except the MediaStoreowner_package_name. - Google Messages references MMS and RCS attachments through the
partstable; the files may be in the app sandbox, in the telephony provider's part folder, or in a cache that is cleared over time.
In every case, match attachments to messages through the database rather than by file time alone, and hash each file you rely on.
Worked example: a deleted WhatsApp thread
A typical sequence on an AFU phone with a full file system extraction:
- The chat with a given number is absent from
msgstore.dband from the app's chat list. - The nightly local backup from two days earlier, decrypted with the sandbox
keyfile, contains 140 messages in that chat. - The WAL of the live database still holds pages with some of the most recent messages from the day of deletion.
- Notification history shows three incoming previews from the contact on the day of deletion, after the last backup.
- usagestats shows WhatsApp in the foreground for four minutes just before the WAL pages stop, with the chat activity class.
Together these show the conversation existed, what most of it said, and a window in which it was removed. None of them alone shows who removed it; say so.
Reporting pitfalls
- Do not call Signal or WhatsApp data "decrypted end-to-end messages": say what was acquired (the local database) and how.
- Separate the sender's clock (
date_sent, WhatsApptimestamp) from the device's receive time. - Name identifiers precisely: a WhatsApp
@lidis not a phone number, a Telegram user ID is not a phone number, a Signal ACI is a service identifier. - State which apps were present but not acquired, and why (BFU, unsupported version, no key).
Summary checklist
- Keep the phone AFU and powered; plan for a full file system extraction.
- Collect each database with WAL and SHM, plus preferences, files and caches.
- Pull shared-storage backups and media even when you have the sandbox.
- For Signal, confirm tool support before extraction; ask for backups and passphrases through the proper channel.
- For Telegram, consider a cloud export for full history.
- For Google Messages, always take
bugle_db: RCS lives only there. - Corroborate with notifications, usage, media and contacts before reporting.