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,123 @@
import glob
import plistlib
import os
from bplist import BPlistReader
import cPickle
import gzip
def read_file(filename):
f = open(filename, "rb")
data = f.read()
f.close()
return data
def write_file(filename,data):
f = open(filename, "wb")
f.write(data)
f.close()
def makedirs(dirs):
try:
os.makedirs(dirs)
except:
pass
def getHomePath(foldername, filename):
home = os.path.expanduser('~')
folderpath = os.path.join(home, foldername)
if not os.path.exists(folderpath):
makedirs(folderpath)
return os.path.join(folderpath, filename)
def readHomeFile(foldername, filename):
path = getHomePath(foldername, filename)
if not os.path.exists(path):
return None
return read_file(path)
#return path to HOME+foldername+filename
def writeHomeFile(foldername, filename, data):
filepath = getHomePath(foldername, filename)
write_file(filepath, data)
return filepath
def readPlist(filename):
f = open(filename,"rb")
d = f.read(16)
f.close()
if d.startswith("bplist"):
return BPlistReader.plistWithFile(filename)
else:
return plistlib.readPlist(filename)
def parsePlist(s):
if s.startswith("bplist"):
return BPlistReader.plistWithString(s)
else:
return plistlib.readPlistFromString(s)
#http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size
def sizeof_fmt(num):
for x in ['bytes','KB','MB','GB','TB']:
if num < 1024.0:
return "%d%s" % (num, x)
num /= 1024.0
#http://www.5dollarwhitebox.org/drupal/node/84
def convert_bytes(bytes):
bytes = float(bytes)
if bytes >= 1099511627776:
terabytes = bytes / 1099511627776
size = '%.2fT' % terabytes
elif bytes >= 1073741824:
gigabytes = bytes / 1073741824
size = '%.2fG' % gigabytes
elif bytes >= 1048576:
megabytes = bytes / 1048576
size = '%.2fM' % megabytes
elif bytes >= 1024:
kilobytes = bytes / 1024
size = '%.2fK' % kilobytes
else:
size = '%.2fb' % bytes
return size
def xor_strings(a,b):
r=""
for i in xrange(len(a)):
r+= chr(ord(a[i])^ord(b[i]))
return r
hex = lambda data: " ".join("%02X" % ord(i) for i in data)
ascii = lambda data: "".join(c if 31 < ord(c) < 127 else "." for c in data)
def hexdump(d):
for i in xrange(0,len(d),16):
data = d[i:i+16]
print "%08X | %s | %s" % (i, hex(data).ljust(47), ascii(data))
def search_plist(directory, matchDict):
for p in map(os.path.normpath, glob.glob(directory + "/*.plist")):
try:
d = plistlib.readPlist(p)
ok = True
for k,v in matchDict.items():
if d.get(k) != v:
ok = False
break
if ok:
print "Using plist file %s" % p
return d
except:
continue
def save_pickle(filename,data):
f = gzip.open(filename,"wb")
cPickle.dump(data, f, cPickle.HIGHEST_PROTOCOL)
f.close()
def load_pickle(filename):
f = gzip.open(filename,"rb")
data = cPickle.load(f)
f.close()
return data

View File

@@ -0,0 +1,29 @@
def print_table(title, headers, rows):
widths = []
for i in xrange(len(headers)):
z = map(len, [str(row[i]) for row in rows])
z.append(len(headers[i]))
widths.append(max(z))
width = sum(widths) + len(headers) + 1
print "-"* width
print "|" + title.center(width-2) + "|"
print "-"* width
hline = "|"
for i in xrange(len(headers)):
hline += headers[i].ljust(widths[i]) + "|"
print hline
print "-"* width
for row in rows:
line = "|"
for i in xrange(len(row)):
line += str(row[i]).ljust(widths[i]) + "|"
print line
if len(rows) == 0:
print "|" + "No entries".center(width-2) + "|"
print "-"* width
print ""

View File

