VI-02 · Mobile device forensics

Recovering deleted messages from a chat database

Messages were deleted from an Android app. The examination explains how some rows are recovered from the SQLite log and free pages, and why recovery is partial.

Synthetic case for educational use. Organisations, people, numbers and hashes are invented.

01The question

Can messages deleted from the chat with one contact be recovered from the handset, and over what period is recovery reliable?

02Background

In a fictional dispute at Delta Lojistik, one party says a chat thread was deleted. A full file-system extraction of the Android device was provided. The examination is limited to recovery and interpretation of the database; it does not address how the extraction was obtained.

03Constraints

  • Only a file-system extraction was available, not a physical image, so unallocated flash space could not be carved.
  • The database uses WAL journaling; recovery depends on whether a checkpoint had flushed the log.
  • End-to-end transport encryption does not affect on-device recovery; the local database stores messages in readable form for the app.

04Evidence received

ItemFormHashNote
File-system extraction of the Android handsetF-03sha256: 2a7f…c410Extraction package hashed on receipt.
msgstore.db, msgstore.db-wal and msgstore.db-shmF-04sha256: e5d9…b832The WAL and shared-memory files kept with the main database; all three hashed and worked on copies.

05Examination method

  1. Preserve the database and its journal together

    The main database and its -wal and -shm companions were copied as a set and hashed. Opening the database without the WAL present would have shown a stale view; the WAL holds pages not yet written back into the main file.

    WhatsApp for Android messages (msgstore.db)Hash calculator

  2. Read live rows and the schema

    The message table's columns (identifier, chat key, direction, timestamp in milliseconds, text, media reference) were read so that recovered fragments could be interpreted the same way as live rows. The chat with the contact was identified by the contact's row in the chat list.

    WhatsApp for Android messages (msgstore.db)

  3. Recover deleted rows from the WAL and free list

    Deleting a message marks its row's cell as free inside the b-tree page; the bytes often remain until the page is reused. The WAL frames and the freeblock chains in the main file were parsed to recover message text and timestamps that no longer appear in a normal query. Each recovered row was tagged with the page and offset it came from.

    WhatsApp for Android messages (msgstore.db)Hex viewer and data inspectorTimestamp decoder

  4. Convert timestamps and bound the reliable period

    Millisecond timestamps were converted to UTC. Live rows gave a continuous record up to a point; recovered rows filled part of the gap. The earliest safely recoverable message set the start of the reliable window; older deletions whose pages had been overwritten could not be recovered and were reported as a gap, not as absence of messages.

    WhatsApp for Android messages (msgstore.db)Timestamp decoderLocal time to UTC

06Findings

  • Established

    Deleted messages exchanged with the contact were recovered for a bounded period, with text, direction and UTC timestamps.

    BasisRecovered rows match the live table schema and carry page/offset references; timestamps are consistent with surrounding live rows.

  • Established

    Some earlier messages in the thread could not be recovered because their database pages had been reused.

    BasisThe free-list and WAL no longer held those pages' prior contents.

  • Probable

    The thread was deleted as a block rather than message by message over time.

    BasisRecovered rows cluster on a small set of freed pages with close free timings, which fits a single bulk deletion better than scattered deletions.

07Limits

  • Recovery is partial by nature; non-recovery of a message is not proof it never existed.
  • Without a physical image, data in unallocated flash outside the database file could not be examined.
  • Recovered text is attributed to the account on the device; it does not by itself prove who was holding the phone.
  • A message's local timestamp reflects the device clock, which can be wrong or changed.

08Report excerpt

Forty-one messages that do not appear in a normal query were recovered from freed pages of msgstore.db and from WAL frames, each tagged with its page and offset. They cover 2026-… to 2026-…. Messages before that date in this thread could not be recovered: their pages had been reused, so we report a gap, not an absence.

09Lessons

  • Always preserve the -wal and -shm files with the database; without them the view is stale.
  • Tag every recovered row with its page and offset so the recovery can be repeated and checked.
  • Report non-recovery as a gap with its cause, never as proof a message did not exist.

10Sources

  1. SQLite — Write-Ahead Logging
  2. SQLite — Database File Format