def do_undelete(self, p):
     if not self.data.keybag.unlocked:
         print "Warning, keybag is not unlocked, some files will be inaccessible"
     if not self.carver:
         self.carver = NANDCarver(self.data, self.image)
     if False:  #len(p):
         z = self.volume.catalogTree.getLBAsHax()
         v = self.volume.getFileRecordForPath(self.curdir)
         folderId = v.folderID
         f = lambda k, v: k.parentID == folderId
     else:
         z = None
         f = None
     self.carver.carveDeletedFiles_fast(z, f)
 def do_undelete(self, p):
     if not self.data.keybag.unlocked:
         print "Warning, keybag is not unlocked, some files will be inaccessible"
     if not self.carver:
         if self.image.ppn:
             self.carver = PPNCarver(self.data, self.image)
         else:
             self.carver = NANDCarver(self.data, self.image)
     if False:#len(p):
         z =  self.volume.catalogTree.getLBAsHax()
         v = self.volume.getFileRecordForPath(self.curdir)
         folderId = v.folderID
         f = lambda k,v: k.parentID == folderId
     else:
         z = None
         f = None
     self.carver.carveDeletedFiles_fast(z, f)
예제 #3
0
class ExaminerShell(Cmd):
    def __init__(self, image, completekey='tab', stdin=None, stdout=None):
        Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
        self.curdir = "/"
        self.rdisk = None
        if image.filename == "remote":
            self.rdisk = RamdiskToolClient.get()
        self.device_infos = image.device_infos
        self.complete_open = self._complete
        self.complete_xattr = self._complete
        self.complete_cprotect = self._complete
        self.complete_ls = self._complete
        self.complete_cd = self._complete
        self.complete_plist = self._complete
        self.complete_xxd = self._complete
        self.image = image
        if image.ppn and image.filename == "remote":
            self.savepath = "."
            print "Remote PPN device, use nand_dump + save, other commands will fail"
            return
        self.system = image.getPartitionVolume(0)
        self.data = image.getPartitionVolume(1)
        self.volume = None
        self.volname = ""
        grab_system_version(self.system, self.device_infos)
        print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) * "un")
        self.deviceName = get_device_name(self.data)
        self.do_data("")
        self.savepath = os.path.join(os.path.dirname(image.filename), "%s.plist" % self.device_infos.udid[:10])
        #if image.iosVersion > 3 and not image.device_infos.has_key("passcode"):
        #    print "No passcode found in plist file, bruteforce required to access protected data"
        
        self.carver = None
    
    def set_partition(self, name, vol):
        self.volume = vol
        self.do_cd("/")
        self.volname = name
        self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, self.curdir)
    
    def do_info(self, p):
        pprint(self.device_infos)
        
    def do_save(self, p):
        print "Save device information plist to [%s]:" % self.savepath,
        path2 = raw_input()
        if path2: self.savepath = path2
        if os.path.exists(self.savepath):
            print "File already exists, overwrite ? [y/n]:",
            if raw_input() != "y":
                return
        plistlib.writePlist(self.device_infos, self.savepath)
        
    def do_system(self, p):
        self.set_partition("system", self.system)
    
    def do_data(self, p):
        self.set_partition("data", self.data)
    
    def do_pix(self, p):
        self.do_data("")
        self.do_cd("/mobile/Media/DCIM/100APPLE")
        
    def do_keychain(self, p):
        #self.data.readFile("/Keychains/keychain-2.db")
        self._pull__and_open_sqlitedb("/Keychains/keychain-2.db")
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.print_all(False)
    
    def do_keychain_cert(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.cert(id, filename)
    
    def do_keychain_key(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.key(id, filename)
    
    def do_exit(self, p):
        return True
    
    def do_quit(self, p):
        return self.do_exit(p)

    def do_reboot(self, p):
        if not self.rdisk:
            self.rdisk = RamdiskToolClient.get()
        self.rdisk.reboot()
        return self.do_exit(p)
    
    def do_pwd(self, p):
        print self.curdir
            
    def do_cd(self, p):
        if len(p) == 0: p = "/"
        if not p.startswith("/"):
            new = self.curdir + p
        else:
            new = p
        if not p.endswith("/"): new = new + "/"
        d = self.volume.ls(new)
        if d != None:
            self.curdir = new
            self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, new)
        else:
            print "%s not found/is not a directory" % new
            
    def get_path(self, p):
        path = p
        if not path.startswith("/"):
            path = self.curdir + path
        return path
   
    def _complete(self, text, line, begidx, endidx):
        filename = text.split("/")[-1]
        dirname = "/".join(text.split("/")[:-1])
        if text.startswith("/"):
            contents = self.volume.ls(dirname)
        else:
            contents = self.volume.ls(self.curdir + dirname)
        if not contents:
            return []
        if dirname != "" and not dirname.endswith("/"):
            dirname += "/"
        res = [dirname + x for x in contents.keys() if x.startswith(filename)]
        return res
    
    #TODO if size==0 check if compressed
    def do_ls(self, p):
        dirDict = self.volume.ls((self.curdir + "/" + p).replace("//","/")) 
        if not dirDict:
            return
        for name in sorted(dirDict.keys()):
            size = ""
            protection_class = ""
            record = dirDict[name]
            if hasattr(record, "fileID"):
                size = sizeof_fmt(record.dataFork.logicalSize)
                cprotect = self.volume.getXattr(record.fileID, "com.apple.system.cprotect")
                if cprotect:
                    protection_class = PROTECTION_CLASSES[struct.unpack("<L", cprotect[8:12])[0]]
            print "%s\t%s\t%s\t%s" % (name[:30].ljust(30), size.ljust(10), hfs_date(record.createDate), protection_class)
    
    def do_undelete(self, p):
        if not self.data.keybag.unlocked:
            print "Warning, keybag is not unlocked, some files will be inaccessible"
        if not self.carver:
            if self.image.ppn:
                self.carver = PPNCarver(self.data, self.image)
            else:
                self.carver = NANDCarver(self.data, self.image)
        if False:#len(p):
            z =  self.volume.catalogTree.getLBAsHax()
            v = self.volume.getFileRecordForPath(self.curdir)
            folderId = v.folderID
            f = lambda k,v: k.parentID == folderId
        else:
            z = None
            f = None
        self.carver.carveDeletedFiles_fast(z, f)
        #self.carver.carveDeleteFiles_slow(z, f)
    
    def do_xattr(self, p):
        xattr = self.volume.listXattrs(self.get_path(p))
        if not xattr:
            return
        for name, value in xattr.items():
            print name, value.encode("hex")

    def do_protected_files(self, p):
        self.data.list_protected_files()
        
    def do_cprotect(self, p):
        id = self.volume.getFileIDByPath(self.get_path(p))
        if not id:
            return
        
        cprotect = self.volume.getXattr(id, "com.apple.system.cprotect")
        if not cprotect:
            return
        cp = cprotect_xattr.parse(cprotect)
        print cp
        print "Protection class %d => %s" % (cp.persistent_class, PROTECTION_CLASSES.get(cp.persistent_class))
        if not cp.persistent_key:
            return
        fk = self.volume.getFileKeyForCprotect(cprotect)
        if fk:
            print "Unwrapped file key : %s" % fk.encode("hex")
        else:
            print "Cannot decrypt file key"


    def do_open(self, p):
        path = self.get_path(p)
        if self.volume.readFile(path):
            os.startfile(os.path.basename(path))

    def do_xxd(self, p):
        t = p.split()
        path = self.get_path(t[0])
        data = self.volume.readFile(path, returnString=True)
        if not data:
            return
        if len(t) > 1:
            hexdump(data[:int(t[1])])
        else:
            hexdump(data[:0x200])
            if len(data) > 0x200:
                print "Output truncated to %d bytes" % 0x200
    
    def do_effaceable(self, p):
        print "Effaceable Lockers"
        for k,v in self.image.lockers.lockers.items():
            print "%s: %s" % (k, v.encode("hex"))
    
    def do_BAG1(self, p):
        print "BAG1 locker from effaceable storage"
        bag1 = self.image.lockers.get("BAG1")
        hexdump(bag1)
        print "IV:", bag1[4:20].encode("hex")
        print "Key:", bag1[20:].encode("hex")
    
    def do_keybag(self, p):
        self.data.keybag.printClassKeys()
        
    def do_plist(self, p):
        d = None
        data = self.volume.readFile(self.get_path(p), returnString=True)
        if data:
            d = parsePlist(data)
            pprint(d)
        else:
            try:
                d = readPlist(p)
                if d: pprint(d)
            except:
                pass
        if d and d.has_key("_MKBIV"):
            print "=>System keybag file"
            print "_MKBPAYLOAD: encrypted"
            print "_MKBIV: %s" % d["_MKBIV"].data.encode("hex")
            print "_MKBWIPEID: 0x%x (%s)" % (d["_MKBWIPEID"], ("%x"%(d["_MKBWIPEID"])).decode("hex"))
    
    def do_bruteforce(self, p):
        if bruteforcePasscode(self.image.device_infos, self.data):
            print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) * "un")
            self.do_save("")
    
    def do_ptable(self, p):
        pt = self.image.getPartitionTable()
        print "Block device partition table"
        print "".join(map(lambda x:x.ljust(12), ["Index", "Name", "Start LBA", "End LBA", "Size"]))
        for i in xrange(len(pt)):
            p = pt[i]
            print "".join(map(lambda x:str(x).ljust(12), [i, p.name, p.first_lba, p.last_lba, sizeof_fmt((p.last_lba - p.first_lba)*self.image.pageSize)])) 

    def do_nand_dump(self, p):
        if len(p)==0:
            print "Usage: nand_dump my_nand.bin"
            return
        self.image.dump(p)

    def do_dd(self, p):
        if len(p)==0:
            print "Usage: dd output_file.dmg"
            return
        self.volume.bdev.dumpToFile(p.split()[0])
        
    def do_img3(self, p):
        self.image.extract_img3s("./")
    
    def do_shsh(self, p):
        self.image.extract_shsh()

    def _pull__and_open_sqlitedb(self, path):
        outdir = "/tmp/"
        self.volume.readFile(path, outdir)
        self.volume.readFile(path + "-shm", outdir)
        if self.volume.readFile(path + "-wal", outdir):
            if sqlite3.sqlite_version < "3.7":
                print "Python sqlite3 version %s < 3.7" % (sqlite3.sqlite_version)
                print "Please update python sqlite dll to use this feature on iOS 6+ images (WAL)"
                return
        dbpath = os.path.join(outdir, os.path.basename(path))
        print dbpath
        return sqlite3.connect(dbpath)

    def do_sms(self, p):
        conn = self._pull__and_open_sqlitedb("/mobile/Library/SMS/sms.db")
        if not conn: return
        #http://linuxsleuthing.blogspot.fr/2012/10/whos-texting-ios6-smsdb.html
        z = conn.execute("""SELECT
            DATETIME(date + 978307200, 'unixepoch', 'localtime') as Date,
              h.id as "Phone Number",
                CASE is_from_me
                WHEN 0 THEN "Received"
                WHEN 1 THEN "Sent"
                ELSE "Unknown"
                  END as Type,
              text as Text
              FROM message m, handle h
              WHERE h.rowid = m.handle_id
              ORDER BY Date ASC;""")

        print " ".join(["Date".ljust(20), "To/From".ljust(12), "Text"])
        for row in z.fetchall():
            print row[0].ljust(21) + row[1].ljust(13) + row[3][:80]

    def do_contacts(self, p):
        conn = self._pull__and_open_sqlitedb("/mobile/Library/AddressBook/AddressBook.sqlitedb")
        if not conn: return
        #https://gist.github.com/laacz/1180765
        conn = sqlite3.connect("AddressBook.sqlitedb")
        z = conn.execute("""select ABPerson.first,
                                 ABPerson.last,
                                 ABPerson.Organization as organization,
                                 (select value from ABMultiValue where property = 4 and record_id = ABPerson.ROWID LIMIT 1) as email
                                 from ABPerson
                                order by ABPerson.ROWID;""")

        print "".join(["First name".ljust(15), "Last name".ljust(15), "Organization".ljust(15), "Email"])
        for row in z.fetchall():
            row = map(str, row)
            print row[0].ljust(15) + row[1].ljust(15) + row[2].ljust(15) + row[3]

    def do_email(self, p):
        conn = self._pull__and_open_sqlitedb("/mobile/Library/Mail/Protected Index")
        #conn = self._pull__and_open_sqlitedb("/mobile/Library/Mail/Envelope Index")
        z = conn.execute("SELECT message_id, sender, _to, subject from messages")
        print "  ".join(map(lambda x:x.ljust(40), ["From", "To", "Subject"]))
        for row in z.fetchall():
            print "  ".join(map(lambda x:x.ljust(40)[:40], row[1:]))

    def do_debug(self,p):
        from IPython.Shell import IPShellEmbed
        ipshell = IPShellEmbed()
        ipshell(local_ns=locals())
