DEV Community

Yuvraj Raghuvanshi
Yuvraj Raghuvanshi

Posted on • Originally published at yuvrajraghuvanshis.Medium on

I Told My Friend I’d Hack WhatsApp. Then I Actually Did It.

A friend asked me if I could “hack” WhatsApp. I was bored, so I said yes.

I didn’t break any encryption. I didn’t exploit a server. What I found was something more interesting: a door that was left open inside Android itself, and an old version of WhatsApp that was still politely holding it open years after everyone else had moved on.

The result was a Python tool that extracts WhatsApp’s encryption key and message database from a phone (without root access) by exploiting the Android backup system. It picked up 540 stars on GitHub. It also got me a message from a developer trying to help people migrate from WhatsApp to Signal right before WhatsApp changed its terms of service to share data with Facebook. That part felt like it actually mattered.

This is how it works, from the ground up.

How Android Stores App Data

To understand the trick, you first need to understand how Android isolates apps from each other.

Every Android app runs in its own sandbox. This is not metaphorical — it’s enforced at the Linux kernel level. When an app is installed, Android creates a dedicated Linux user for it. WhatsApp might be u0_a123. Your banking app might be u0_a201. These are real Unix users with real UIDs. Because they're different users, they cannot read each other's files. The file permissions are enforced by the kernel, the same way they are on any Linux system.

The directory where an app’s private data lives is /data/data/. For WhatsApp, that's /data/data/com.whatsapp. Inside it, you'll find subdirectories that look like this:

/data/data/com.whatsapp/
    databases/
        msgstore.db ← your messages (plaintext SQLite)
        wa.db ← contacts
    files/
        key ← the encryption key
    shared_prefs/
    cache/
    lib/
Enter fullscreen mode Exit fullscreen mode

