WhatsApp stores all messages in an sqlite file which is first zlib compressed and then AES encrypted.
Decryption/decompression can be done quite easily like that:
def decrypt(db_file, key_file):
""" Function decrypt Crypt12 Database """
try:
with open(key_file, "rb") as fh:
key_data = fh.read()
key = key_data[126:]
with open(db_file, "rb") as fh:
db_data = fh.read()
iv = db_data[51:67]
aes = AES.new(key, mode=AES.MODE_GCM, nonce=iv)
with open("msgstore.db", "wb") as fh:
fh.write(zlib.decompress(aes.decrypt(db_data[67:-20])))
print db_file + " decrypted, msgstore.db created."
except Exception as e:
print "An error has ocurred decrypting the Database:", e
But if you wanted to reverse the process, you would have to generate the header, footer and IV. At first I thought you could just copy them from another already existing crypt12 file like this:
def encrypt(db_file, key_file, db_cript):
""" Function encrypt msgstore Database """
try:
with open(key_file, "rb") as fh:
key_data = fh.read()
key = key_data[126:]
with open(db_cript, "rb") as fh:
db_cript_data = fh.read()
header = db_cript_data[:51]
iv = db_cript_data[51:67]
footer = db_cript_data[-20:]
with open(db_file, "rb") as fh:
data = fh.read()
aes = AES.new(key, mode=AES.MODE_GCM, nonce=iv)
with open("msgstore.db.crypt12", "wb") as fh:
fh.write(header + iv + aes.encrypt(zlib.compress(data)) + footer)
print "Encrypted msgstore.db created."
except Exception as e:
print "An error has ocurred encrypting the Database:", e
But that does not appear to be the case. The .crypt12 files headers and footers are different for every file.
Example 1 header hexdump:
0000000 0100 5802 ee46 bcaf 8968 d43b bfaf ca5d
0000010 a774 6b66 53ec dbae af60 a09c 3ad2 ba5e
0000020 7341 d224 22e7 55c3 c944 cf77 2c71 9c29
0000030 3a58 3798 d10f 897c 6ffa f27a e788 4d7b
0000040 24a7 43a0 f1d3 5f7c 5c74 d64d 58c9 21a4
0000050 771c 7fdc 2e14 ca93 0848 758a c184 9058
0000060 b7d9 d847 604f cadf 05a2 a678 4994 b711
0000070 2641 9170 8965 f6a7 9ff3 95a0 860d aca1
0000080 214c d644 faa7 .... .... .... .... ....
Example 2 header hexdump:
0000000 0100 5802 ee46 bcaf 8968 d43b bfaf ca5d
0000010 a774 6b66 53ec dbae af60 a09c 3ad2 ba5e
0000020 7341 d224 22e7 55c3 c944 cf77 2c71 9c29
0000030 3a58 1898 1435 4cc9 bdbf e506 f138 41a6
0000040 32a0 71c5 faa9 2499 36f7 d2be d7a8 bf28
0000050 f3ea 1571 ed80 da14 addb 63b6 1d8e 2de5
0000060 0a9c bc31 8d86 cb42 b4ce b603 af7c c295
0000070 d67f b787 1ad8 eb69 3180 1c9d 8106 1f98
0000080 e880 edd5 c285 .... .... .... .... ....
You can see the first 25 bytes are always equal, but the rest is completely different. The last 16 bytes are the IV (nonce) btw.
For the footer you can see the last two bytes are equal and the rest is completely different.
Example 1 footer hexdump:
1464cd0 .... 2feb 12d8 a2bc 92bd ca30 a99f 621c
1464ce0 9e87 88df dc82 f7ea 2cfd 3e0b ecd4 03b1
1464cf0 7720 8d3d 2dcb 382d 0031
Example 2 footer hexdump:
1474330 .... d4e9 80de d0e8 9786 4fd3 8bf2 957c
1474340 fdf5 2e24 c4f2 1b55 121a 4410 014d c516
1474350 7531 7f0a 2d71 382d 0031
So how do I generate those?