from construct import Struct, ULInt16, ULInt32, String from construct.macros import ULInt64, Padding, If from crypto.aes import AESencryptCBC, AESdecryptCBC from hfs import HFSVolume, HFSFile from keystore.keybag import Keybag from structs import HFSPlusVolumeHeader, kHFSPlusFileRecord, getString, \ kHFSRootParentID from util import search_plist from util.bruteforce import loadKeybagFromVolume import hashlib import os import plistlib import struct """ iOS >= 4 raw images http://opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/hfs/hfs_cprotect.c http://opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/sys/cprotect.h """ cp_root_xattr = Struct("cp_root_xattr", ULInt16("major_version"), ULInt16("minor_version"), ULInt64("flags"), ULInt32("reserved1"), ULInt32("reserved2"), ULInt32("reserved3"), ULInt32("reserved4") ) cprotect_xattr = Struct("cprotect_xattr", ULInt16("xattr_major_version"), ULInt16("xattr_minor_version"), ULInt32("flags"), ULInt32("persistent_class"), ULInt32("key_size"), If(lambda ctx: ctx["xattr_major_version"] >= 4, Padding(20)), String("persistent_key", length=lambda ctx: ctx["key_size"]) ) NSProtectionNone = 4 PROTECTION_CLASSES={ 1:"NSFileProtectionComplete", 2:"NSFileProtectionCompleteUnlessOpen", 3:"NSFileProtectionCompleteUntilFirstUserAuthentication", 4:"NSFileProtectionNone", 5:"NSFileProtectionRecovery?" } #HAX: flags set in finderInfo[3] to tell if the image was already decrypted FLAG_DECRYPTING = 0x454d4664 #EMFd big endian FLAG_DECRYPTED = 0x454d4644 #EMFD big endian class EMFFile(HFSFile): def __init__(self, volume, hfsplusfork, fileID, filekey, deleted=False): super(EMFFile,self).__init__(volume, hfsplusfork, fileID, deleted) self.filekey = filekey self.ivkey = None self.decrypt_offset = 0 if volume.cp_major_version == 4: self.ivkey = hashlib.sha1(filekey).digest()[:16] def processBlock(self, block, lba): iv = self.volume.ivForLBA(lba) ciphertext = AESencryptCBC(block, self.volume.emfkey, iv) if not self.ivkey: clear = AESdecryptCBC(ciphertext, self.filekey, iv) else: clear = "" for i in xrange(len(block)/0x1000): iv = self.volume.ivForLBA(self.decrypt_offset, False) iv = AESencryptCBC(iv, self.ivkey) clear += AESdecryptCBC(ciphertext[i*0x1000:(i+1)*0x1000], self.filekey,iv) self.decrypt_offset += 0x1000 return clear def decryptFile(self): self.decrypt_offset = 0 bs = self.volume.blockSize for extent in self.extents: for i in xrange(extent.blockCount): lba = extent.startBlock+i data = self.volume.readBlock(lba) if len(data) == bs: clear = self.processBlock(data, lba) self.volume.writeBlock(lba, clear) class EMFVolume(HFSVolume): def __init__(self, bdev, device_infos, **kwargs): super(EMFVolume,self).__init__(bdev, **kwargs) volumeid = self.volumeID().encode("hex") if not device_infos: dirname = os.path.dirname(bdev.filename) device_infos = search_plist(dirname, {"dataVolumeUUID":volumeid}) if not device_infos: raise Exception("Missing keyfile") try: self.emfkey = None if device_infos.has_key("EMF"): self.emfkey = device_infos["EMF"].decode("hex") self.lbaoffset = device_infos["dataVolumeOffset"] self.keybag = Keybag.createWithPlist(device_infos) except: raise #Exception("Invalid keyfile") rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") self.decrypted = (self.header.finderInfo[3] == FLAG_DECRYPTED) self.cp_major_version = None self.cp_root = None if rootxattr == None: print "(No root com.apple.system.cprotect xattr)" else: self.cp_root = cp_root_xattr.parse(rootxattr) ver = self.cp_root.major_version print "cprotect version : %d (iOS %d)" % (ver, 4 + int(ver != 2)) assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4 self.cp_major_version = self.cp_root.major_version self.keybag = loadKeybagFromVolume(self, device_infos) def ivForLBA(self, lba, add=True): iv = "" if add: lba = lba + self.lbaoffset lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1); else: lba = lba >> 1; iv += struct.pack("