@@ -0,0 +1,139 @@
import os
import sys
from util import sizeof_fmt, hexdump
from progressbar import ProgressBar
from crypto.aes import AESdecryptCBC, AESencryptCBC
class FileBlockDevice(object):
def __init__(self, filename, offset=0, write=False):
flag = os.O_RDONLY if not write else os.O_RDWR
if sys.platform == 'win32':
flag = flag | os.O_BINARY
self.filename = filename
self.fd = os.open(filename, flag)
self.offset = offset
self.writeFlag = write
self.size = os.path.getsize(filename)
self.setBlockSize(8192)
def setBlockSize(self, bs):
self.blockSize = bs
self.nBlocks = self.size / bs
def readBlock(self, blockNum):
os.lseek(self.fd, self.offset + self.blockSize * blockNum, os.SEEK_SET)
return os.read(self.fd, self.blockSize)
def write(self, offset, data):
if self.writeFlag: #fail silently for testing
os.lseek(self.fd, self.offset + offset, os.SEEK_SET)
return os.write(self.fd, data)
def writeBlock(self, lba, block):
return self.write(lba*self.blockSize, block)
class FTLBlockDevice(object):
def __init__(self, nand, first_lba, last_lba, defaultKey=None):
self.nand = nand
self.pageSize = nand.pageSize
self.blockSize = 0 #not used
self.key = defaultKey
self.lbaoffset = first_lba
self.last_lba = last_lba
self.setBlockSize(self.pageSize)
def setBlockSize(self, bs):
self.blockSize = bs
self.lbasPerPage = self.pageSize / bs
self.lbaToLpnFactor = bs / (self.pageSize+0.0)
self.pagesPerLBA = bs / self.pageSize
if bs > self.pageSize:
pass#raise Exception("FTLBlockDevice lba-size > pageSize not handled")
def readBlock(self, blockNum):
#if (self.lbaoffset + blockNum / self.lbasPerPage) > self.last_lba:
# print "readBlock past last lba", blockNum
# print "readBlock past last lba", blockNum
# return "\x00" * self.blockSize
lpn = int(self.lbaoffset + blockNum * self.lbaToLpnFactor)
d = self.nand.readLPN(lpn, self.key)
for i in xrange(1, self.pagesPerLBA):
d += self.nand.readLPN(lpn + i, self.key)
if self.lbasPerPage:
zz = blockNum % self.lbasPerPage
return d[zz*self.blockSize:(zz+1)*self.blockSize]
return d
def write(self, offset, data):
raise Exception("FTLBlockDevice write method not implemented")
def writeBlock(self, lba, block):
raise Exception("FTLBlockDevice writeBlock method not implemented")
def dumpToFile(self, outputfilename):
hs = sizeof_fmt((self.last_lba - self.lbaoffset) * self.pageSize)
print "Dumping partition to %s (%s)" % (outputfilename, hs)
flags = os.O_CREAT | os.O_RDWR
if sys.platform == "win32":
flags |= os.O_BINARY
fd=os.open(outputfilename, flags)
pbar = ProgressBar(self.last_lba - self.lbaoffset - 1)
pbar.start()
for i in xrange(self.lbaoffset, self.last_lba):
pbar.update(i-self.lbaoffset)
d = self.nand.readLPN(i, self.key)
if i == self.lbaoffset and d[0x400:0x402] != "HX":
print "FAIL? Not HFS partition or wrong key"
os.write(fd, d)
pbar.finish()
os.close(fd)
class IMG3BlockDevice(object):
def __init__(self, filename, key, iv, write=False):
flag = os.O_RDONLY if not write else os.O_RDWR
if sys.platform == 'win32':
flag = flag | os.O_BINARY
self.filename = filename
self.fd = os.open(filename, flag)
self.writeFlag = write
d = os.read(self.fd, 8192)
if d[:4] != "3gmI":
raise Exception("IMG3BlockDevice bad magic %s" % d[:4])
if d[0x34:0x38] != "ATAD":
raise Exception("Fu")
self.encrypted = True
self.key = key
self.iv0 = iv
self.offset = 0x40
self.size = os.path.getsize(filename)
self.setBlockSize(8192)
def setBlockSize(self, bs):
self.blockSize = bs
self.nBlocks = self.size / bs
self.ivs = {0: self.iv0}
def getIVforBlock(self, blockNum):
#read last 16 bytes of previous block to get IV
if not self.ivs.has_key(blockNum):
os.lseek(self.fd, self.offset + self.blockSize * blockNum - 16, os.SEEK_SET)
self.ivs[blockNum] = os.read(self.fd, 16)
return self.ivs[blockNum]
def readBlock(self, blockNum):
os.lseek(self.fd, self.offset + self.blockSize * blockNum, os.SEEK_SET)
data = os.read(self.fd, self.blockSize)
if self.encrypted:
data = AESdecryptCBC(data, self.key, self.getIVforBlock(blockNum))
return data
def _write(self, offset, data):
if self.writeFlag: #fail silently for testing
os.lseek(self.fd, self.offset + offset, os.SEEK_SET)
return os.write(self.fd, data)
def writeBlock(self, lba, data):
if self.encrypted:
data = AESencryptCBC(data, self.key, self.getIVforBlock(lba))
return self._write(lba*self.blockSize, data)