class ExaminerShell(Cmd):
    def __init__(self, image, completekey='tab', stdin=None, stdout=None):
        Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
        self.curdir = "/"
        self.rdisk = None
        if image.filename == "remote":
            self.rdisk = RamdiskToolClient.get()
        self.device_infos = image.device_infos
        self.complete_open = self._complete
        self.complete_xattr = self._complete
        self.complete_cprotect = self._complete
        self.complete_ls = self._complete
        self.complete_cd = self._complete
        self.complete_plist = self._complete
        self.complete_xxd = self._complete
        self.image = image
        self.system = image.getPartitionVolume(0)
        self.data = image.getPartitionVolume(1)
        self.volume = None
        self.volname = ""
        grab_system_version(self.system, self.device_infos)
        print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) * "un")
        self.deviceName = get_device_name(self.data)
        self.do_data("")
        self.savepath = os.path.join(os.path.dirname(image.filename), "%s.plist" % self.device_infos.udid[:10])
        #if image.iosVersion > 3 and not image.device_infos.has_key("passcode"):
        #    print "No passcode found in plist file, bruteforce required to access protected data"
        
        self.carver = None
    
    def set_partition(self, name, vol):
        self.volume = vol
        self.do_cd("/")
        self.volname = name
        self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, self.curdir)
    
    def do_info(self, p):
        pprint(self.device_infos)
        
    def do_save(self, p):
        print "Save device information plist to [%s]:" % self.savepath,
        path2 = raw_input()
        if path2: self.savepath = path2
        if os.path.exists(self.savepath):
            print "File already exists, overwrite ? [y/n]:",
            if raw_input() != "y":
                return
        plistlib.writePlist(self.device_infos, self.savepath)
        
    def do_system(self, p):
        self.set_partition("system", self.system)
    
    def do_data(self, p):
        self.set_partition("data", self.data)
    
    def do_pix(self, p):
        self.do_data("")
        self.do_cd("/mobile/Media/DCIM/100APPLE")
        
    def do_keychain(self, p):
        self.data.readFile("/Keychains/keychain-2.db")
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.print_all(False)
    
    def do_keychain_cert(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.cert(id, filename)
    
    def do_keychain_key(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load("keychain-2.db", self.data.keybag, self.image.device_infos["key835"].decode("hex"))
        keychain.key(id, filename)
    
    def do_exit(self, p):
        return True
    
    def do_quit(self, p):
        return self.do_exit(p)

    def do_reboot(self, p):
        if not self.rdisk:
            self.rdisk = RamdiskToolClient.get()
        self.rdisk.reboot()
        return self.do_exit(p)
    
    def do_pwd(self, p):
        print self.curdir
            
    def do_cd(self, p):
        if len(p) == 0: p = "/"
        if not p.startswith("/"):
            new = self.curdir + p
        else:
            new = p
        if not p.endswith("/"): new = new + "/"
        d = self.volume.ls(new)
        if d != None:
            self.curdir = new
            self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, new)
        else:
            print "%s not found/is not a directory" % new
            
    def get_path(self, p):
        path = p
        if not path.startswith("/"):
            path = self.curdir + path
        return path
   
    def _complete(self, text, line, begidx, endidx):
        filename = text.split("/")[-1]
        dirname = "/".join(text.split("/")[:-1])
        if text.startswith("/"):
            contents = self.volume.ls(dirname)
        else:
            contents = self.volume.ls(self.curdir + dirname)
        if not contents:
            return []
        if dirname != "" and not dirname.endswith("/"):
            dirname += "/"
        res = [dirname + x for x in contents.keys() if x.startswith(filename)]
        return res
    
    #TODO if size==0 check if compressed
    def do_ls(self, p):
        dirDict = self.volume.ls((self.curdir + "/" + p).replace("//","/")) 
        if not dirDict:
            return
        for name in sorted(dirDict.keys()):
            size = ""
            protection_class = ""
            record = dirDict[name]
            if hasattr(record, "fileID"):
                size = sizeof_fmt(record.dataFork.logicalSize)
                cprotect = self.volume.getXattr(record.fileID, "com.apple.system.cprotect")
                if cprotect:
                    protection_class = PROTECTION_CLASSES[struct.unpack("<L", cprotect[8:12])[0]]
            print "%s\t%s\t%s\t%s" % (name[:30].ljust(30), size.ljust(10), hfs_date(record.createDate), protection_class)
    
    def do_undelete(self, p):
        if not self.data.keybag.unlocked:
            print "Warning, keybag is not unlocked, some files will be inaccessible"
        if not self.carver:
            self.carver = NANDCarver(self.data, self.image)
        if False:#len(p):
            z =  self.volume.catalogTree.getLBAsHax()
            v = self.volume.getFileRecordForPath(self.curdir)
            folderId = v.folderID
            f = lambda k,v: k.parentID == folderId
        else:
            z = None
            f = None
        self.carver.carveDeletedFiles_fast(z, f)
        #self.carver.carveDeleteFiles_slow(z, f)
    
    def do_xattr(self, p):
        xattr = self.volume.listXattrs(self.get_path(p))
        if not xattr:
            return
        for name, value in xattr.items():
            print name, value.encode("hex")

    def do_protected_files(self, p):
        self.data.list_protected_files()
        
    def do_cprotect(self, p):
        id = self.volume.getFileIDByPath(self.get_path(p))
        if not id:
            return
        
        cprotect = self.volume.getXattr(id, "com.apple.system.cprotect")
        if not cprotect:
            return
        cp = cprotect_xattr.parse(cprotect)
        print cp
        print "Protection class %d => %s" % (cp.persistent_class, PROTECTION_CLASSES.get(cp.persistent_class))
        if not cp.persistent_key:
            return
        fk = self.volume.getFileKeyForCprotect(cprotect)
        if fk:
            print "Unwrapped file key : %s" % fk.encode("hex")
        else:
            print "Cannot decrypt file key"


    def do_open(self, p):
        path = self.get_path(p)
        if self.volume.readFile(path):
            os.startfile(os.path.basename(path))

    def do_xxd(self, p):
        t = p.split()
        path = self.get_path(t[0])
        data = self.volume.readFile(path, returnString=True)
        if not data:
            return
        if len(t) > 1:
            hexdump(data[:int(t[1])])
        else:
            hexdump(data)
    
    def do_effaceable(self, p):
        print "Effaceable Lockers"
        for k,v in self.image.lockers.lockers.items():
            print "%s: %s" % (k, v.encode("hex"))
    
    def do_BAG1(self, p):
        print "BAG1 locker from effaceable storage"
        bag1 = self.image.lockers.get("BAG1")
        hexdump(bag1)
        print "IV:", bag1[4:20].encode("hex")
        print "Key:", bag1[20:].encode("hex")
    
    def do_keybag(self, p):
        self.data.keybag.printClassKeys()
        
    def do_plist(self, p):
        d = None
        data = self.volume.readFile(self.get_path(p), returnString=True)
        if data:
            d = parsePlist(data)
            pprint(d)
        else:
            try:
                d = readPlist(p)
                if d: pprint(d)
            except:
                pass
        if d and d.has_key("_MKBIV"):
            print "=>System keybag file"
            print "_MKBPAYLOAD: encrypted"
            print "_MKBIV: %s" % d["_MKBIV"].data.encode("hex")
            print "_MKBWIPEID: 0x%x (%s)" % (d["_MKBWIPEID"], ("%x"%(d["_MKBWIPEID"])).decode("hex"))
    
    def do_bruteforce(self, p):
        if bruteforcePasscode(self.image.device_infos, self.data):
            print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) * "un")
            self.do_save("")
    
    def do_ptable(self, p):
        pt = self.image.getPartitionTable()
        print "Block device partition table"
        print "".join(map(lambda x:x.ljust(12), ["Index", "Name", "Start LBA", "End LBA", "Size"]))
        for i in xrange(len(pt)):
            p = pt[i]
            print "".join(map(lambda x:str(x).ljust(12), [i, p.name, p.first_lba, p.last_lba, sizeof_fmt((p.last_lba - p.first_lba)*self.image.pageSize)])) 

    def do_nand_dump(self, p):
        if len(p)==0:
            print "Usage: nand_dump my_nand.bin"
            return
        self.image.dump(p)

    def do_dd(self, p):
        if len(p)==0:
            print "Usage: dd output_file.dmg"
            return
        self.volume.bdev.dumpToFile(p.split()[0])
        
    def do_img3(self, p):
        self.image.extract_img3s()
    
    def do_shsh(self, p):
        self.image.extract_shsh()

    def do_debug(self,p):
        from IPython.Shell import IPShellEmbed
        ipshell = IPShellEmbed()
        ipshell(local_ns=locals())
