Extracting WhatsApp Messages from an iOS Backup

Extracting WhatsApp Messages from an iOS Backup

This is another fancy ingredient!!

Hi everyone! 👋 I was recently exploring how to get a local backup of WhatsApp messages from my iPhone. I switched from Android to iOS in the past and lost all of my WhatsApp messages. I wanted to make sure that if I switched again from iOS to Android I don’t lose any messages. I don’t really care if I can import the messages in WhatsApp. I just don’t want to lose all of the important information I have in my chats. I don’t have any immediate plans for switching (if ever) but it seemed like a fun challenge and so I started surveying the available tools and how they work.

This was mostly a learning exercise for me regarding how Apple stores iOS backups and how I can selectively extract information and data from one. My target was to have a local copy of WhatsApp messages that I can read and search through locally. It would be doubly awesome if I can move the messages to an Android device but, as I mentioned before, that wasn’t my main aim.

Exploring iOS backup

By default, when you create an iOS backup on Mac (Catalina in my case), it is stored under ~/Library/Application Support/MobileSync/Backup/. This folder contains sub-folders with unique device identifiers. Each sub-folder is a backup and contains a bunch of additional subfolders along with the following 4 important files:


We mainly care about both of the Manifest files.

The Manifest.plist file is a binary Property List file that contains information about the backup. It contains:

Backup keybag: The Backup keybag contains a set of data protection class keys that are different from the keys in the System keybag, and backed-up data is re-encrypted with the new class keys. Keys in the Backup keybag facilitate the secure storage of backups. We will learn about protection classes later
Date: This is the timestamp of a backup created or last updated
ManifestKey: This is the key used to encrypt Manifest.db (wrapped with protection class four)
WasPasscodeSet: This identifies whether a passcode was set on the device when it was last synced
And much more…

Source: O’Reilly + Richinfante

Whereas, the Manifest.db file contains all the juicy info about the files in the backup and their paths. The only problem is that the Manifest.db file is encrypted and we need to use the information from the Manifest.plist file to decrypt it. If the backup was not encrypted, we could have probably gotten away without making use of the Manifest.plist file.

We can verify that the db file is encrypted by opening it in any SQL db viewer. I used “DB Browser for SQLite” and it showed me this screen:

This clearly shows that the db is encrypted. Later we will see that not only is the DB encrypted, but every file is also encrypted with its own random per-file encryption key.

Decrypting the Manifest.db file

The basic decryption process is as follows:

Decode the keybag stored in the BackupKeyBag entry of Manifest.plist. A high-level overview of this structure is given in the iOS Security Whitepaper. The iPhone Wiki describes the binary format: a 4-byte string type field, a 4-byte big-endian length field, and then the value itself.

The important values are the PBKDF2 ITERations and SALT, the double protection salt DPSL and iteration count DPIC, and then for each protection CLS, the WPKY wrapped key.

Using the backup password derive a 32-byte key using the correct PBKDF2 salt and number of iterations. First, use a SHA256 round with DPSL and DPIC, then a SHA1 round with ITER and SALT.

Unwrap each wrapped key according to RFC 3394.

Decrypt the manifest database by pulling the 4-byte protection class and longer key from the ManifestKey in Manifest.plist, and unwrapping it. You now have a SQLite database with all file metadata.

For each file of interest, get the class-encrypted per-file encryption key and protection class code by looking in the Files.file database column for a binary plist containing EncryptionKey and ProtectionClass entries. Strip the initial four-byte length tag from EncryptionKey before using.

Then, derive the final decryption key by unwrapping it with the class key that was unwrapped with the backup password. Then decrypt the file using AES in CBC mode with a zero IV.

Source: StackOverflow

If protection classes and double protection doesn’t make much sense, I would highly recommend reading the iOS Security Whitepaper from page 12 onwards. It provides details about all of this and why iOS uses these protection classes.

If you don’t know what a Keybag is, Apple has decent documentation:

A data structure used to store a collection of class keys. Each type (user, device, system, backup, escrow, or iCloud Backup) has the same format.

A header containing: Version (set to four in iOS 12 or later), Type (system, backup, escrow, or iCloud Backup), Keybag UUID, an HMAC if the keybag is signed, and the method used for wrapping the class keys—tangling with the UID or PBKDF2, along with the salt and iteration count.

A list of class keys: Key UUID, Class (which file or Keychain Data Protection class), wrapping type (UID-derived key only; UID-derived key and passcode-derived key), wrapped class key, and a public key for asymmetric classes.

We can read the Manifest.plist file in Python using the biplist module. You can install it using pip:

pip install biplist

And then use it like this:

from biplist import readPlist
import os

backup_directory=os.path.expanduser(“~/Library/Application Support/MobileSync/Backup/”)
plist_path=os.path.join(backup_directory, “Manifest.plist”)

Note: Don’t forget to replace with the name of you particular device backup folder.

This is what the plist contents would look like:

From this dict, we require the backupKeyBag and ManifestKey. It will help us decrypt the Manifest.db file. The BackupKeybag is a binary string with the following format:

4-byte block identifier
4-byte block length (most significant byte first), length 4 means total block length of 0xC bytes.

The first block is “VERS” with a version number of 3. There are a lot of block types: VERS, TYPE, UUID, HMCK, WRAP, SALT, ITER, UUID, CLAS, WRAP, KTYP, WPKY, etc.

Source: IPhone Wiki

Decrypting the keybag

There are quite a few resources available online that show you how you can decrypt the keybag. It uses PBKDF2 for key generation and AES for encryption. You can take a look at this StackOverflow answer for working Python code to decrypt the keybag. I will be making use of the code from that answer.

There are a bunch of different protection classes. The one used for the manifest database is class 3. We can find this by reading the first 4 bytes of the ManifestKey value in our Manifest.plist file:

import struct
Read More
Share this on knowasiak.com to discuss with people on this topicSign up on Knowasiak.com now if you’re not registered yet.

Charlie Layers

Charlie Layers

Fill your life with experiences so you always have a great story to tellBio: About:

Leave a Reply

Your email address will not be published. Required fields are marked *