WhatsApp on Android: msgstore.db and wa.db
WhatsApp's Android databases and backups: msgstore.db message and chat tables, wa.db contacts, crypt14 and crypt15 backups, media paths and deleted messages.
- Location
- /data/data/com.whatsapp/databases/msgstore.db
- Proves
- WhatsApp chats and groups, message text, direction, sender, sent and received times, media, calls, and contact names of chat partners
- Timestamps
- Unix milliseconds (UTC): message.timestamp, received_timestamp, receipt tables, chat.created_timestamp, call_log.timestamp
- Access
- Full file system (root, exploit or agent); credential-encrypted sandbox, so AFU only. Logical: only encrypted local backups and media in shared storage, plus user chat exports. ADB backup only via an APK downgrade method.
- Retention
- Until the user deletes messages or chats, disappearing-message timers expire, or the app is removed; local backups rotate (usually about 7 daily copies)
- Collection
- Full file system extraction, adb pull of /sdcard/Android/media/com.whatsapp, Chat export (user), APK downgrade (alters device)
Tools
Compare all tools- Messaging ForensicsIn browser
- ALEAPPCLI · open source
- wa-crypt-toolsCLI · open source
- sqlite3CLI · open source
- DB Browser for SQLiteGUI · open source
- Cellebrite UFEDPlatform · commercial
- Magnet AXIOMPlatform · commercial
- Oxygen Forensic DetectivePlatform · commercial
- MSAB XRYPlatform · commercial
- Belkasoft XPlatform · commercial
- Messaging Forensics: Android SMS: reads the sms table of mmssms.db (not MMS parts) and SMS Backup & Restore XML, which also carries MMS and calls. WhatsApp: needs a decrypted msgstore.db, not a .crypt14/.crypt15 backup. Telegram and Signal support covers their desktop apps only.
What it is
WhatsApp for Android keeps its entire message history in a local SQLite database, msgstore.db, and its view of the user's contacts in wa.db. Messages are end-to-end encrypted in transit, but on the phone the database itself is plain SQLite inside the app sandbox, protected only by Android's file-based encryption. WhatsApp also writes encrypted copies of the database to shared storage every night (msgstore.db.crypt14 or .crypt15) and, if the user enabled it, to Google Drive.
WhatsApp Business (com.whatsapp.w4b) uses the same layout under its own package name.
Where it lives
| Item | Path | Notes |
|---|---|---|
| Messages | /data/data/com.whatsapp/databases/msgstore.db (+ -wal, -shm) | Chats, messages, media metadata, receipts, calls |
| Contacts | /data/data/com.whatsapp/databases/wa.db | wa_contacts: JIDs, names, about text |
| Other databases | axolotl.db, chatsettings.db | Signal-protocol key material, per-chat settings |
| Backup key (crypt14) | /data/data/com.whatsapp/files/key | Decrypts local .crypt14 backups |
| Preferences | /data/data/com.whatsapp/shared_prefs/ | Registered number, settings, backup configuration |
| Local backups | /sdcard/Android/media/com.whatsapp/WhatsApp/Databases/ | msgstore.db.crypt14 / .crypt15 and dated copies msgstore-YYYY-MM-DD.1.db.crypt14; before the 2021 scoped-storage move: /sdcard/WhatsApp/Databases/ |
| Media | /sdcard/Android/media/com.whatsapp/WhatsApp/Media/ | Images, Video, Voice Notes, Documents, .Statuses |
The sandbox paths are the same on Android 8 to 16; the shared-storage location depends on the WhatsApp version rather than the Android version.
What it proves
- The chats and groups on the device (
chat, with groupsubject), the messages in each, and whether each was sent by the device owner (from_me = 1). - The sender of each group message (
sender_jid_row_id→jid), the message text (text_data) and its type (text, image, voice note, document, location, system event). - When a message was sent and when this device received it, and on outgoing messages when recipients received, read or played it (receipt tables).
- Media sent or received (file path, MIME type, size, and caption through the message text) and WhatsApp voice and video calls (
call_logtable). - That a message was deleted for everyone: a revoked placeholder row remains even though the text is gone.
It does not prove who held the phone, and the contact names in wa.db come from the address book or the partner's own profile name, not from WhatsApp verification.
Key fields
| Table | Columns | Meaning |
|---|---|---|
message | chat_row_id, from_me, key_id, sender_jid_row_id, timestamp, received_timestamp, message_type, text_data, starred | One row per message (current schema) |
chat | _id, jid_row_id, subject, created_timestamp, archived, hidden | Conversations and groups |
jid | _id, user, server, raw_string | Identifiers: <number>@s.whatsapp.net, <id>@g.us for groups, @lid on newer builds |
message_media | message_row_id, file_path, mime_type, file_size, media_name | Attachments; a caption is stored as the message's text_data |
call_log | jid_row_id, from_me, timestamp, video_call, duration, call_result | WhatsApp calls |
messages (legacy) | key_remote_jid, key_from_me, data, timestamp, media_wa_type, remote_resource | Pre-2022 schema, still found on old backups and devices |
wa_contacts (wa.db) | jid, display_name, wa_name, status, is_whatsapp_user | Contact book as WhatsApp sees it |
Table and column names drift between WhatsApp releases: confirm them with .schema on the database you have before trusting a saved query.
Timestamps
All WhatsApp times are Unix epoch milliseconds in UTC.
SELECT m._id, cj.raw_string AS chat, c.subject,
CASE m.from_me WHEN 1 THEN 'owner' ELSE sj.raw_string END AS sender,
datetime(m.timestamp/1000, 'unixepoch') AS sent_utc,
datetime(m.received_timestamp/1000, 'unixepoch') AS received_utc,
m.message_type, m.text_data
FROM message m
JOIN chat c ON c._id = m.chat_row_id
LEFT JOIN jid cj ON cj._id = c.jid_row_id
LEFT JOIN jid sj ON sj._id = m.sender_jid_row_id
ORDER BY m.timestamp;
timestamp is the send time as reported to this device; received_timestamp is when this handset stored it. A long gap means the phone was offline.
Retention
Messages stay until the user deletes them, clears or deletes the chat, or a disappearing-messages timer removes them (24 hours, 7 days or 90 days). "Delete for everyone" leaves a revoked row. Uninstalling the app deletes the sandbox, but the local backups and media in shared storage survive unless the user removes them. Local backups are written nightly and old dated copies are rotated. Deleted rows often remain in the WAL and in free pages of msgstore.db.
Collection
- Full file system extraction (AFU) is the reliable route to
msgstore.db,wa.db, the key file and the WAL. - Logical: pull the local backups and media with
adb pull /sdcard/Android/media/com.whatsapp/. A.crypt14backup needs thekeyfile from the sandbox; a.crypt15end-to-end encrypted backup needs the 64-digit key or, via WhatsApp's servers, the user's password. - APK downgrade: replacing WhatsApp with an old version that allows
adb backup, then restoring the current one. It changes the device and can fail; Android 14 and later block installing very old target SDK APKs unlessadb install --bypass-low-target-sdk-blockis used. Document it fully if used. - Chat export: the user can export a chat as
.txt(with or without media) from the app.
Parsing
- Messaging Forensics reads a decrypted
msgstore.db(current and legacy schemas) withwa.dbfor contact names, replays the WAL, and also opens chat export.txt/.zipfiles, entirely in the browser. - wa-crypt-tools decrypts
.crypt12,.crypt14and.crypt15backups when you have the key. - ALEAPP and the commercial suites parse messages, media, calls and group participants, and some carve deleted records.
Investigator tips
- Always resolve JIDs through
wa.dband the device contacts;@lididentifiers on newer builds are not phone numbers. - Compare the newest local backup with the live database: messages deleted after the last nightly backup are often still in the backup.
- Media files are named with the date they were saved (
IMG-20260914-WA0003.jpg); the name date, the file's timestamps andmessage_mediarows should agree. - WhatsApp voice calls are in
call_loginsidemsgstore.db, not in the Android call log. - Use usagestats to show WhatsApp was in the foreground when a message was typed, and installed packages for its install and update dates.
See also
Related artifacts
- Android Contacts: contacts2.db
- SMS and MMS on Android: mmssms.db
- Android Call Log: calllog.db
- Installed Apps: packages.xml and packages.list
- Usagestats: Android App Usage Events
- Signal on Android: signal.db, Keys and Backups
- Telegram on Android: cache4.db and Media
- Android Notification History and notification_log.db
- DCIM, EXIF and MediaStore external.db on Android