View File

@@ -0,0 +1,251 @@
"""
http://github.com/farcaller/bplist-python/blob/master/bplist.py
"""
import struct
import plistlib
from datetime import datetime, timedelta
class BPListWriter(object):
def __init__(self, objects):
self.bplist = ""
self.objects = objects
def binary(self):
'''binary -> string
Generates bplist
'''
self.data = 'bplist00'
# TODO: flatten objects and count max length size
# TODO: write objects and save offsets
# TODO: write offsets
# TODO: write metadata
return self.data
def write(self, filename):
'''
Writes bplist to file
'''
if self.bplist != "":
pass
# TODO: save self.bplist to file
else:
raise Exception('BPlist not yet generated')
class BPlistReader(object):
def __init__(self, s):
self.data = s
self.objects = []
self.resolved = {}
def __unpackIntStruct(self, sz, s):
'''__unpackIntStruct(size, string) -> int
Unpacks the integer of given size (1, 2 or 4 bytes) from string
'''
if sz == 1:
ot = '!B'
elif sz == 2:
ot = '!H'
elif sz == 4:
ot = '!I'
elif sz == 8:
ot = '!Q'
else:
raise Exception('int unpack size '+str(sz)+' unsupported')
return struct.unpack(ot, s)[0]
def __unpackInt(self, offset):
'''__unpackInt(offset) -> int
Unpacks int field from plist at given offset
'''
return self.__unpackIntMeta(offset)[1]
def __unpackIntMeta(self, offset):
'''__unpackIntMeta(offset) -> (size, int)
Unpacks int field from plist at given offset and returns its size and value
'''
obj_header = struct.unpack('!B', self.data[offset])[0]
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
int_sz = 2**obj_info
return int_sz, self.__unpackIntStruct(int_sz, self.data[offset+1:offset+1+int_sz])
def __resolveIntSize(self, obj_info, offset):
'''__resolveIntSize(obj_info, offset) -> (count, offset)
Calculates count of objref* array entries and returns count and offset to first element
'''
if obj_info == 0x0F:
ofs, obj_count = self.__unpackIntMeta(offset+1)
objref = offset+2+ofs
else:
obj_count = obj_info
objref = offset+1
return obj_count, objref
def __unpackFloatStruct(self, sz, s):
'''__unpackFloatStruct(size, string) -> float
Unpacks the float of given size (4 or 8 bytes) from string
'''
if sz == 4:
ot = '!f'
elif sz == 8:
ot = '!d'
else:
raise Exception('float unpack size '+str(sz)+' unsupported')
return struct.unpack(ot, s)[0]
def __unpackFloat(self, offset):
'''__unpackFloat(offset) -> float
Unpacks float field from plist at given offset
'''
obj_header = struct.unpack('!B', self.data[offset])[0]
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
int_sz = 2**obj_info
return int_sz, self.__unpackFloatStruct(int_sz, self.data[offset+1:offset+1+int_sz])
def __unpackDate(self, offset):
td = int(struct.unpack(">d", self.data[offset+1:offset+9])[0])
return datetime(year=2001,month=1,day=1) + timedelta(seconds=td)
def __unpackItem(self, offset):
'''__unpackItem(offset)
Unpacks and returns an item from plist
'''
obj_header = struct.unpack('!B', self.data[offset])[0]
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
if obj_type == 0x00:
if obj_info == 0x00: # null 0000 0000
return None
elif obj_info == 0x08: # bool 0000 1000 // false
return False
elif obj_info == 0x09: # bool 0000 1001 // true
return True
elif obj_info == 0x0F: # fill 0000 1111 // fill byte
raise Exception("0x0F Not Implemented") # this is really pad byte, FIXME
else:
raise Exception('unpack item type '+str(obj_header)+' at '+str(offset)+ 'failed')
elif obj_type == 0x10: # int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
return self.__unpackInt(offset)
elif obj_type == 0x20: # real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
return self.__unpackFloat(offset)
elif obj_type == 0x30: # date 0011 0011 ... // 8 byte float follows, big-endian bytes
return self.__unpackDate(offset)
elif obj_type == 0x40: # data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
obj_count, objref = self.__resolveIntSize(obj_info, offset)
return plistlib.Data(self.data[objref:objref+obj_count]) # XXX: we return data as str
elif obj_type == 0x50: # string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
obj_count, objref = self.__resolveIntSize(obj_info, offset)
return self.data[objref:objref+obj_count]
elif obj_type == 0x60: # string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
obj_count, objref = self.__resolveIntSize(obj_info, offset)
return self.data[objref:objref+obj_count*2].decode('utf-16be')
elif obj_type == 0x80: # uid 1000 nnnn ... // nnnn+1 is # of bytes
# FIXME: Accept as a string for now
obj_count, objref = self.__resolveIntSize(obj_info, offset)
return plistlib.Data(self.data[objref:objref+obj_count])
elif obj_type == 0xA0: # array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
obj_count, objref = self.__resolveIntSize(obj_info, offset)
arr = []
for i in range(obj_count):
arr.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size]))
return arr
elif obj_type == 0xC0: # set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
# XXX: not serializable via apple implementation
raise Exception("0xC0 Not Implemented") # FIXME: implement
elif obj_type == 0xD0: # dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
obj_count, objref = self.__resolveIntSize(obj_info, offset)
keys = []
for i in range(obj_count):
keys.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size]))
values = []
objref += obj_count*self.object_ref_size
for i in range(obj_count):
values.append(self.__unpackIntStruct(self.object_ref_size, self.data[objref+i*self.object_ref_size:objref+i*self.object_ref_size+self.object_ref_size]))
dic = {}
for i in range(obj_count):
dic[keys[i]] = values[i]
return dic
else:
raise Exception('don\'t know how to unpack obj type '+hex(obj_type)+' at '+str(offset))
def __resolveObject(self, idx):
try:
return self.resolved[idx]
except KeyError:
obj = self.objects[idx]
if type(obj) == list:
newArr = []
for i in obj:
newArr.append(self.__resolveObject(i))
self.resolved[idx] = newArr
return newArr
if type(obj) == dict:
newDic = {}
for k,v in obj.iteritems():
rk = self.__resolveObject(k)
rv = self.__resolveObject(v)
newDic[rk] = rv
self.resolved[idx] = newDic
return newDic
else:
self.resolved[idx] = obj
return obj
def parse(self):
# read header
if self.data[:8] != 'bplist00':
raise Exception('Bad magic')
# read trailer
self.offset_size, self.object_ref_size, self.number_of_objects, self.top_object, self.table_offset = struct.unpack('!6xBB4xI4xI4xI', self.data[-32:])
#print "** plist offset_size:",self.offset_size,"objref_size:",self.object_ref_size,"num_objs:",self.number_of_objects,"top:",self.top_object,"table_ofs:",self.table_offset
# read offset table
self.offset_table = self.data[self.table_offset:-32]
self.offsets = []
ot = self.offset_table
for i in xrange(self.number_of_objects):
offset_entry = ot[:self.offset_size]
ot = ot[self.offset_size:]
self.offsets.append(self.__unpackIntStruct(self.offset_size, offset_entry))
#print "** plist offsets:",self.offsets
# read object table
self.objects = []
k = 0
for i in self.offsets:
obj = self.__unpackItem(i)
#print "** plist unpacked",k,type(obj),obj,"at",i
k += 1
self.objects.append(obj)
# rebuild object tree
#for i in range(len(self.objects)):
# self.__resolveObject(i)
# return root object
return self.__resolveObject(self.top_object)
@classmethod
def plistWithString(cls, s):
parser = cls(s)
return parser.parse()
@classmethod
def plistWithFile(cls, f):
file = open(f,"rb")
parser = cls(file.read())
file.close()
return parser.parse()

