1#!/usr/bin/env python 2 3# Written by Dhiru Kholia <dhiru at openwall.com> in March, 2013 4# My code is under "Simplified BSD License" 5 6import sys 7import os 8import struct 9from binascii import hexlify 10 11# header structs are taken from vilefault and hashkill projects 12v1_header_fmt = '> 48s I I 48s 32s I 296s I 300s I 48s 484s' 13v1_header_size = struct.calcsize(v1_header_fmt) 14# v2_header_fmt = '> 8s I I I I I I I 16s I Q Q 24s I I I I 32s I 32s I I I I I 48s' 15# encrypted_blob_size can be 64, handle such cases properly too, a value of 48 16# is already handled well 17v2_header_fmt = '> 8s I I I I I I I 16s I Q Q 24s I I I I 32s I 32s I I I I I 64s' 18v2_header_size = struct.calcsize(v2_header_fmt) 19 20PY3 = sys.version_info[0] == 3 21PMV = sys.version_info[1] >= 6 22 23if PY3 or PMV: 24 exec('MAGICv1=b"cdsaencr"') 25 exec('MAGICv2=b"encrcdsa"') 26else: 27 MAGICv1 = "cdsaencr" 28 MAGICv2 = "encrcdsa" 29 30 31def process_file(filename): 32 cno = 0 33 data_size = 0 34 35 headerver = 0 36 try: 37 fd = open(filename, "rb") 38 except IOError: 39 e = sys.exc_info()[1] 40 sys.stderr.write("%s\n" % str(e)) 41 return 42 43 buf8 = fd.read(8) 44 if len(buf8) != 8: 45 sys.stderr.write("%s is not a DMG file!\n" % filename) 46 return 47 48 if buf8 == MAGICv2: 49 headerver = 2 50 else: 51 fd.seek(-8, 2) 52 buf8 = fd.read(8) 53 if len(buf8) != 8: 54 sys.stderr.write("%s is not a DMG file!\n" % filename) 55 return 56 57 if buf8 == MAGICv1: 58 headerver = 1 59 60 if headerver == 0: 61 sys.stderr.write("%s is not an encrypted DMG file!\n" % filename) 62 return 63 64 sys.stderr.write("Header version %d detected\n" % headerver) 65 66 if headerver == 1: 67 fd.seek(- v1_header_size, 2) 68 data = fd.read(v1_header_size) 69 if len(data) != v1_header_size: 70 sys.stderr.write("%s is not a DMG file!\n" % filename) 71 return 72 73 data = struct.unpack(v1_header_fmt, data) 74 75 (_, kdf_iteration_count, kdf_salt_len, kdf_salt, _, 76 len_wrapped_aes_key, wrapped_aes_key, len_hmac_sha1_key, 77 wrapped_hmac_sha1_key, _, _, _) = data 78 79 sys.stdout.write("%s:$dmg$%d*%d*%s*%d*%s*%d*%s*%d::::%s\n" % \ 80 (os.path.basename(filename), headerver, kdf_salt_len, 81 hexlify(kdf_salt)[0:kdf_salt_len * 2].decode("ascii"), 82 len_wrapped_aes_key, 83 hexlify(wrapped_aes_key)[0:len_wrapped_aes_key * 2].decode("ascii"), 84 len_hmac_sha1_key, 85 hexlify(wrapped_hmac_sha1_key)[0:len_hmac_sha1_key * 2].decode("ascii"), 86 kdf_iteration_count, filename)) 87 else: 88 fd.seek(0, 0) 89 data = fd.read(v2_header_size) 90 if len(data) != v2_header_size: 91 sys.stderr.write("%s is not a DMG file!\n" % filename) 92 return 93 94 data = struct.unpack(v2_header_fmt, data) 95 (sig, version, enc_iv_size, _, _, _, _, 96 unk5, uuid, blocksize, datasize, dataoffset, filler1, 97 kdf_algorithm, kdf_prng_algorithm, kdf_iteration_count, 98 kdf_salt_len, kdf_salt, blob_enc_iv_size, blob_enc_iv, 99 blob_enc_key_bits, blob_enc_algorithm, blob_enc_padding, 100 blob_enc_mode, encrypted_keyblob_size, encrypted_keyblob) = data 101 102 fd.seek(dataoffset, 0) 103 cno = ((datasize + 4095) // 4096) - 2 104 data_size = datasize - cno * 4096 105 data_size = int(data_size) 106 if data_size < 0: 107 sys.stderr.write("%s is not a valid DMG file! \n" % filename) 108 return 109 if kdf_salt_len > 32: 110 sys.stderr.write("%s is not a valid DMG file. salt length " \ 111 "is too long!\n" % filename) 112 return 113 114 # read starting chunk(s) 115 fd.seek(dataoffset + int(cno * 4096), 0) 116 chunk1 = fd.read(data_size) 117 if len(chunk1) != data_size: 118 sys.stderr.write("%s is not a DMG file!\n" % filename) 119 return 120 121 # read last chunk 122 fd.seek(dataoffset, 0) 123 chunk2 = fd.read(4096) 124 if len(chunk2) != 4096: 125 sys.stderr.write("%s is not a DMG file!\n" % filename) 126 return 127 128 # output hash 129 sys.stdout.write("%s:$dmg$%d*%d*%s*32*%s*%d*%s*%d*%d*%s*1*%s*%d::::%s\n" % \ 130 (os.path.basename(filename), headerver, 131 kdf_salt_len, 132 hexlify(kdf_salt)[0:kdf_salt_len*2].decode("ascii"), 133 hexlify(blob_enc_iv)[0:64].decode("ascii"), 134 encrypted_keyblob_size, 135 hexlify(encrypted_keyblob)[0:encrypted_keyblob_size*2].decode("ascii") + \ 136 "0" * (encrypted_keyblob_size * 2 - \ 137 len(encrypted_keyblob) * 2), 138 cno, data_size, hexlify(chunk1).decode("ascii"), 139 hexlify(chunk2).decode("ascii"), 140 kdf_iteration_count, filename)) 141 142if __name__ == "__main__": 143 sys.stderr.write("Using 'dmg2john' instead of this program (%s) is recommended!\n\n" % sys.argv[0]) 144 145 if len(sys.argv) < 2: 146 sys.stderr.write("Usage: %s [DMG files]\n" % sys.argv[0]) 147 sys.exit(-1) 148 149 for i in range(1, len(sys.argv)): 150 process_file(sys.argv[i]) 151