/data itself is on a partition that is not accessible to normal users or normal processes. On a rooted phone, you can adb shell in and read it directly because root bypasses the permission system. On a non-rooted phone, you cannot. The files are there (they're on the storage) but the kernel will refuse every read attempt from any process that isn't WhatsApp itself.

This is the wall that everyone trying to extract WhatsApp data runs into.


Screenshot: adb shell showing “Permission denied” when trying to ls /data/data/com.whatsapp without root

The Key and the Database

Before going further, it’s worth explaining what these two files actually are and why you need both of them.

msgstore.db inside the sandbox at /data/data/com.whatsapp/databases/ is a plain, unencrypted SQLite database. WhatsApp works with it directly - reading and writing your messages in cleartext while the app is running. SQLite is a well-understood format; tools to read it are everywhere.

What you see if you browse your phone’s SD card storage is different: msgstore.db.crypt14, stored at /sdcard/WhatsApp/Databases/. This is the encrypted backup copy that WhatsApp writes to external storage so Google Drive can sync it. The number in the extension (crypt12, crypt14) indicates the encryption scheme version. This external copy is encrypted with AES-256 and is unreadable without the key.

The key file lives at /data/data/com.whatsapp/files/key. It's a binary file containing key material that WhatsApp generates based on private factors specific to your account and device. WhatsApp doesn't document the generation process publicly - we only know it exists because the file exists, and because without it, the crypt14 file is unreadable garbage.

Here’s what makes the key particularly valuable: it isn't rotated. Once you have it, you can use it to decrypt any crypt14 backup from that account (past ones, current ones, and future ones) until WhatsApp generates a new key. This is different from, say, TLS session keys that are discarded after use. The same key file that works today will work on a database backup taken six months ago, and on one taken six months from now.

The problem is getting the key. It lives inside the sandbox. Root gives you a sledgehammer to break through it. But there’s a quieter way.

What adb backup Is

Android Debug Bridge (adb) is a command-line tool included in the Android SDK. It lets a connected computer communicate with an Android device for development purposes: install APKs, run shell commands, read logs, transfer files. When you enable USB Debugging on your phone, you're enabling adb.

One of the things adb could do, from Android 4.0 onwards, was create full app backups:

adb backup -f myApp.ab -apk com.foobar.app
Enter fullscreen mode Exit fullscreen mode

This would back up the app’s APK and its private data directory to a file on your computer. The backup system was designed for legitimate use: before switching phones, before a factory reset. It needed to read files from /data/data/ (which normally only the app itself can do) so it ran with elevated OS-level privileges to do so.

This is the door. The backup system could read app sandboxes. The question was whether it was allowed to for any given app.

AndroidManifest.xml and the allowBackup Flag

Every Android app ships with a file called AndroidManifest.xml. This is the app's declaration to the operating system - it lists the app's package name, the permissions it needs, the activities it contains, and dozens of other properties. Android reads this file when installing an app and uses it to configure how the app is treated by the system.

One of those properties is android:allowBackup. It controls whether adb backup is permitted to include this app's data in a backup. The default value, historically, was true - if you didn't specify it, backups were allowed.

When WhatsApp realised that this flag meant anyone with USB debugging access could extract their users’ messages with a single command, they set it to false in their manifest. After that point, running adb backup com.whatsapp would produce a backup file, but it would be empty. The backup system would see allowBackup="false" in the manifest and skip the data entirely.

Here is what that flag looks like inside a decompiled WhatsApp APK manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.whatsapp"
    android:versionCode="452658"
    android:versionName="2.21.1.1">

    <application
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:allowBackup="false"  current versions: explicitly denied
        ...>
Enter fullscreen mode Exit fullscreen mode

Modern WhatsApp has allowBackup="false". But old versions of WhatsApp (specifically versions from before WhatsApp realised this was a problem) had allowBackup="true", or simply didn't specify it at all, defaulting to permitted.

The version used in this tool is v2.11.431 , from around 2015. At that version, the manifest permits backups. The Android backup system doesn't know it's running an old version. It reads the manifest, sees the flag, and opens the data directory.

The Trick: Uninstall (Keeping Data), Backup, Restore

The loophole requires three steps: uninstall current WhatsApp while keeping its data intact, install the old version that allows backups, run the backup, then reinstall current WhatsApp.

That first step (“uninstall while keeping data”) deserves explanation, because it’s not what most people think of when they hear “uninstall.”

Android normally does two things when you uninstall an app: it removes the APK, and it wipes /data/data/. Your messages, settings, everything - gone. This is the standard uninstall. But adb exposes a flag that separates these two operations:

adb shell pm uninstall -k com.whatsapp
Enter fullscreen mode Exit fullscreen mode

The -k flag means "keep data." The APK is removed. The data directory at /data/data/com.whatsapp is left completely untouched - key, databases, shared preferences, all of it still there, owned by a UID that no longer has an app attached to it.

This is the correct way to do the downgrade. Android does not allow installing an older version of an app directly over a newer one — it checks version codes and refuses with INSTALL_FAILED_VERSION_DOWNGRADE. In-place downgrade is blocked. But if the app is already uninstalled (even with -k), there's nothing to compare version codes against. The legacy APK installs cleanly. Then when Legacy WhatsApp starts, it finds the existing data directory (the one that current WhatsApp left behind) and picks up from exactly where it was. The key file is there. The databases are there. Legacy WhatsApp doesn't care that the data was written by a newer version.

So the sequence is:

  1. Current WhatsApp is installed on the device. Its data (key, msgstore.db) is in /data/data/com.whatsapp.
  2. Uninstall current WhatsApp with adb shell pm uninstall -k com.whatsapp. The APK is gone; the data directory survives.
  3. Install Legacy WhatsApp v2.11.431 via adb install. It finds the existing data directory and inherits it.
  4. Run adb backup com.whatsapp. Legacy WhatsApp's manifest says allowBackup="true", so the backup system reads the full data directory and writes it to whatsapp.ab on the computer. This includes the key file and the plaintext msgstore.db.
  5. Uninstall Legacy WhatsApp. Reinstall current WhatsApp. The data directory is still there. WhatsApp opens normally.

The user ends up with their current WhatsApp running unchanged, and the computer has a copy of the key and msgstore.db.

What Happens on the Phone Screen During Backup

When adb backup is triggered, Android shows a system prompt on the phone that the user must explicitly interact with. This is a security measure - silent background backups are not allowed.


Screenshot: the “Full backup” system dialog — the image uploaded above showing password field and “Back up my data” button

The dialog says: “A full backup of all data to a connected desktop computer has been requested. Do you want to allow this to happen? If you did not request the backup yourself, do not allow the operation to proceed.”

The user taps “Back up my data.” The backup begins.

There’s also a password field. If the user enters a password here, the backup archive is encrypted with AES before it’s written to disk. The script asks the user on the terminal whether they want to set a backup password. If they do, they need to type the same password into both places — the terminal (so the script knows it for decryption later) and the phone screen (so Android encrypts with it). If the passwords don’t match, the extracted backup will be unreadable.

This is also why one of the troubleshooting notes says: “If you have set a default backup password in your Android settings, then this MUST be the backup password that you PROVIDE when prompted.” Some users have a system-wide default backup password set in their Android settings that they’ve forgotten about. Android silently uses it to encrypt the backup. The script then can’t decrypt it because it received a different password.

The .ab File Format

adb backup produces a .ab file - Android Backup. This is not a zip or a standard archive format. Its structure is documented because Android is open source, so the format is known.

The file begins with a plaintext header:

ANDROID BACKUP
4
1
none
Enter fullscreen mode Exit fullscreen mode

These four lines tell you: this is an Android backup file, format version 4, compressed (1 = yes), encryption algorithm (none, or AES-256 if a password was set). After the header, the rest of the file is a zlib-compressed tar archive. Inside the tar is the app’s data directory, with paths like:

apps/com.whatsapp/f/key
apps/com.whatsapp/db/msgstore.db
apps/com.whatsapp/db/wa.db
Enter fullscreen mode Exit fullscreen mode

The f/ prefix corresponds to the files/ directory in the app's data, and db/ to databases/. The tar contains the full directory structure, just with these path prefixes.

To unpack this, the tool uses android-backup-extractor  — a small open source Java utility that understands the .ab header, handles the optional AES decryption, decompresses the zlib payload, and extracts the tar. I did try to rewrite this part in Python. The unencrypted case is straightforward - strip the header, decompress with zlib, untar. But when a password is involved, the AES key derivation that Android uses involves specific parameters and byte-level handling that I couldn't get right. Rather than ship a broken decryptor, I kept the Java dependency. The android-backup-extractor handles it correctly.

java -jar abe.jar unpack whatsapp.ab whatsapp.tar [password]
Enter fullscreen mode Exit fullscreen mode

After that, the tar is standard:

tar -xf whatsapp.tar
Enter fullscreen mode Exit fullscreen mode

And the key file and databases are on disk, readable.

Installing the Legacy Version: INSTALL_FAILED_VERSION_DOWNGRADE

The first attempt most people make is to just install the legacy APK directly:

adb install LegacyWhatsApp.apk
Enter fullscreen mode Exit fullscreen mode

Android refuses. It checks the version code of the APK being installed against the already-installed app. If the incoming version code is lower, it rejects with INSTALL_FAILED_VERSION_DOWNGRADE. Downgrade over an existing installation is blocked.

The correct approach is to uninstall current WhatsApp first (using the -k flag to preserve the data directory) and then install the legacy APK cleanly. With no existing installation to compare against, Android has no version code conflict to enforce.

The --allow-reboot flag handles a related but separate problem. On some devices, even after uninstalling with -k, the installation fails for other reasons. Rebooting before the install clears whatever state was causing the refusal. The exact mechanism is in the "if it works, don't touch it" category - the device is rebooted via adb reboot before the legacy APK is installed, and on devices where it was failing, it stops failing. The most likely explanation is that some manufacturers' Android builds check additional conditions at runtime that aren't re-evaluated immediately after boot.

There’s a related issue on MIUI (Xiaomi’s Android skin): adb install is blocked by a separate setting ("Install via USB") in Developer Options, distinct from USB Debugging. Without it, every install attempt fails with INSTALL_FAILED_USER_RESTRICTED, regardless of version codes.

Running Legacy WhatsApp Before the Backup

Issue #16 in the repository documents one of the more interesting device-specific behaviours encountered. On some devices, the backup would run without errors but produce a nearly empty archive — no key, no database.

The cause turned out to be that Legacy WhatsApp hadn’t been launched even once before the backup was taken. On those devices, Android’s backup system only includes app data that has been “activated” — meaning the app has run at least once since installation. Without that first launch, the data directory exists (carried over from the current WhatsApp installation), but the backup hook reports nothing to back up.

The fix was to launch Legacy WhatsApp briefly before triggering the backup. The script does this, with a prompt asking the user to open the app and let it sit for a few seconds before continuing. It’s the kind of fix that makes no sense until you see the behaviour it’s correcting.

Approximately 90% of the issues filed on this project follow the same pattern: something fails silently, the fix involves a step that has no obvious causal relationship with the problem, and once you add the step the issue disappears. Android’s backup system is not well-documented in its edge cases.

ADB Over TCP: Using the Tool Without a USB Cable — and Seeing the Screen

Normal adb communication happens over USB. The tool also supports ADB over TCP - connecting to a device over Wi-Fi instead.

python3 wa_kdbe.py --tcp-ip 192.168.43.130 --tcp-port 5555
Enter fullscreen mode Exit fullscreen mode

Android has had ADB-over-TCP support built in since early versions. Once enabled (either through Developer Options on some devices, or by first connecting via USB and running adb tcpip 5555), the device listens for ADB connections on port 5555 over the local network.

The practical use cases: a broken USB port, a device on the other side of a room, or a device anywhere on the same network as the computer. The --tcp-ip flag accepts any IP address, including a phone connected via mobile hotspot. The tool works identically over TCP as over USB; the ADB protocol doesn't care about the transport layer.

When running in TCP mode, there’s an additional flag: --scrcpy. This uses scrcpy (an open source tool by Genymobile) to mirror the phone's screen to a window on the computer and allow touch input via mouse and keyboard. In USB mode this is less necessary since the device is physically in hand, but over TCP the phone might be in another room. With --scrcpy, the backup dialog that appears on the phone screen (the one asking the user to tap "Back up my data") can be interacted with directly from the computer. No need to walk over to the device.

python3 wa_kdbe.py --tcp-ip 192.168.43.130 --tcp-port 5555 --scrcpy
Enter fullscreen mode Exit fullscreen mode

Both features were added because they could be added, and because removing the USB requirement while adding screen control made the tool work from genuinely anywhere on the same network.

The Signal Developer Who Reached Out

A few weeks after the project picked up traction, I received a message from a developer named Sam:

“I want to make it easy for people to switch from WhatsApp to Signal, which I think is important, especially given the upcoming changes to the TOS of WhatsApp [sharing all user data with Facebook from February 8]. I’ve forked the Signal Android App and added a WhatsApp import functionality, that migrates your existing WhatsApp threads from msgstore.db to Signal. However, the process of retrieving msgstore.db is way too complicated for many people and most people don’t want to root their phone.”

He had built a Signal fork that could import WhatsApp’s msgstore.db directly. The blocking problem was that getting msgstore.db off a non-rooted phone was too technically involved for most users. He was asking if the tool could be adapted to make the extraction seamless enough for non-technical users.

This is the part that reframes what the project actually was. What started as me being bored and proving something to a friend turned out to be infrastructure for something with a real privacy rationale: helping people leave a platform that had just announced it would share their data with one of the largest advertising companies in the world, and take their message history with them.

The msgstore.db that the tool extracts (once decrypted with the key) is a standard SQLite database. Every message is in there. You can read it with any SQLite browser, write queries against it, import it into other applications. The data is yours. It exists on your device. The only thing standing between you and it was the sandbox, the allowBackup flag, and the question of which version of WhatsApp was currently installed.

Why It No Longer Works Reliably

I have marked this tool as “NO LONGER MAINTAINED.” This needs context.

The allowBackup loophole in WhatsApp v2.11.431 hasn't been patched in the sense that the old APK was changed - the APK still has allowBackup="true". But Android itself has progressively made downgrade installations harder and backup extraction more restricted.

Newer Android versions (11, 12, 13+) have tightened the backup system significantly. Google restricted what adb backup could access, eventually deprecating the feature entirely in higher API levels. The backup dialog still appears, the process runs, but on many modern Android builds the data directories are excluded by the OS regardless of what the app manifest says. The flag that WhatsApp forgot to set stopped mattering because Android stopped honouring it.

The tool works on older Android versions and some devices where the restrictions haven’t fully landed. On others (most new devices running Android 12 or 13) it will produce an empty or near-empty backup. The backup system is still there; it just doesn’t open the data directories anymore.

I moved on when it became clear that the percentage of devices where it worked reliably was shrinking with every Android release, and that fixing it would require a different approach entirely — one that likely involves ADB shell commands and run-as, which has its own restrictions and device-specific behaviour.

What the Tool Actually Does, End to End

For completeness, here is the full sequence the script runs:

  1. Connect to the device via USB or TCP. Verify adb can see it.
  2. Back up the current WhatsApp APK from /data/app/com.whatsapp/ to /data/local/tmp/WhatsAppbackup.apk on the device. This is the user's currently installed version, saved so it can be reinstalled later.
  3. Uninstall current WhatsApp with adb shell pm uninstall -k com.whatsapp. The -k flag keeps the data directory intact.
  4. Install Legacy WhatsApp v2.11.431 via adb install. It finds the existing data directory and inherits it.
  5. Prompt the user to open Legacy WhatsApp briefly on the phone, then return to the terminal.
  6. Run adb backup com.whatsapp. The system dialog appears on the phone. The user taps "Back up my data."
  7. Receive whatsapp.ab on the computer.
  8. Unpack with android-backup-extractor : java -jar abe.jar unpack whatsapp.ab whatsapp.tar.
  9. Extract the tar. Locate key and msgstore.db (and other databases).
  10. Copy files to extracted//.
  11. Optionally compress the folder as a password-protected 7z archive.
  12. Uninstall Legacy WhatsApp. Reinstall the backed-up current WhatsApp APK.
  13. Copy msgstore.db back to the phone's SD card for convenience.

The reinstallation in step 11 uses the APK that was saved in step 2 (the user’s exact current version) rather than downloading from the Play Store. This matters because the Play Store version might have updated since the backup was taken, and using an intermediate version could cause issues with the database format.

The whole process takes a few minutes. The window where the user’s WhatsApp is replaced by the legacy version is the longest part — the backup can take a while depending on database size. If something goes wrong during reinstallation and WhatsApp refuses to open, the recovery is a clean reinstall from the Play Store followed by restoring from the local or Google Drive backup that the README tells you to take before starting.


GIF: Screen recording of complete run of the tool

That warning at the top of the README (“Hope for the best, prepare for the worst”) is there for a reason.

Project URL: github.com/YuvrajRaghuvanshiS/WhatsApp-Key-Database-Extractor

This article is for educational purposes — for understanding how Android’s app sandbox and backup system work and how a single overlooked flag in a manifest file can undermine both. Use it on your own data.

This article is rewritten using AI chatbots.

May 03, 2026

Top comments (0)