View File

@@ -0,0 +1,71 @@
from keystore.keybag import Keybag
from keystore.effaceable import EffaceableLockers
from util.ramdiskclient import RamdiskToolClient
import plistlib
COMPLEXITY={
0: "4 digits",
1: "n digits",
2: "n alphanum"
}
def checkPasscodeComplexity(data_volume):
pl = data_volume.readFile("/mobile/Library/ConfigurationProfiles/UserSettings.plist", returnString=True)
if not pl:
print "Failed to read UserSettings.plist, assuming simple passcode"
return 0
pl = plistlib.readPlistFromString(pl)
#print "passcodeKeyboardComplexity :", pl["restrictedValue"]["passcodeKeyboardComplexity"]
value = pl["restrictedValue"]["passcodeKeyboardComplexity"]["value"]
print "passcodeKeyboardComplexity %d => %s" % (value, COMPLEXITY.get(value))
return pl["restrictedValue"]["passcodeKeyboardComplexity"]["value"]
def loadKeybagFromVolume(volume, device_infos):
systembag = volume.readFile("/keybags/systembag.kb", returnString=True)
if not systembag or not systembag.startswith("bplist"):
print "FAIL: could not read /keybags/systembag.kb from data partition"
return False
lockers = EffaceableLockers(device_infos["lockers"].data)
bag1key = lockers.get("BAG1")[-32:]
keybag = Keybag.createWithSystemkbfile(systembag, bag1key, device_infos.get("key835", "").decode("hex"))
keybag.setDKey(device_infos)
if device_infos.has_key("passcodeKey"):
keybag.unlockWithPasscodeKey(device_infos.get("passcodeKey").decode("hex"))
return keybag
def bruteforcePasscode(device_infos, data_volume):
if device_infos.has_key("passcode"):
print "Passcode already found, no bruteforce required"
return False
kb = data_volume.keybag
if not kb:
return False
rd = RamdiskToolClient.get()
if rd.device_infos.udid != device_infos.udid:
print "Wrong device connected"
return
print "Passcode comlexity (from OpaqueStuff) : %s" % COMPLEXITY.get(kb.passcodeComplexity)
print "Enter passcode or leave blank for bruteforce:"
z = raw_input()
bf = rd.getPasscodeKey(kb.KeyBagKeys, z)
if kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")):
print "Passcode \"%s\" OK" % z
else:
if z != "":
print "Wrong passcode, trying to bruteforce !"
if kb.passcodeComplexity != 0:
print "Complex passcode used, not bruteforcing"
return False
bf = rd.bruteforceKeyBag(kb.KeyBagKeys)
if bf and kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")):
print "Bruteforce successful, passcode : %s" % bf["passcode"]
print "Passcode key : %s" % bf.get("passcodeKey")
if kb.unlocked:
device_infos.update(bf)
device_infos["classKeys"] = kb.getClearClassKeysDict()
device_infos["KeyBagKeys"] = plistlib.Data(kb.KeyBagKeys)
return True
return False

