How to decrypt and encrypt iphone backup using python IOS 10.1.1 -


with requirement set apple , mdm force encryption on backups, there way decrypt , encrypt iphone backups?

we need access raw files saved within backup make slight changes few plist files inside, problem need in code doing hand take forever 1000+ phones. have of code written in python , editing plist seems working fine until put on mdm profile resulting in phone requiring encrypted backups point on.

here found far: unable make 10> change file mbdb db file. reading others, seems that might have change how files encrypted. or update on helpful!

#!/usr/bin/env python2.7 # coding: utf-8  # default true avoid leaking secrets anonymize_output = true  import pbkdf2 # http://iphone-dataprotection.googlecode.com/hg-history/tip/python_scripts/crypto/pbkdf2.py import bplist # https://github.com/farcaller/bplist-python/raw/master/bplist.py import crypto.cipher.aes # https://www.dlitz.net/software/pycrypto/  import hashlib import os.path import pprint import sys  backup_dir = "data/encrypted"  def main():     open(os.path.join(backup_dir, 'manifest.plist'), 'rb') infile:         manifest_plist = bplist.bplistreader.plistwithstring(infile.read())     keybag = keybag(manifest_plist['backupkeybag'])     # actual keys unknown, wrapped keys known     keybag.printclasskeys()     if not keybag.unlockwithpasscode('test'):         raise exception('could not unlock keybag; bad password?')     # keys known     keybag.printclasskeys()      item in process_mbdb_file(             os.path.join(backup_dir, 'manifest.mbdb')).values():         filename = item['filename']         if not filename.endswith('calculator.plist'):             continue         encryption_key = item['unknown1'][4:]         protection_class = item['flag']         backup_filename = os.path.join(             backup_dir,             hashlib.sha1(item['domain'] + '-' + item['filename']).hexdigest())         open(backup_filename, 'rb') infile:             data = infile.read()         print '== encrypted data:'         print wrap(data)         print          key = keybag.unwrapkeyforclass(protection_class, encryption_key)         # truncate actual length, because encryption may introduce padding         decrypted_data = aesdecryptcbc(data, key)[:item['filelen']]         print '== decrypted data:'         print wrap(decrypted_data)         print          print '== pretty-printed calculator preferences'         pprint.pprint(bplist.bplistreader.plistwithstring(decrypted_data))  ## # section copied parts of iphone-dataprotection # http://code.google.com/p/iphone-dataprotection/  import struct  classkey_tags = ["clas","wrap","wpky", "ktyp", "pbky"]  #uuid keybag_types = ["system", "backup", "escrow", "ota (icloud)"] 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" } wrap_device = 1 wrap_passcode = 2  class keybag(object):     def __init__(self, data):         self.type = none         self.uuid = none         self.wrap = none         self.devicekey = none         self.attrs = {}         self.classkeys = {}         self.keybagkeys = none #datasign blob         self.parsebinaryblob(data)      def parsebinaryblob(self, data):         currentclasskey = none          tag, data in looptlvblocks(data):             if len(data) == 4:                 data = struct.unpack(">l", data)[0]             if tag == "type":                 self.type = data                 if self.type > 3:                     print "fail: keybag type > 3 : %d" % self.type             elif tag == "uuid" , self.uuid none:                 self.uuid = data             elif tag == "wrap" , self.wrap 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 unlockwithpasscode(self, passcode):         passcodekey = pbkdf2.pbkdf2(passcode, self.attrs["salt"],                                     iterations=self.attrs["iter"]).read(32)         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                 classkey["key"] = k         return true      def unwrapkeyforclass(self, protection_class, persistent_key):         ck = self.classkeys[protection_class]["key"]         if len(persistent_key) != 0x28:             raise exception("invalid key length")         return aesunwrap(ck, persistent_key)      def printclasskeys(self):         print "== keybag"         print "keybag type: %s keybag (%d)" % (keybag_types[self.type], self.type)         print "keybag version: %d" % self.attrs["vers"]         print "keybag iterations: %d, iv=%s" % (             self.attrs["iter"], anonymize(self.attrs["salt"].encode('hex')))         print "keybag uuid: %s" % anonymize(self.uuid.encode("hex"))         print "-"*209         print "".join(["class".ljust(53),                       "wrap".ljust(5),                       "type".ljust(11),                       "key".ljust(65),                       "wpky".ljust(65),                       "public key"])         print "-"*208         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),                  anonymize(ck.get("key", "").encode("hex")).ljust(65),                  anonymize(ck.get("wpky", "").encode("hex")).ljust(65),                  ck.get("pbky", "").encode("hex")])         print  def looptlvblocks(blob):     = 0     while + 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)         += 8 + length  def unpack64bit(s):     return struct.unpack(">q",s)[0] def pack64bit(s):     return struct.pack(">q",s)  def aesunwrap(kek, wrapped):     c = []     in xrange(len(wrapped)/8):         c.append(unpack64bit(wrapped[i*8:i*8+8]))     n = len(c) - 1     r = [0] * (n+1)     = c[0]      in xrange(1,n+1):         r[i] = c[i]      j in reversed(xrange(0,6)):         in reversed(xrange(1,n+1)):             todec = pack64bit(a ^ (n*j+i))             todec += pack64bit(r[i])             b = crypto.cipher.aes.new(kek).decrypt(todec)             = unpack64bit(b[:8])             r[i] = unpack64bit(b[8:])      if != 0xa6a6a6a6a6a6a6a6:         return none     res = "".join(map(pack64bit, r[1:]))     return res  zeroiv = "\x00"*16 def aesdecryptcbc(data, key, iv=zeroiv, padding=false):     if len(data) % 16:         print "aesdecryptcbc: data length not /16, truncating"         data = data[0:(len(data)/16) * 16]     data = crypto.cipher.aes.new(key, crypto.cipher.aes.mode_cbc, iv).decrypt(data)     if padding:         return removepadding(16, data)     return data  ## # .mbdb-parsing code http://stackoverflow.com/q/3085153/14558:  def getint(data, offset, intsize):     """retrieve integer (big-endian) , new offset current offset"""     value = 0     while intsize > 0:         value = (value<<8) + ord(data[offset])         offset = offset + 1         intsize = intsize - 1     return value, offset  def getstring(data, offset):     """retrieve string , new offset current offset data"""     if data[offset] == chr(0xff) , data[offset+1] == chr(0xff):         return '', offset+2 # blank string     length, offset = getint(data, offset, 2) # 2-byte length     value = data[offset:offset+length]     return value, (offset + length)  def process_mbdb_file(filename):     mbdb = {} # map offset of info in file => file info     data = open(filename).read()     if data[0:4] != "mbdb": raise exception("this not mbdb file")     offset = 4     offset = offset + 2 # value x05 x00, not sure     while offset < len(data):         fileinfo = {}         fileinfo['start_offset'] = offset         fileinfo['domain'], offset = getstring(data, offset)         fileinfo['filename'], offset = getstring(data, offset)         fileinfo['linktarget'], offset = getstring(data, offset)         fileinfo['datahash'], offset = getstring(data, offset)         fileinfo['unknown1'], offset = getstring(data, offset)         fileinfo['mode'], offset = getint(data, offset, 2)         fileinfo['unknown2'], offset = getint(data, offset, 4)         fileinfo['unknown3'], offset = getint(data, offset, 4)         fileinfo['userid'], offset = getint(data, offset, 4)         fileinfo['groupid'], offset = getint(data, offset, 4)         fileinfo['mtime'], offset = getint(data, offset, 4)         fileinfo['atime'], offset = getint(data, offset, 4)         fileinfo['ctime'], offset = getint(data, offset, 4)         fileinfo['filelen'], offset = getint(data, offset, 8)         fileinfo['flag'], offset = getint(data, offset, 1)         fileinfo['numprops'], offset = getint(data, offset, 1)         fileinfo['properties'] = {}         ii in range(fileinfo['numprops']):             propname, offset = getstring(data, offset)             propval, offset = getstring(data, offset)             fileinfo['properties'][propname] = propval         mbdb[fileinfo['start_offset']] = fileinfo     return mbdb  ## # , here utility functions, 1 making sure don’t leak # secret keys when posting output on stack exchange  if anonymize_output:     memo = {}     def anonymize(s):         global memo         if s in memo:             return memo[s]         import random         import string         r = random.random(0)         possible_alphabets = [             string.digits,             string.digits + 'abcdef',             "".join(chr(x) x in range(0, 256)),         ]         in possible_alphabets:             if all(c in c in s):                 alphabet =                 break         ret = "".join([r.choice(alphabet) in range(len(s))])         memo[s] = ret         return ret else:     def anonymize(s): return s  def wrap(s, width=78):     "return width-wrapped repr(s)-like string without breaking on \’s"     s = repr(s)     quote = s[0]     s = s[1:-1]     ret = []     while len(s):         = s.rfind('\\', 0, width)         if <= width - 4: # "\x??" 4 characters             = width         ret.append(s[:i])         s = s[i:]     return '\n'.join("%s%s%s" % (quote, line ,quote) line in ret)  if __name__ == '__main__':     main() 


Comments

Popular posts from this blog

asynchronous - C# WinSCP .NET assembly: How to upload multiple files asynchronously -

aws api gateway - SerializationException in posting new Records via Dynamodb Proxy Service in API -

asp.net - Problems sending emails from forum -