initial code for dumping imessages in a reasonable format

This commit is contained in:
Jeffrey Paul
2014-02-09 00:30:49 +01:00
parent c0021efb13
commit 9dd7628f04
157 changed files with 24178 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
from construct import RepeatUntil
from construct.core import Struct, Union
from construct.macros import *
from crypto.aes import AESdecryptCBC
from crypto.aeswrap import AESUnwrap
from zipfile import crc32
import struct
Dkey = 0x446B6579
EMF = 0x454D4621
BAG1 = 0x42414731
DONE = 0x444f4e45 #locker sentinel
#**** = 0x2A2A2A2A #wildcard for erase
#MAGIC (kL) | LEN (2bytes) | TAG (4) | DATA (LEN)
Locker = Struct("Locker",
String("magic",2),
ULInt16("length"),
Union("tag",
ULInt32("int"),
String("tag",4))
,
String("data", lambda ctx: ctx["length"])
)
Lockers = RepeatUntil(lambda obj, ctx: obj.tag.int == DONE, Locker)
def xor_strings(s, key):
res = ""
for i in xrange(len(s)):
res += chr(ord(s[i]) ^ ord(key[i%len(key)]))
return res
def check_effaceable_header(plog):
z = xor_strings(plog[:16], plog[16:32])
if z[:4] != "ecaF":
return False
plog_generation = struct.unpack("<L", plog[0x38:0x3C])[0]
print "Effaceable generation" , plog_generation
plog_crc = crc32(plog[0x40:0x40 + 960], crc32(plog[0x20:0x3C], crc32(z))) & 0xffffffff
assert plog_crc == struct.unpack("<L", plog[0x3C:0x40])[0] , "Effaceable CRC"
print "Effaceable CRC OK"
return True
class EffaceableLockers(object):
def __init__(self, data):
self.lockers = {}
for l in Lockers.parse(data):
tag = l.tag.int & ~0x80000000
tag = struct.pack("<L", tag)[::-1]
self.lockers[tag] = l.data
def display(self):
print "Lockers : " + ", ".join(sorted(self.lockers.keys()))
def get(self, tag):
return self.lockers.get(tag)
def get_DKey(self, k835):
if self.lockers.has_key("Dkey"):
return AESUnwrap(k835, self.lockers["Dkey"])
def get_EMF(self, k89b):
if self.lockers.has_key("LwVM"):
lwvm = AESdecryptCBC(self.lockers["LwVM"], k89b)
return lwvm[-32:]
elif self.lockers.has_key("EMF!"):
return AESdecryptCBC(self.lockers["EMF!"][4:], k89b)

View File