View File

@@ -0,0 +1,16 @@
import base64
def chunks(l, n):
return (l[i:i+n] for i in xrange(0, len(l), n))
def RSA_KEY_DER_to_PEM(data):
a = ["-----BEGIN RSA PRIVATE KEY-----"]
a.extend(chunks(base64.b64encode(data),64))
a.append("-----END RSA PRIVATE KEY-----")
return "\n".join(a)
def CERT_DER_to_PEM(data):
a = ["-----BEGIN CERTIFICATE-----"]
a.extend(chunks(base64.b64encode(data),64))
a.append("-----END CERTIFICATE-----")
return "\n".join(a)

View File

@@ -0,0 +1,71 @@
"""
/**************************************************************
LZSS.C -- A Data Compression Program
***************************************************************
4/6/1989 Haruhiko Okumura
Use, distribute, and modify this program freely.
Please send me your improved versions.
PC-VAN SCIENCE
NIFTY-Serve PAF01022
CompuServe 74050,1022
**************************************************************/
/*
* lzss.c - Package for decompressing lzss compressed objects
*
* Copyright (c) 2003 Apple Computer, Inc.
*
* DRI: Josh de Cesare
*/
"""
from array import array
import struct
N = 4096
F = 18
THRESHOLD = 2
NIL = N
def decompress_lzss(str):
if str[:8] !="complzss":
print "decompress_lzss: complzss magic missing"
return
decompsize = struct.unpack(">L", str[12:16])[0]
text_buf = array("B", " "*(N + F - 1))
src = array("B", str[0x180:])
srclen = len(src)
dst = array("B", " "*decompsize)
r = N - F
srcidx, dstidx, flags, c = 0, 0, 0, 0
while True:
flags >>= 1
if ((flags & 0x100) == 0):
if (srcidx >= srclen):
break
c = src[srcidx]; srcidx += 1
flags = c | 0xFF00;
if (flags & 1):
if (srcidx >= srclen):
break
c = src[srcidx]; srcidx += 1
dst[dstidx] = c; dstidx += 1
text_buf[r] = c; r += 1
r &= (N - 1);
else:
if (srcidx >= srclen):
break
i = src[srcidx]; srcidx += 1
if (srcidx >= srclen):
break
j = src[srcidx]; srcidx += 1
i |= ((j & 0xF0) << 4)
j = (j & 0x0F) + THRESHOLD
for k in xrange(j+1):
c = text_buf[(i + k) & (N - 1)]
dst[dstidx] = c; dstidx += 1
text_buf[r] = c; r += 1
r &= (N - 1)
return dst.tostring()

