[Android General] siemer:
Goal: pass a whatsapp chat history backup (.crypt12) from one device to another with different telephone (whatsapp account) numbers.

Update: I succeeded with ultimate goal to move the chat history to a different device with a different phone number, but I failed with the re-encryption.

Encryption

First, I believed that the /data/data/com.whatsapp/files/key file might have been a leftover from an old version of Whatsapp on the old device, because I could not find the file. It turned out that it was generated later on the new device, after I finished my experiments. I’m not sure what triggers its creation.

Furthermore I was not able to decipher header and footer of the crypt12 backup file. I believe that a message authentication code (MAC) is part of it and something related to the Whatsapp account number (telephone number), because the app was quick in determining if a backup file is a restore candidate without decrypting, I’ld say.

How I managed to transfer the chat history:

Short version: get root on both devices and move /data/data/com.whatsapp/databases/msgstore.db over to the new device.

Long version: root on the new device will be less problematic, I guess. For the old device I used the fishy Kingroot app. It looks very professional and seems to download exploit code from a huge database for many devices. I got instant root access without flashing anything, but to be honest I don’t trust it regarding what else it might be doing...

Kill both Whatsapp apps before reading the msgstore.db file from the old and writing to the new device. I also removed the msgstore.db-* files on the new device. Sqlite might detect itself that the new database does not fit those helper files, but if they are not even there, they will be recreated correctly without any doubt.

Also set the permissions and ownership of that file to what it would be on the new device (not the old). My biggest oversight were the SELinux security attributes stored in the extended file attributes (XA). It made me believe that Whatsapp is verifying the database content and rejects it, but in reality it eats it just fine, as long as it gets proper access to it.

The XA tools I had available on the phones apparently show all the extended file attributes with “getfattr -d filename”, while e.g. on a standard Linux you need “getfattr -dm- filename” to get them all, not just the user.* domain. „ls -Z” shows the SELinux security context, which is a specific part of the extended file attributes, and in this case the only part I had set.

As with the file ownership, check which context is usually set on the database file on the new device and set it accordingly (“setfattr”). My old device did not even have SELinux and no extended file attributes were set there.

Other findings:

As the restore kicks only in during initial whatsapp setup, I’ve cleared data on whatsapp a lot. Constant reactivating the same phone number will trigger a hold-off on the whatsapp servers, delaying activation-SMS or -call.

But a seemingly corrupt or missing msgstore.db file trigger a restore as well, so it is possible to feed backups to Whatsapp without constant reactivation. Whatsapp failed with the restore if the SELinux context was wrong on the to-be-replaced database file, I believe. During my trials I just deleted the database (if I recall correctly) and the artificially triggered restored worked out.

Below this line is stuff I tried before:

At the moment I try to plant the old backup on whatsapp for decryption. To get to the point of restore, I force close whatsapp, clear app data, open whatsapp, give it no permissions and activate a number. At this point it asks for permissions to find restore files: I deny “contacts”, but allow for “media/files”. Here it consistently finds the most recent _local_ backup from the _same_ whatsapp account. At this point I force close the app and clear the app cache.

Restarting whatsapp brings it to the backup screen where it finds the local backup for the _same_ device.

At the moment I’m investigating which backups it picks up. For this I use backups from the same device/account as well (known to be working).

/data/data/com.whatsapp/files/key

This file is non-existent on the new device. If I drop a key file from a different account, it seems to get ignored. Whatsapp still finds the native backup (not one matching the key), and it successfully restores it.

/data/data/com.whatsapp/shared_prefs/keystore.xml

This file already exists, but only with “client_static_keypair”. If I replace that entry with the one from the old device, whatsapp will still find the native backup, but it will fail to restore it. The restore has to be skipped and whatsapp triggers a reactivation of the phone number, but if you enter the same new number again, whatsapp accepts it without SMS/call.

→this seems to be the encryption key to be used by whatsapp.

I can confirm the following crypt12 decryption code to be working:

https://gist.github.com/nlitsme/b079f351eb1bf9c3d356ce988bb6afdc

https://github.com/EliteAndroidApps/WhatsApp-Crypt12-Decrypter

They both require the backup file and the “key” file. The latter has a check where it compares a component from the key file with the backup 1:1. In the code this is called “t1” and “t2” which should match. So far I have backups with three different t1/t2. The original backup, with a matching key; the backups from the new account on the new devices, without a key; and the backups from the new device/account, where I mixed the string from the keystore.xml file in.

The “key” file is not generated by the latest Whatsapp anymore, as it seems. Maybe the encryption/decryption key is generated on-the-fly from the keystore.xml data. If this is true, then a new activation of my new telephone number would make these backups unreadable.

Created by author: 2018-04-19

Last edit by author: 2018-05-01