class ExaminerShell(Cmd):
    def __init__(self, image, completekey='tab', stdin=None, stdout=None):
        Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
        self.curdir = "/"
        self.rdisk = None
        if image.filename == "remote":
            self.rdisk = RamdiskToolClient.get()
        self.device_infos = image.device_infos
        self.complete_open = self._complete
        self.complete_xattr = self._complete
        self.complete_cprotect = self._complete
        self.complete_ls = self._complete
        self.complete_cd = self._complete
        self.complete_plist = self._complete
        self.complete_xxd = self._complete
        self.image = image
        self.system = image.getPartitionVolume(0)
        self.data = image.getPartitionVolume(1)
        self.volume = None
        self.volname = ""
        grab_system_version(self.system, self.device_infos)
        print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) *
                                          "un")
        self.deviceName = get_device_name(self.data)
        self.do_data("")
        self.savepath = os.path.join(os.path.dirname(image.filename),
                                     "%s.plist" % self.device_infos.udid[:10])
        #if image.iosVersion > 3 and not image.device_infos.has_key("passcode"):
        #    print "No passcode found in plist file, bruteforce required to access protected data"

        self.carver = None

    def set_partition(self, name, vol):
        self.volume = vol
        self.do_cd("/")
        self.volname = name
        self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname,
                                       self.curdir)

    def do_info(self, p):
        pprint(self.device_infos)

    def do_save(self, p):
        print "Save device information plist to [%s]:" % self.savepath,
        path2 = raw_input()
        if path2: self.savepath = path2
        if os.path.exists(self.savepath):
            print "File already exists, overwrite ? [y/n]:",
            if raw_input() != "y":
                return
        plistlib.writePlist(self.device_infos, self.savepath)

    def do_system(self, p):
        self.set_partition("system", self.system)

    def do_data(self, p):
        self.set_partition("data", self.data)

    def do_pix(self, p):
        self.do_data("")
        self.do_cd("/mobile/Media/DCIM/100APPLE")

    def do_keychain(self, p):
        self.data.readFile("/Keychains/keychain-2.db")
        keychain = keychain_load(
            "keychain-2.db", self.data.keybag,
            self.image.device_infos["key835"].decode("hex"))
        keychain.print_all(False)

    def do_keychain_cert(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load(
            "keychain-2.db", self.data.keybag,
            self.image.device_infos["key835"].decode("hex"))
        keychain.cert(id, filename)

    def do_keychain_key(self, p):
        t = p.split()
        id = int(t[0])
        if len(t) == 2: filename = t[1]
        else: filename = ""
        keychain = keychain_load(
            "keychain-2.db", self.data.keybag,
            self.image.device_infos["key835"].decode("hex"))
        keychain.key(id, filename)

    def do_exit(self, p):
        return True

    def do_quit(self, p):
        return self.do_exit(p)

    def do_reboot(self, p):
        if not self.rdisk:
            self.rdisk = RamdiskToolClient.get()
        self.rdisk.reboot()
        return self.do_exit(p)

    def do_pwd(self, p):
        print self.curdir

    def do_cd(self, p):
        if len(p) == 0: p = "/"
        if not p.startswith("/"):
            new = self.curdir + p
        else:
            new = p
        if not p.endswith("/"): new = new + "/"
        d = self.volume.ls(new)
        if d != None:
            self.curdir = new
            self.prompt = "(%s-%s) %s " % (self.deviceName, self.volname, new)
        else:
            print "%s not found/is not a directory" % new

    def get_path(self, p):
        path = p
        if not path.startswith("/"):
            path = self.curdir + path
        return path

    def _complete(self, text, line, begidx, endidx):
        filename = text.split("/")[-1]
        dirname = "/".join(text.split("/")[:-1])
        if text.startswith("/"):
            contents = self.volume.ls(dirname)
        else:
            contents = self.volume.ls(self.curdir + dirname)
        if not contents:
            return []
        if dirname != "" and not dirname.endswith("/"):
            dirname += "/"
        res = [dirname + x for x in contents.keys() if x.startswith(filename)]
        return res

    #TODO if size==0 check if compressed
    def do_ls(self, p):
        dirDict = self.volume.ls((self.curdir + "/" + p).replace("//", "/"))
        if not dirDict:
            return
        for name in sorted(dirDict.keys()):
            size = ""
            protection_class = ""
            record = dirDict[name]
            if hasattr(record, "fileID"):
                size = sizeof_fmt(record.dataFork.logicalSize)
                cprotect = self.volume.getXattr(record.fileID,
                                                "com.apple.system.cprotect")
                if cprotect:
                    protection_class = PROTECTION_CLASSES[struct.unpack(
                        "<L", cprotect[8:12])[0]]
            print "%s\t%s\t%s\t%s" % (name[:30].ljust(30), size.ljust(10),
                                      hfs_date(
                                          record.createDate), protection_class)

    def do_undelete(self, p):
        if not self.data.keybag.unlocked:
            print "Warning, keybag is not unlocked, some files will be inaccessible"
        if not self.carver:
            self.carver = NANDCarver(self.data, self.image)
        if False:  #len(p):
            z = self.volume.catalogTree.getLBAsHax()
            v = self.volume.getFileRecordForPath(self.curdir)
            folderId = v.folderID
            f = lambda k, v: k.parentID == folderId
        else:
            z = None
            f = None
        self.carver.carveDeletedFiles_fast(z, f)
        #self.carver.carveDeleteFiles_slow(z, f)

    def do_xattr(self, p):
        xattr = self.volume.listXattrs(self.get_path(p))
        if not xattr:
            return
        for name, value in xattr.items():
            print name, value.encode("hex")

    def do_protected_files(self, p):
        self.data.list_protected_files()

    def do_cprotect(self, p):
        id = self.volume.getFileIDByPath(self.get_path(p))
        if not id:
            return

        cprotect = self.volume.getXattr(id, "com.apple.system.cprotect")
        if not cprotect:
            return
        cp = cprotect_xattr.parse(cprotect)
        print cp
        print "Protection class %d => %s" % (
            cp.persistent_class, PROTECTION_CLASSES.get(cp.persistent_class))
        if not cp.persistent_key:
            return
        fk = self.volume.getFileKeyForCprotect(cprotect)
        if fk:
            print "Unwrapped file key : %s" % fk.encode("hex")
        else:
            print "Cannot decrypt file key"

    def do_open(self, p):
        path = self.get_path(p)
        if self.volume.readFile(path):
            os.startfile(os.path.basename(path))

    def do_xxd(self, p):
        t = p.split()
        path = self.get_path(t[0])
        data = self.volume.readFile(path, returnString=True)
        if not data:
            return
        if len(t) > 1:
            hexdump(data[:int(t[1])])
        else:
            hexdump(data)

    def do_effaceable(self, p):
        print "Effaceable Lockers"
        for k, v in self.image.lockers.lockers.items():
            print "%s: %s" % (k, v.encode("hex"))

    def do_BAG1(self, p):
        print "BAG1 locker from effaceable storage"
        bag1 = self.image.lockers.get("BAG1")
        hexdump(bag1)
        print "IV:", bag1[4:20].encode("hex")
        print "Key:", bag1[20:].encode("hex")

    def do_keybag(self, p):
        self.data.keybag.printClassKeys()

    def do_plist(self, p):
        d = None
        data = self.volume.readFile(self.get_path(p), returnString=True)
        if data:
            d = parsePlist(data)
            pprint(d)
        else:
            try:
                d = readPlist(p)
                if d: pprint(d)
            except:
                pass
        if d and d.has_key("_MKBIV"):
            print "=>System keybag file"
            print "_MKBPAYLOAD: encrypted"
            print "_MKBIV: %s" % d["_MKBIV"].data.encode("hex")
            print "_MKBWIPEID: 0x%x (%s)" % (d["_MKBWIPEID"],
                                             ("%x" %
                                              (d["_MKBWIPEID"])).decode("hex"))

    def do_bruteforce(self, p):
        if bruteforcePasscode(self.image.device_infos, self.data):
            print "Keybag state: %slocked" % (int(self.data.keybag.unlocked) *
                                              "un")
            self.do_save("")

    def do_ptable(self, p):
        pt = self.image.getPartitionTable()
        print "Block device partition table"
        print "".join(
            map(lambda x: x.ljust(12),
                ["Index", "Name", "Start LBA", "End LBA", "Size"]))
        for i in xrange(len(pt)):
            p = pt[i]
            print "".join(
                map(lambda x: str(x).ljust(12), [
                    i, p.name, p.first_lba, p.last_lba,
                    sizeof_fmt(
                        (p.last_lba - p.first_lba) * self.image.pageSize)
                ]))

    def do_nand_dump(self, p):
        if len(p) == 0:
            print "Usage: nand_dump my_nand.bin"
            return
        self.image.dump(p)

    def do_dd(self, p):
        if len(p) == 0:
            print "Usage: dd output_file.dmg"
            return
        self.volume.bdev.dumpToFile(p.split()[0])

    def do_img3(self, p):
        self.image.extract_img3s()

    def do_shsh(self, p):
        self.image.extract_shsh()

    def do_debug(self, p):
        from IPython.Shell import IPShellEmbed
        ipshell = IPShellEmbed()
        ipshell(local_ns=locals())