View File

@@ -0,0 +1,172 @@
import plistlib
import struct
import socket
from datetime import datetime
from progressbar import ProgressBar, Percentage, Bar, SimpleProgress, ETA
from usbmux import usbmux
from util import sizeof_fmt
kIOAESAcceleratorEncrypt = 0
kIOAESAcceleratorDecrypt = 1
kIOAESAcceleratorGIDMask = 0x3E8
kIOAESAcceleratorUIDMask = 0x7D0
class DeviceInfo(dict):
@staticmethod
def create(dict):
try:
assert dict.has_key("dataVolumeUUID")
filename = "%s.plist" % dict.get("dataVolumeUUID")
return DeviceInfo(plistlib.readPlist(filename))
except:
return DeviceInfo(dict)
def save(self):
filename = "%s.plist" % self.get("dataVolumeUUID", "unk")
plistlib.writePlist(self, filename)
#stop doing magic stuff
#def __del__(self):
# self.save()
class RamdiskToolClient(object):
instance = None
@staticmethod
def get():
if not RamdiskToolClient.instance:
RamdiskToolClient.instance = RamdiskToolClient()
return RamdiskToolClient.instance
def __init__(self, udid=None, host="localhost", port=1999):
self.host = host
self.port = port
self.device_infos = {}
self.s = None
self.connect(udid)
self.getDeviceInfos()
def close(self):
if self.s:
self.s.close()
self.s = None
def connect(self, udid=None):
mux = usbmux.USBMux()
mux.process(1.0)
if not mux.devices:
print "Waiting for iOS device"
while not mux.devices:
mux.process(1.0)
if not mux.devices:
print "No device found"
return
dev = mux.devices[0]
print "Connecting to device : " + dev.serial
try:
self.s = mux.connect(dev, self.port)
except:
raise Exception("Connexion to device port %d failed" % self.port)
def getDeviceInfos(self):
self.device_infos = self.send_req({"Request":"DeviceInfo"})
keys = self.grabDeviceKeys()
if keys:
self.device_infos.update(keys)
return DeviceInfo.create(self.device_infos)
def downloadFile(self, path):
res = self.send_req({"Request": "DownloadFile",
"Path": path})
if type(res) == plistlib._InternalDict and res.has_key("Data"):
return res["Data"].data
def getSystemKeyBag(self):
return self.send_req({"Request":"GetSystemKeyBag"})
def bruteforceKeyBag(self, KeyBagKeys):
return self.send_req({"Request":"BruteforceSystemKeyBag",
"KeyBagKeys": plistlib.Data(KeyBagKeys)})
def getEscrowRecord(self, hostID):
return self.send_req({"Request":"GetEscrowRecord",
"HostID": hostID})
def getPasscodeKey(self, keybagkeys, passcode):
return self.send_req({"Request":"KeyBagGetPasscodeKey",
"KeyBagKeys": plistlib.Data(keybagkeys),
"passcode": passcode})
def send_msg(self, dict):
plist = plistlib.writePlistToString(dict)
data = struct.pack("<L",len(plist)) + plist
return self.s.send(data)
def recv_msg(self):
try:
l = self.s.recv(4)
if len(l) != 4:
return None
ll = struct.unpack("<L",l)[0]
data = ""
l = 0
while l < ll:
x = self.s.recv(ll-l)
if not x:
return None
data += x
l += len(x)
return plistlib.readPlistFromString(data)
except:
raise
return None
def send_req(self, dict):
start = None
self.send_msg(dict)
while True:
r = self.recv_msg()
if type(r) == plistlib._InternalDict and r.get("MessageType") == "Progress":
if not start:
pbar = ProgressBar(r.get("Total",100),[SimpleProgress(), " ", ETA(), "\n", Percentage(), " ", Bar()])
pbar.start()
start = datetime.utcnow()
pbar.update( r.get("Progress", 0))
else:
if start:
pbar.finish()
print dict.get("Request"), ":", datetime.utcnow() - start
return r
def aesUID(self, data):
return self.aes(data, kIOAESAcceleratorUIDMask, kIOAESAcceleratorEncrypt)
def aesGID(self, data):
return self.aes(data, kIOAESAcceleratorGIDMask, kIOAESAcceleratorDecrypt)
def aes(self, data, keyMask, mode):
return self.send_req({"Request":"AES",
"input": plistlib.Data(data),
"keyMask": keyMask,
"mode": mode,
"bits": 128
})
def grabDeviceKeys(self):
blobs = {"key835": "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
"key899": "\xD1\xE8\xFC\xB5\x39\x37\xBF\x8D\xEF\xC7\x4C\xD1\xD0\xF1\xD4\xB0",
"key89A": "\xDB\x1F\x5B\x33\x60\x6C\x5F\x1C\x19\x34\xAA\x66\x58\x9C\x06\x61",
"key89B": "\x18\x3E\x99\x67\x6B\xB0\x3C\x54\x6F\xA4\x68\xF5\x1C\x0C\xBD\x49"
}
for k,b in blobs.items():
r = self.aesUID(b)
if not r or r.returnCode != 0 or not r.has_key("data"):
print "AES UID error"
return
blobs[k] = r.data.data.encode("hex")
return blobs
def reboot(self):
print "Rebooting device"
return self.send_req({"Request":"Reboot"})

View File

@@ -0,0 +1,19 @@
import struct
def tlvToDict(blob):
d = {}
for tag,data in loopTLVBlocks(blob):
d[tag] = data
return d
def tlvToList(blob):
return list(loopTLVBlocks(blob))
def loopTLVBlocks(blob):
i = 0
while i + 8 <= len(blob):
tag = blob[i:i+4]
length = struct.unpack(">L",blob[i+4:i+8])[0]
data = blob[i+8:i+8+length]
yield (tag,data)
i += 8 + length