1212import sys
1313import re
1414import base64
15+ import zlib
1516
1617try :
1718 from cryptography .hazmat .primitives .ciphers import Cipher , algorithms , modes
3031BOOT_DIR_REGEX = re .compile (r'com.apple.boot.(?P<boot_letter>[A-Z])' )
3132
3233# long regex for CryptoUsers dict (removable drives only) because difficult to parse with plistlib
33- CRYPTO_USERS_REGEX = re .compile (r'<key>CryptoUsers<\/key>.*?<key>PassphraseWrappedKEKStruct<\/key><data.*?>(?P<PassphraseWrappedKEKStruct>.*?)<\/data><key>WrapVersion<\/key><integer.*?>(?P<wrap_version>.*?)<\/integer><key>UserType<\/key><integer.*?>(?P<UserType>.*?)<\/integer><key>UserIdent<\/key><string.*?>(?P<UserIdent>.+?)<\/string><key>UserNamesData<\/key><string.*?>(?P<UserNamesData>.*?)<\/string><key>PassphraseHint<\/key><reference.*?><key>KeyEncryptingKeyIdent<\/key><string.*?>(?P<KeyEncryptingKeyIdent>.*?)<\/string><key>UserFullName<\/key><reference.*?><key>EFILoginGraphics<\/key><data.*?>(?P<EFILoginGraphics>.*?)<\/data>' )
34+ CRYPTO_USERS_REGEX_STR = r'<key>CryptoUsers<\/key>.*?'
35+ for EntryName in ["PassphraseWrappedKEKStruct" , "WrapVersion" , "UserType" , "UserIdent" ,
36+ "UserNamesData" , "PassphraseHint" , "KeyEncryptingKeyIdent" , "UserFullName" , "EFILoginGraphics" ]:
37+ CRYPTO_USERS_REGEX_STR += r'<key>' + EntryName + r'<\/key><.*?>(?P<' + EntryName + r'>.*?)(<.*?>)*?'
38+ CRYPTO_USERS_REGEX_STR += r'<.*?>'
39+ CRYPTO_USERS_REGEX = re .compile (CRYPTO_USERS_REGEX_STR )
3440
3541def uint_to_int (b ):
3642 return int (b [::- 1 ].hex (), 16 )
@@ -236,9 +242,15 @@ def parse_metadata_block_0x11(metadata_block):
236242 return volume_group_xml_offset , volume_group_xml_size , volume_groups_descriptor_offset
237243
238244def parse_metadata_block_0x19 (metadata_block ):
245+ compressed_data_size = uint_to_int (metadata_block [40 :44 ])
246+ uncompressed_data_size = uint_to_int (metadata_block [44 :48 ])
239247 xml_plist_data_offset = uint_to_int (metadata_block [48 :52 ])
240248 xml_plist_data_size = uint_to_int (metadata_block [52 :56 ])
241249 xml_plist = metadata_block [xml_plist_data_offset - 64 : xml_plist_data_offset + xml_plist_data_size - 64 ]
250+
251+ if compressed_data_size < uncompressed_data_size :
252+ xml_plist = decompress_xml_plist (xml_plist )
253+
242254 return xml_plist
243255
244256def parse_volume_group_descriptor (volume_group_descriptor ):
@@ -269,10 +281,19 @@ def parse_CryptoUsers_dict(CryptoUsers_dict, EncryptedRoot):
269281 if not EncryptedRoot :
270282 PassphraseWrappedKEKStruct = base64 .b64decode (PassphraseWrappedKEKStruct )
271283 fvde_hash = construct_fvde_hash (PassphraseWrappedKEKStruct )
272-
273284 sys .stdout .write (f"{ username_info } :{ fvde_hash } :::{ full_name_info } { passphrase_hint } ::\n " )
274285 return
275286
287+ def decompress_xml_plist (xml_plist ):
288+ # sometimes the xml plist in metadata block type 0x19 is compressed with zlib
289+ try :
290+ decompressed_xml_plist = zlib .decompress (xml_plist )
291+ return decompressed_xml_plist
292+ except zlib .error :
293+ sys .stderr .write ("[!] Zlib decompression error, exiting.\n " )
294+ sys .exit (1 )
295+
296+
276297def get_CryptoUsers_dict_from_EncryptedRoot (EncryptedRoot_data , aes_key1 ):
277298 aes_key2 = b'\x00 ' * 16
278299 tweak = b'\x00 ' * 16
@@ -288,8 +309,8 @@ def get_CryptoUsers_dict_from_encrypted_metadata(cs_start_pos, offsets, block_si
288309 metadata_block = try_read_fp (fp , 0x200 )
289310
290311 volume_group_xml_offset , volume_group_xml_size , volume_groups_descriptor_offset = parse_metadata_block (metadata_block )
291- # fp.seek(metadata_block_start + volume_group_xml_offset)
292- # xml = try_read_fp(fp, volume_group_xml_size)
312+ fp .seek (metadata_block_start + volume_group_xml_offset )
313+ xml = try_read_fp (fp , volume_group_xml_size )
293314
294315 fp .seek (metadata_block_start + volume_groups_descriptor_offset )
295316 volume_group_descriptor = try_read_fp (fp , 0x200 )
@@ -298,30 +319,35 @@ def get_CryptoUsers_dict_from_encrypted_metadata(cs_start_pos, offsets, block_si
298319 fp .seek (cs_start_pos + primary_enc_metadata_block_no * block_size )
299320 enc_metadata = try_read_fp (fp , enc_metadata_size * block_size )
300321
301- for block_number in range (0 , 64 ): # change to the number
322+ for block_number in range (0 , enc_metadata_size ):
302323 aes_key2 = physical_UUID
303- decrypted_block = AES_XTS_decrypt_metadata_block (aes_key1 , aes_key2 , block_number , enc_metadata )
304324
325+ decrypted_block = AES_XTS_decrypt_metadata_block (aes_key1 , aes_key2 , block_number , enc_metadata )
305326 decrypted_metadata_block_header = decrypted_block [:64 ]
306327 block_type , metadata_block_size , possibly_lvfwiped = parse_metadata_block_header (decrypted_metadata_block_header )
307328
329+ # data block is wiped, skip
330+ if possibly_lvfwiped == b'LVFwiped' :
331+ continue
332+
308333 # iterate through blocks until find block_type 0x19 - containing encryption context
309334 if block_type == 0x19 :
310- xml_plist = parse_metadata_block_0x19 (decrypted_block [64 :metadata_block_size ]).decode ('latin-1' )
335+ xml_plist = parse_metadata_block_0x19 (decrypted_block [64 :metadata_block_size ]).decode ()
311336
312337 matches = []
313338 for m in re .finditer (CRYPTO_USERS_REGEX , xml_plist ):
314339 matches .append (m .groupdict ())
315340
316- CryptoUsers_dict = {}
317- for user_index in range (len (matches )):
318- CryptoUsers_dict [user_index ] = matches [user_index ]
319- # if present convert user type from string to hex
320- user_type = CryptoUsers_dict [user_index ].get ('UserType' )
321- if user_type :
322- CryptoUsers_dict [user_index ]['UserType' ] = int (CryptoUsers_dict [user_index ]['UserType' ], 16 )
341+ if matches :
342+ CryptoUsers_dict = {}
343+ for user_index in range (len (matches )):
344+ CryptoUsers_dict [user_index ] = matches [user_index ]
345+ # if present convert user type from string to hex
346+ user_type = CryptoUsers_dict [user_index ].get ('UserType' )
347+ if user_type :
348+ CryptoUsers_dict [user_index ]['UserType' ] = int (CryptoUsers_dict [user_index ]['UserType' ], 16 )
323349
324- return CryptoUsers_dict
350+ return CryptoUsers_dict
325351
326352def main ():
327353
0 commit comments