@@ -0,0 +1,265 @@
from crypto.PBKDF2 import PBKDF2
from crypto.aes import AESdecryptCBC
from crypto.aeswrap import AESUnwrap
from crypto.aeswrap import AESwrap
from crypto.curve25519 import curve25519
from hashlib import sha256, sha1
from util.bplist import BPlistReader
from util.tlv import loopTLVBlocks, tlvToDict
import hmac
import struct
KEYBAG_TAGS = ["VERS", "TYPE", "UUID", "HMCK", "WRAP", "SALT", "ITER"]
CLASSKEY_TAGS = ["CLAS","WRAP","WPKY", "KTYP", "PBKY"] #UUID
KEYBAG_TYPES = ["System", "Backup", "Escrow", "OTA (icloud)"]
SYSTEM_KEYBAG = 0
BACKUP_KEYBAG = 1
ESCROW_KEYBAG = 2
OTA_KEYBAG = 3
#ORed flags in TYPE since iOS 5
FLAG_UIDPLUS = 0x40000000 # UIDPlus hardware key (>= iPad 3)
FLAG_UNKNOWN = 0x80000000
WRAP_DEVICE = 1
WRAP_PASSCODE = 2
KEY_TYPES = ["AES", "Curve25519"]
PROTECTION_CLASSES={
1:"NSFileProtectionComplete",
2:"NSFileProtectionCompleteUnlessOpen",
3:"NSFileProtectionCompleteUntilFirstUserAuthentication",
4:"NSFileProtectionNone",
5:"NSFileProtectionRecovery?",
6: "kSecAttrAccessibleWhenUnlocked",
7: "kSecAttrAccessibleAfterFirstUnlock",
8: "kSecAttrAccessibleAlways",
9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly",
10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly",
11: "kSecAttrAccessibleAlwaysThisDeviceOnly"
}
"""
device key : key 0x835
"""
class Keybag(object):
def __init__(self, data):
self.type = None
self.uuid = None
self.wrap = None
self.deviceKey = None
self.unlocked = False
self.passcodeComplexity = 0
self.attrs = {}
self.classKeys = {}
self.KeyBagKeys = None #DATASIGN blob
self.parseBinaryBlob(data)
@staticmethod
def getSystemkbfileWipeID(filename):
mkb = BPlistReader.plistWithFile(filename)
return mkb["_MKBWIPEID"]
@staticmethod
def createWithPlist(pldict):
k835 = pldict.key835.decode("hex")
data = ""
if pldict.has_key("KeyBagKeys"):
data = pldict["KeyBagKeys"].data
else:
data = ""
keybag = Keybag.createWithDataSignBlob(data, k835)
if pldict.has_key("passcodeKey"):
if keybag.unlockWithPasscodeKey(pldict["passcodeKey"].decode("hex")):
print "Keybag unlocked with passcode key"
else:
print "FAILed to unlock keybag with passcode key"
#HAX: inject DKey
keybag.setDKey(pldict)
return keybag
def setDKey(self, device_infos):
self.classKeys[4] = {"CLAS": 4, "KEY": device_infos["DKey"].decode("hex")}
@staticmethod
def createWithSystemkbfile(filename, bag1key, deviceKey=None):
if filename.startswith("bplist"): #HAX
mkb = BPlistReader.plistWithString(filename)
else:
mkb = BPlistReader.plistWithFile(filename)
try:
decryptedPlist = AESdecryptCBC(mkb["_MKBPAYLOAD"].data, bag1key, mkb["_MKBIV"].data, padding=True)
except:
print "FAIL: AESdecryptCBC _MKBPAYLOAD => wrong BAG1 key ?"
return None
if not decryptedPlist.startswith("bplist"):
print "FAIL: decrypted _MKBPAYLOAD is not bplist"
return None
decryptedPlist = BPlistReader.plistWithString(decryptedPlist)
blob = decryptedPlist["KeyBagKeys"].data
kb = Keybag.createWithDataSignBlob(blob, deviceKey)
if decryptedPlist.has_key("OpaqueStuff"):
OpaqueStuff = BPlistReader.plistWithString(decryptedPlist["OpaqueStuff"].data)
kb.passcodeComplexity = OpaqueStuff.get("keyboardType")
return kb
@staticmethod
def createWithDataSignBlob(blob, deviceKey=None):
keybag = tlvToDict(blob)
kb = Keybag(keybag.get("DATA", ""))
kb.deviceKey = deviceKey
kb.KeyBagKeys = blob
kb.unlockAlwaysAccessible()
if len(keybag.get("SIGN", "")):
hmackey = AESUnwrap(deviceKey, kb.attrs["HMCK"])
#hmac key and data are swapped (on purpose or by mistake ?)
sigcheck = hmac.new(key=keybag["DATA"], msg=hmackey, digestmod=sha1).digest()
#fixed in ios 7
if kb.attrs["VERS"] >= 4:
sigcheck = hmac.new(key=hmackey, msg=keybag["DATA"], digestmod=sha1).digest()
if sigcheck != keybag.get("SIGN", ""):
print "Keybag: SIGN check FAIL"
return kb
@staticmethod
def createWithBackupManifest(manifest, password, deviceKey=None):
kb = Keybag(manifest["BackupKeyBag"].data)
kb.deviceKey = deviceKey
if not kb.unlockBackupKeybagWithPasscode(password):
print "Cannot decrypt backup keybag. Wrong password ?"
return
return kb
def isBackupKeybag(self):
return self.type == BACKUP_KEYBAG
def parseBinaryBlob(self, data):
currentClassKey = None
for tag, data in loopTLVBlocks(data):
if len(data) == 4:
data = struct.unpack(">L", data)[0]
if tag == "TYPE":
self.type = data & 0x3FFFFFFF #ignore the flags
if self.type > 3:
print "FAIL: keybag type > 3 : %d" % self.type
elif tag == "UUID" and self.uuid is None:
self.uuid = data
elif tag == "WRAP" and self.wrap is None:
self.wrap = data
elif tag == "UUID":
if currentClassKey:
self.classKeys[currentClassKey["CLAS"]] = currentClassKey
currentClassKey = {"UUID": data}
elif tag in CLASSKEY_TAGS:
currentClassKey[tag] = data
else:
self.attrs[tag] = data
if currentClassKey:
self.classKeys[currentClassKey["CLAS"]] = currentClassKey
def getPasscodekeyFromPasscode(self, passcode):
if self.type == BACKUP_KEYBAG or self.type == OTA_KEYBAG:
return PBKDF2(passcode, self.attrs["SALT"], iterations=self.attrs["ITER"]).read(32)
else:
#Warning, need to run derivation on device with this result
return PBKDF2(passcode, self.attrs["SALT"], iterations=1).read(32)
def unlockBackupKeybagWithPasscode(self, passcode):
if self.type != BACKUP_KEYBAG and self.type != OTA_KEYBAG:
print "unlockBackupKeybagWithPasscode: not a backup keybag"
return False
return self.unlockWithPasscodeKey(self.getPasscodekeyFromPasscode(passcode))
def unlockAlwaysAccessible(self):
for classkey in self.classKeys.values():
k = classkey["WPKY"]
if classkey["WRAP"] == WRAP_DEVICE:
if not self.deviceKey:
continue
k = AESdecryptCBC(k, self.deviceKey)
classkey["KEY"] = k
return True
def unlockWithPasscodeKey(self, passcodekey):
if self.type != BACKUP_KEYBAG and self.type != OTA_KEYBAG:
if not self.deviceKey:
print "ERROR, need device key to unlock keybag"
return False
for classkey in self.classKeys.values():
if not classkey.has_key("WPKY"):
continue
k = classkey["WPKY"]
if classkey["WRAP"] & WRAP_PASSCODE:
k = AESUnwrap(passcodekey, classkey["WPKY"])
if not k:
return False
if classkey["WRAP"] & WRAP_DEVICE:
if not self.deviceKey:
continue
k = AESdecryptCBC(k, self.deviceKey)
classkey["KEY"] = k
self.unlocked = True
return True
def unwrapCurve25519(self, persistent_class, persistent_key):
assert len(persistent_key) == 0x48
#assert persistent_class == 2 #NSFileProtectionCompleteUnlessOpen
mysecret = self.classKeys[persistent_class]["KEY"]
mypublic = self.classKeys[persistent_class]["PBKY"]
hispublic = persistent_key[:32]
shared = curve25519(mysecret, hispublic)
md = sha256('\x00\x00\x00\x01' + shared + hispublic + mypublic).digest()
return AESUnwrap(md, persistent_key[32:])
def unwrapKeyForClass(self, clas, persistent_key, printError=True):
if not self.classKeys.has_key(clas) or not self.classKeys[clas].has_key("KEY"):
if printError: print "Keybag key %d missing or locked" % clas
return ""
ck = self.classKeys[clas]["KEY"]
#if self.attrs.get("VERS", 2) >= 3 and clas == 2:
if self.attrs.get("VERS", 2) >= 3 and self.classKeys[clas].get("KTYP", 0) == 1:
return self.unwrapCurve25519(clas, persistent_key)
if len(persistent_key) == 0x28:
return AESUnwrap(ck, persistent_key)
return
def wrapKeyForClass(self, clas, persistent_key):
if not self.classKeys.has_key(clas) or not self.classKeys[clas].has_key("KEY"):
print "Keybag key %d missing or locked" % clas
return ""
ck = self.classKeys[clas]["KEY"]
return AESwrap(ck, persistent_key)
def printClassKeys(self):
print "Keybag type : %s keybag (%d)" % (KEYBAG_TYPES[self.type], self.type)
print "Keybag version : %d" % self.attrs["VERS"]
print "Keybag UUID : %s" % self.uuid.encode("hex")
print "-"*128
print "".join(["Class".ljust(53),
"WRAP".ljust(5),
"Type".ljust(11),
"Key".ljust(65),
"Public key"])
print "-"*128
for k, ck in self.classKeys.items():
if k == 6: print ""
print "".join([PROTECTION_CLASSES.get(k).ljust(53),
str(ck.get("WRAP","")).ljust(5),
KEY_TYPES[ck.get("KTYP",0)].ljust(11),
ck.get("KEY", "").encode("hex").ljust(65),
ck.get("PBKY", "").encode("hex")])
print ""
def getClearClassKeysDict(self):
if self.unlocked:
d = {}
for ck in self.classKeys.values():
d["%d" % ck["CLAS"]] = ck.get("KEY","").encode("hex")
return d