initial code for dumping imessages in a reasonable format
This commit is contained in:
@@ -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
|
||||
@@ -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 ""
|
||||
139
dump-imessages/iphone-dataprotection/python_scripts/util/bdev.py
Normal file
139
dump-imessages/iphone-dataprotection/python_scripts/util/bdev.py
Normal 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)
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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"})
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user