def create_info_plist(self): root_node = self.lockdown.allValues info = {"BuildVersion": root_node.get("BuildVersion") or "", "DeviceName": root_node.get("DeviceName") or "", "Display Name": root_node.get("DeviceName") or "", "GUID": "---", "ProductType": root_node.get("ProductType") or "", "ProductVersion": root_node.get("ProductVersion") or "", "Serial Number": root_node.get("SerialNumber") or "", "Unique Identifier": self.udid.upper(), "Target Identifier": self.udid, "Target Type": "Device", "iTunes Version": "10.0.1" } info["ICCID"] = root_node.get("IntegratedCircuitCardIdentity") or "" info["IMEI"] = root_node.get("InternationalMobileEquipmentIdentity") or "" info["Last Backup Date"] = datetime.datetime.now() afc = AFCClient(self.lockdown) iTunesFilesDict = {} iTunesFiles = afc.read_directory("/iTunes_Control/iTunes/") for i in iTunesFiles: data = afc.get_file_contents("/iTunes_Control/iTunes/" + i) if data: iTunesFilesDict[i] = plistlib.Data(data) info["iTunesFiles"] = iTunesFilesDict iBooksData2 = afc.get_file_contents("/Books/iBooksData2.plist") if iBooksData2: info["iBooks Data 2"] = plistlib.Data(iBooksData2) info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes") self.logger.info("Creating: %s", os.path.join(self.udid,"Info.plist")) self.write_file(os.path.join(self.udid,"Info.plist"), plistlib.writePlistToString(info))
def start(self): self.udid = lockdown.getValue("", "UniqueDeviceID") self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt") self.escrowBag = lockdown.getValue('', 'EscrowBag') # We need this to create lock files self.afc = AFCClient(self.lockdown) self.service = self.lockdown.startServiceWithEscrowBag( "com.apple.mobilebackup2", self.escrowBag) if not self.service: raise Exception( "MobileBackup2 init error : Could not start com.apple.mobilebackup2" ) if not os.path.isdir(self.backupPath): os.makedirs(self.backupPath, 0o0755) self.logger.info( "Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath) DLMessageVersionExchange = self.service.recvPlist() version_major = DLMessageVersionExchange[1] self.service.sendPlist( ["DLMessageVersionExchange", "DLVersionsOk", version_major]) DLMessageDeviceReady = self.service.recvPlist() if DLMessageDeviceReady and DLMessageDeviceReady[ 0] == "DLMessageDeviceReady": res = self.version_exchange() protocol_version = res.get('ProtocolVersion') self.logger.info("Negotiated Protocol Version %s", protocol_version[1]) else: raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady)
def install(self, ipa_path, options=None, handler=None, *args): '''安装应用程序 :param ipa_path: 安装包的路径 :type ipa_path: str :return: boolean - 安装是否成功 ''' print "上传安装包..." afc_client = AFCClient(self.lockdown) tmp_ipa = "t%d.ipa" % time.time() with open(ipa_path, "rb") as f: ipa_content = f.read() afc_client.set_file_contents("/" + tmp_ipa, ipa_content) print "上传完毕" print "开始安装" cmd = {"Command": "Install", "PackagePath": tmp_ipa} if options: cmd.update(options) self.lockdown = LockdownClient(self.udid) self.service = self.lockdown.startService( "com.apple.mobile.installation_proxy") self.service.sendPlist(cmd) ret = self.wait_completion(handler, args) if ret[0]: print "安装成功" else: print "安装失败:%s" % ret[1] return ret
def install_or_upgrade(self, ipaPath, cmd="Install", options={}, handler=None, *args): afc = AFCClient(self.lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath,"rb").read()) cmd = { "Command": cmd, "ClientOptions": options, "PackagePath": os.path.basename(ipaPath)} self.service.sendPlist(cmd) self.watch_completion(handler, args)
def install_or_upgrade(self, ipaPath, cmd="Install", options=None, handler=None, *args): afc = AFCClient(self.lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath,"rb").read()) cmd = {"Command":cmd, "PackagePath": os.path.basename(ipaPath)} if options: cmd.update(options) self.service.sendPlist(cmd) # print "%s : " % (cmd, bundleID) print "%s : %s\n" % (cmd, self.watch_completion(handler, args))
def install_ipa(uuid, ipa_path): """ docstring for install_ipa """ from pymobiledevice.afc import AFCClient lockdown, service = get_lockdown_and_service(uuid) afc = AFCClient(lockdown=lockdown) afc.set_file_contents(path.basename(ipa_path), open(ipa_path, "rb").read()) cmd = {"Command": "Install", "PackagePath": path.basename(ipa_path)} return run_command(service, uuid, cmd)
def install_or_upgrade(self, ipaPath, cmd="Install", options=None, handler=None, *args): afc = AFCClient(self.lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath, "rb").read()) cmd = {"Command": cmd, "PackagePath": os.path.basename(ipaPath)} if options: cmd.update(options) self.service.sendPlist(cmd) # print "%s : " % (cmd, bundleID) print "%s : %s\n" % (cmd, self.watch_completion(handler, args))
def start(self): self.udid = lockdown.getValue("", "UniqueDeviceID") self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt") self.escrowBag = lockdown.getValue('', 'EscrowBag') self.afc = AFCClient(self.lockdown) #We need this to create lock files self.service = self.lockdown.startService("com.apple.mobilebackup2") #self.service = self.lockdown.startServiceWithEscrowBag("com.apple.mobilebackup2", self.escrowBag) if not self.service: raise Exception("MobileBackup2 init error : Could not start com.apple.mobilebackup2") if not os.path.isdir(self.backupPath): os.makedirs(self.backupPath,0o0755) self.logger.info("Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath) DLMessageVersionExchange = self.service.recvPlist() version_major = DLMessageVersionExchange[1] self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major]) DLMessageDeviceReady = self.service.recvPlist() if DLMessageDeviceReady and DLMessageDeviceReady[0] == "DLMessageDeviceReady": res = self.version_exchange() protocol_version = res.get('ProtocolVersion') self.logger.info("Negotiated Protocol Version %s", protocol_version) else: raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady)
def test_get_crash_log(self): mux = USBMux() if not mux.devices: mux.process(0.1) if len(mux.devices) == 0: print("no real device found") self.no_device = True return udid = mux.devices[0].serial procname = "QQ" lockdown = LockdownClient(udid) self.service = lockdown.startService("com.apple.crashreportcopymobile") client = AFCClient(lockdown, service=self.service) afc_shell = AFCShell(client=client) remote_crash_path = '/' dest_path = '/tmp' local_crashes = [] print('udid:', udid) for _dirname, _dirs, files in afc_shell.afc.dir_walk( remote_crash_path): for filename in files: if procname in filename: remote_crash_file = os.path.join(remote_crash_path, filename) data = afc_shell.afc.get_file_contents(remote_crash_file) local_crash_file = os.path.join(dest_path, filename) local_crashes.append(local_crash_file) with open(local_crash_file, 'wb') as fp: fp.write(data) print(local_crashes)
def test_install_app(self): if self.no_device: return ipa_path = os.path.join(os.path.expanduser("~"), "Downloads/app/DemoApp.ipa") tmp_ipa = "/t%d.ipa" % time.time() with open(ipa_path, "rb") as f: ipa_content = f.read() afc = AFCClient(self.lockdownclient) afc.set_file_contents(tmp_ipa, ipa_content) print("Upload completed") print("Starting installation") cmd = {"Command":"Install", "PackagePath": tmp_ipa} self.lockdownclient = LockdownClient(self.udid) self.service = self.lockdownclient.startService("com.apple.mobile.installation_proxy") self.service.sendPlist(cmd) result, err = self.wait_completion() self.assertTrue(result, 'install_app failed: %s' % err)
def install_or_upgrade(self, ipaPath, cmd="Install", options={}, handler=None, *args): afc = AFCClient(self.lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath, "rb").read()) cmd = { "Command": cmd, "ClientOptions": options, "PackagePath": os.path.basename(ipaPath) } self.service.sendPlist(cmd) self.watch_completion(handler, args)
def install(self, ipa_path, options=None, handler=None, *args): '''安装应用程序 :param ipa_path: 安装包的路径 :type ipa_path: str :return: boolean - 安装是否成功 ''' print "上传安装包..." afc_client = AFCClient() tmp_ipa = "t%d.ipa" % time.time() with open(ipa_path, "rb") as f: ipa_content = f.read() afc_client.set_file_contents("/" + tmp_ipa, ipa_content) print "上传完毕" print "开始安装" cmd = {"Command": "Install", "PackagePath": tmp_ipa} if options: cmd.update(options) self.service.sendPlist(cmd) return self.wait_completion(handler, args)
def mobile_install(lockdown, ipaPath): # Start afc service & upload ipa afc = AFCClient(lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath, 'rb').read()) mci = lockdown.startService("com.apple.mobile.installation_proxy") #print mci.sendPlist({"Command":"Archive","ApplicationIdentifier": "com.joystickgenerals.STActionPig"}) mci.sendPlist({"Command": "Install", # "ApplicationIdentifier": "com.gotohack.JBChecker", "PackagePath": os.path.basename(ipaPath)}) while True: z = mci.recvPlist() if not z: break completion = z.get('PercentComplete') if completion: print('Installing, %s: %s %% Complete' % ( ipaPath, z['PercentComplete'])) if z.get('Status') == 'Complete': print("Installation %s\n" % z['Status']) break
def mobile_install(lockdown, ipaPath): # Start afc service & upload ipa afc = AFCClient(lockdown) afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath, 'rb').read()) mci = lockdown.startService("com.apple.mobile.installation_proxy") # print mci.sendPlist({"Command":"Archive","ApplicationIdentifier": # "com.joystickgenerals.STActionPig"}) mci.sendPlist({"Command": "Install", #"ApplicationIdentifier": "com.gotohack.JBChecker", "PackagePath": os.path.basename(ipaPath)}) while True: z = mci.recvPlist() if not z: break completion = z.get('PercentComplete') if completion: print('Installing, %s: %s %% Complete' % (ipaPath, z['PercentComplete'])) if z.get('Status') == 'Complete': print("Installation %s\n" % z['Status']) break
def create_info_plist(self): root_node = self.lockdown.allValues # print pprint(root_node) info = {"BuildVersion": root_node.get("BuildVersion") or "", "DeviceName": root_node.get("DeviceName") or "", "Display Name": root_node.get("DeviceName") or "", "GUID": "---", "ProductType": root_node.get("ProductType") or "", "ProductVersion": root_node.get("ProductVersion") or "", "Serial Number": root_node.get("SerialNumber") or "", "Unique Identifier": self.udid.upper(), "Target Identifier": self.udid, "Target Type": "Device", "iTunes Version": "10.0.1" } info["ICCID"] = root_node.get("IntegratedCircuitCardIdentity") or "" info["IMEI"] = root_node.get( "InternationalMobileEquipmentIdentity") or "" info["Last Backup Date"] = datetime.datetime.now() afc = AFCClient(self.lockdown) iTunesFilesDict = {} iTunesFiles = afc.read_directory("/iTunes_Control/iTunes/") for i in iTunesFiles: data = afc.get_file_contents("/iTunes_Control/iTunes/" + i) if data: iTunesFilesDict[i] = plistlib.Data(data) info["iTunesFiles"] = iTunesFilesDict iBooksData2 = afc.get_file_contents("/Books/iBooksData2.plist") if iBooksData2: info["iBooks Data 2"] = plistlib.Data(iBooksData2) info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes") print("Creating %s" % os.path.join(self.udid, "Info.plist")) self.write_file(os.path.join(self.udid, "Info.plist"), plistlib.dumps(info))
def setUp(self): mux = USBMux() if not mux.devices: mux.process(0.1) if len(mux.devices) == 0: print("no real device found") self.no_device = True return udid = mux.devices[0].serial lockdown_client = LockdownClient(udid) self.service = lockdown_client.startService( "com.apple.mobile.house_arrest") self.service.sendPlist({ "Command": "VendContainer", "Identifier": "com.gotohack.testapp" }) status = self.service.recvPlist() if 'Error' in status and status['Error'] == "ApplicationLookupFailed": raise RuntimeWarning('ApplicationLookupFailed') if 'Status' in status and status['Status'] != 'Complete': raise RuntimeWarning('House arrest service launch failed') self.afc = AFCClient(lockdown_client, service=self.service) self.afc_shell = AFCShell(client=self.afc)
def house_arrest(lockdown, applicationId): try: mis = lockdown.startService("com.apple.mobile.house_arrest") except: lockdown = LockdownClient() mis = lockdown.startService("com.apple.mobile.house_arrest") if mis == None: return mis.sendPlist({"Command": "VendDocuments", "Identifier": applicationId}) res = mis.recvPlist() if res.get("Error"): print("Unable to Lookup the selected application: You probably trying to access to a system app...") return None return AFCClient(lockdown, service=mis)
class HouseArrestTest(unittest.TestCase): def setUp(self): mux = USBMux() if not mux.devices: mux.process(0.1) if len(mux.devices) == 0: print("no real device found") self.no_device = True return udid = mux.devices[0].serial lockdown_client = LockdownClient(udid) self.service = lockdown_client.startService( "com.apple.mobile.house_arrest") self.service.sendPlist({ "Command": "VendContainer", "Identifier": "com.gotohack.testapp" }) status = self.service.recvPlist() if 'Error' in status and status['Error'] == "ApplicationLookupFailed": raise RuntimeWarning('ApplicationLookupFailed') if 'Status' in status and status['Status'] != 'Complete': raise RuntimeWarning('House arrest service launch failed') self.afc = AFCClient(lockdown_client, service=self.service) self.afc_shell = AFCShell(client=self.afc) def test_list_files_in_sandbox(self): if self.no_device: return sandbox_tree = [] file_path = '/Documents' for l in self.afc.read_directory(file_path): if l not in ('.', '..'): tmp_dict = {} tmp_dict['path'] = os.path.join(file_path, l) info = self.afc.get_file_info(tmp_dict['path']) tmp_dict['is_dir'] = (info is not None and info['st_ifmt'] == 'S_IFDIR') sandbox_tree.append(tmp_dict) pprint.pprint(sandbox_tree) def test_push_file_to_sandbox(self): if self.no_device: return data = b"hello sandbox!" self.afc.set_file_contents('/Documents/test.log', data) def test_pull_file_from_sandbox(self): if self.no_device: return data = b"hello sandbox!" content = self.afc.get_file_contents('/Documents/test.log') print(content) def test_remove_file_in_sandbox(self): if self.no_device: return self.afc_shell.do_rm('/Documents/test.log') def tearDown(self): if not self.no_device and self.service: self.service.close()
class MobileBackup2(MobileBackup): service = None def __init__(self, lockdown = None,backupPath = None, password="", udid=None, logger=None): self.logger = logger or logging.getLogger(__name__) self.backupPath = backupPath if backupPath else "backups" self.password = password self.lockdown = lockdown if lockdown else LockdownClient(udid=udid) if not self.lockdown: raise Exception("Unable to start lockdown") ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion and int(ProductVersion[:ProductVersion.find('.')]) < 5: raise DeviceVersionNotSupported self.start() def start(self): self.udid = lockdown.getValue("", "UniqueDeviceID") self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt") self.escrowBag = lockdown.getValue('', 'EscrowBag') self.afc = AFCClient(self.lockdown) #We need this to create lock files self.service = self.lockdown.startService("com.apple.mobilebackup2") #self.service = self.lockdown.startServiceWithEscrowBag("com.apple.mobilebackup2", self.escrowBag) if not self.service: raise Exception("MobileBackup2 init error : Could not start com.apple.mobilebackup2") if not os.path.isdir(self.backupPath): os.makedirs(self.backupPath,0o0755) self.logger.info("Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath) DLMessageVersionExchange = self.service.recvPlist() version_major = DLMessageVersionExchange[1] self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major]) DLMessageDeviceReady = self.service.recvPlist() if DLMessageDeviceReady and DLMessageDeviceReady[0] == "DLMessageDeviceReady": res = self.version_exchange() protocol_version = res.get('ProtocolVersion') self.logger.info("Negotiated Protocol Version %s", protocol_version) else: raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady) def __del__(self): if self.service: self.service.sendPlist(["DLMessageDisconnect", "___EmptyParameterString___"]) def internal_mobilebackup2_send_message(self, name, data): data["MessageName"] = name self.device_link_service_send_process_message(data) def internal_mobilebackup2_receive_message(self, name=None): res = self.device_link_service_receive_process_message() if res: if name and res["MessageName"] != name: self.logger.error("MessageName does not match %s %s", name, str(res)) return res def version_exchange(self): self.internal_mobilebackup2_send_message("Hello", {"SupportedProtocolVersions": [2.0,2.1]}) return self.internal_mobilebackup2_receive_message("Response") def mobilebackup2_send_request(self, request, target, source, options={}): d = {"TargetIdentifier": target, "SourceIdentifier": source, "Options": options} self.internal_mobilebackup2_send_message(request, d) def mobilebackup2_receive_message(self): return self.service.recvPlist() def mobilebackup2_send_status_response(self, status_code, status1="___EmptyParameterString___", status2={}): a = ["DLMessageStatusResponse", status_code, status1, status2] self.service.sendPlist(a) def mb2_handle_free_disk_space(self,msg): s = os.statvfs(self.backupPath) freeSpace = s.f_bsize * s.f_bavail res = ["DLMessageStatusResponse", 0, "___EmptyParameterString___", freeSpace] self.service.sendPlist(res) def mb2_multi_status_add_file_error(self, errplist, path, error_code, error_message): errplist[path] = {"DLFileErrorCode": error_code, "DLFileErrorString": error_message} def mb2_handle_copy_item(self, msg): src = self.check_filename(msg[1]) dst = self.check_filename(msg[2]) if os.path.isfile(src): data = self.read_file(src) self.write_file(dst, data) else: os.makedirs(dst) self.mobilebackup2_send_status_response(0) def mb2_handle_send_file(self, filename, errplist): self.service.send_raw(filename) if not filename.startswith(self.udid): filename = self.udid + "/" + filename data = self.read_file(self.check_filename(filename)) if data != None: self.logger.info("Sending %s to device", filename) if PY3: # FIXME msg = b"".join([(CODE_FILE_DATA).to_bytes(1,'little'), data]) else: msg = chr(CODE_FILE_DATA) + data self.service.send_raw(msg) self.service.send_raw(chr(CODE_SUCCESS)) else: self.logger.warn("File %s requested from device not found", filename) self.service.send_raw(chr(CODE_ERROR_LOCAL)) self.mb2_multi_status_add_file_error(errplist, filename, ERROR_ENOENT, "Could not find the droid you were looking for ;)") def mb2_handle_send_files(self, msg): errplist = {} for f in msg[1]: self.mb2_handle_send_file(f, errplist) msg = "\x00\x00\x00\x00" if PY3: msg = b"\x00\x00\x00\x00" self.service.send(msg) if len(errplist): self.mobilebackup2_send_status_response(-13, "Multi status", errplist) else: self.mobilebackup2_send_status_response(0) def mb2_handle_list_directory(self, msg): path = msg[1] self.logger.info("List directory: %s" % path) dirlist = {} if path.find("../") != -1: raise Exception("HAX, sneaky dots in path %s" % name) for root, dirs, files in os.walk(os.path.join(self.backupPath, path)): for fname in files: fpath = os.path.join(root, fname) finfo = {} st = os.stat(fpath) ftype = "DLFileTypeUnknown" if S_ISDIR(st.st_mode): ftype = "DLFileTypeDirectory" elif S_ISREG(st.st_mode): ftype = "DLFileTypeRegular" finfo["DLFileType"] = ftype finfo["DLFileSize"] = st.st_size finfo["DLFileModificationDate"] = st.st_mtime dirlist[fname] = finfo self.mobilebackup2_send_status_response(0, status2=dirlist); def mb2_handle_make_directory(self, msg): dirname = self.check_filename(msg[1]) self.logger.info("Creating directory %s", dirname) if not os.path.isdir(dirname): os.makedirs(dirname) self.mobilebackup2_send_status_response(0, "") def mb2_handle_receive_files(self, msg): done = 0 while not done: device_filename = self.service.recv_raw() if device_filename == "": break backup_filename = self.service.recv_raw() self.logger.debug("Downloading: %s to %s", device_filename, backup_filename) filedata = "" if PY3: filedata = bytearray(b"") last_code = 0x00 while True: stuff = self.service.recv_raw() if PY3: code = stuff[0] else: code = ord(stuff[0]) if code == CODE_FILE_DATA: filedata = stuff[1:] elif code == CODE_SUCCESS: self.write_file(self.check_filename(backup_filename), filedata) break elif code == CODE_ERROR_REMOTE: if last_code != CODE_FILE_DATA: self.logger.warn("Received an error message from device: %s for:\n\t%s\n\t[%s]", ord(code), device_filename, backup_filename) else: self.logger.warn("Unknown code: %s for:\n\t%s\n\t[%s]", code, device_filename, backup_filename) self.logger.warn(msg) #break last_code = code self.mobilebackup2_send_status_response(0) def mb2_handle_move_files(self, msg): self.logger.info("Moving %d files", len(msg[1]) ) for k,v in msg[1].items(): self.logger.info("Renaming:\n\t%s \n\tto %s", self.check_filename(k), self.check_filename(v)) os.rename(self.check_filename(k),self.check_filename(v)) self.mobilebackup2_send_status_response(0) def mb2_handle_remove_files(self, msg): self.logger.info("Removing %d files", len(msg[1]) ) for filename in msg[1]: self.logger.info("Removing %s", self.check_filename(filename)) try: filename = self.check_filename(filename) if os.path.isfile(filename): os.unlink(filename) except Exception as e: self.logger.error(e) self.mobilebackup2_send_status_response(0) def work_loop(self): while True: msg = self.mobilebackup2_receive_message() if not msg: break assert(msg[0] in ["DLMessageDownloadFiles", "DLContentsOfDirectory", "DLMessageCreateDirectory", "DLMessageUploadFiles", "DLMessageMoveFiles","DLMessageMoveItems", "DLMessageRemoveFiles", "DLMessageRemoveItems", "DLMessageCopyItem", "DLMessageProcessMessage", "DLMessageGetFreeDiskSpace", "DLMessageDisconnect"]) if msg[0] == "DLMessageDownloadFiles": self.mb2_handle_send_files(msg) elif msg[0] == "DLContentsOfDirectory": self.mb2_handle_list_directory(msg) elif msg[0] == "DLMessageCreateDirectory": self.mb2_handle_make_directory(msg) elif msg[0] == "DLMessageUploadFiles": self.mb2_handle_receive_files(msg) elif msg[0] in ["DLMessageMoveFiles","DLMessageMoveItems"]: self.mb2_handle_move_files(msg) elif msg[0] in ["DLMessageRemoveFiles", "DLMessageRemoveItems"]: self.mb2_handle_remove_files(msg) elif msg[0] == "DLMessageCopyItem": self.mb2_handle_copy_item(msg) elif msg[0] == "DLMessageProcessMessage": errcode = msg[1].get("ErrorCode") if errcode == 0: m = msg[1].get("MessageName") if m != "Response": self.logger.warn(m) break if errcode == 1: self.logger.info("Please unlock your device and retry...") raise Exception("Please unlock your device and retry...") if errcode == 211: self.logger.info("Please go to Settings->iClould->Find My iPhone and disable it") raise Exception('Please go to Settings->iClould->Find My iPhone and disable it') if errcode == 105: self.logger.info("Not enough free space on device for restore") raise Exception('Not enough free space on device for restore') if errcode == 17: self.logger.info("please press 'trust this computer' in your device") raise Exception('please press \'trust this computer\' in your device') if errcode == 102: self.logger.info("Please reboot your device and try again") raise Exception('Please reboot your device and try again') self.logger.error("Unknown error: %d : %s", errcode, msg[1].get("ErrorDescription", "")) raise Exception('Unknown error ' + str(errcode) + msg[1].get("ErrorDescription", "")) elif msg[0] == "DLMessageGetFreeDiskSpace": self.mb2_handle_free_disk_space(msg) elif msg[0] == "DLMessageDisconnect": break def create_status_plist(self,fullBackup=True): #Creating Status file for backup statusDict = { 'UUID': str(uuid4()).upper(), 'BackupState': 'new', 'IsFullBackup': fullBackup, 'Version': '2.4', 'Date': datetime.datetime.fromtimestamp(mktime(gmtime())), 'SnapshotState': 'finished' } writePlist(statusDict,self.check_filename("Status.plist")) # def set_sync_lock(self): # #do_post_notification(device, NP_SYNC_WILL_START); # lockfile = self.afc.file_open("/com.apple.itunes.lock_sync") # if lockfile: # #do_post_notification(device, NP_SYNC_LOCK_REQUEST); # while True: # res = afc_file_lock(afc, lockfile, AFC_LOCK_EX); # if res == AFC_E_SUCCESS: # #do_post_notification(device, NP_SYNC_DID_START); # break # elif res == AFC_E_OP_WOULD_BLOCK): # sleep(0.5) # continue # else: # print "ERROR: could not lock file! error: %d\n" % res # self.afc.file_close(lockfile); # lockfile = 0; # cmd = CMD_LEAVE; # # if i == LOCK_ATTEMPTS: # print "ERROR: timeout while locking for sync" # self.afc.file_close(afc, lockfile) # lockfile = 0 # cmd = CMD_LEAVE # break # # def remove_sync_lock(self): # pass def create_info_plist(self): # Get device information device_info = self.lockdown.allValues # Get a list of installed user applications instpxy = installation_proxy(self.lockdown) apps = instpxy.browse({"ApplicationType":"User"}, ["CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata"]) # Create new info.plits info = {"BuildVersion": device_info.get("BuildVersion") or "", "DeviceName": device_info.get("DeviceName") or "", "Display Name": device_info.get("DeviceName") or "", "GUID": "---", "Product Name" : device_info.get("ProductName" or ""), "ProductType": device_info.get("ProductType") or "", "ProductVersion": device_info.get("ProductVersion") or "", "Serial Number": device_info.get("SerialNumber") or "", "Unique Identifier": self.udid.upper(), "Target Identifier": self.udid, "Target Type": "Device", "iTunes Version": "10.0.1", "MEID" : device_info.get("MobileEquipmentIdentifier") or "", "Phone Number" : device_info.get("PhoneNumber") or "", } info["ICCID"] = device_info.get("IntegratedCircuitCardIdentity") or "" info["IMEI"] = device_info.get("InternationalMobileEquipmentIdentity") or "" info["Last Backup Date"] = datetime.datetime.now() # Starting SpringBoard service to retrieve icons position self.sbs = SBServiceClient(self.lockdown) installed_apps = [] apps_data = {} for app_entry in apps: tmp = {} bid = app_entry.get("CFBundleIdentifier") if bid: installed_apps.append(bid) pngdata = self.sbs.get_icon_pngdata(bid) if pngdata: tmp["PlaceholderIcon"] = pngdata tmp["iTunesMetadata"] = app_entry.get("iTunesMetadata") tmp["ApplicationSINF"] = app_entry.get("ApplicationSINF") apps_data[bid] = tmp info["Applications"] = apps_data info["Installed Applications"] = installed_apps # Handling itunes files iTunesFiles = [ "ApertureAlbumPrefs", "IC-Info.sidb", "IC-Info.sidv", "PhotosFolderAlbums", "PhotosFolderName", "PhotosFolderPrefs", "VoiceMemos.plist", "iPhotoAlbumPrefs", "iTunesApplicationIDs", "iTunesPrefs", "iTunesPrefs.plist" ] iTunesFilesDict = {} for i in iTunesFiles: data = self.afc.get_file_contents("/iTunes_Control/iTunes/" + i) if data: iTunesFilesDict[i] = plistlib.Data(data) info["iTunesFiles"] = iTunesFilesDict iBooksData2 = self.afc.get_file_contents("/Books/iBooksData2.plist") if iBooksData2: info["iBooks Data 2"] = plistlib.Data(iBooksData2) info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes") self.logger.info("Creating %s", os.path.join(self.udid,"Info.plist")) self.write_file(os.path.join(self.udid,"Info.plist"), plistlib.writePlistToString(info)) def backup(self,fullBackup=True): #TODO set_sync_lock self.logger.info("Starting %s backup...", ("Encrypted " if self.willEncrypt else "")) if not os.path.isdir(os.path.join(self.backupPath,self.udid)): os.makedirs(os.path.join(self.backupPath,self.udid)) self.logger.info("Backup mode: %s", "Full backup" if fullBackup else "Incremental backup") self.create_info_plist() options = {"ForceFullBackup": fullBackup} self.mobilebackup2_send_request("Backup", self.udid, options) self.work_loop() def restore(self, options = {"RestoreSystemFiles": True, "RestoreShouldReboot": True, "RestorePreserveCameraRoll": True, "RemoveItemsNotRestored": False, "RestoreDontCopyBackup": True, "RestorePreserveSettings": True}, password=None): self.logger.info("Starting restoration...") m = os.path.join(self.backupPath,self.udid,"Manifest.plist") try: manifest = readPlist(m) except IOError: self.logger.error("not a valid backup folder") return -1 if manifest.get("IsEncrypted"): print("Backup is encrypted, enter password : "******"Password"] = self.password self.mobilebackup2_send_request("Restore", self.udid, self.udid, options) self.work_loop() def info(self, options={}): source_udid = self.udid self.mobilebackup2_send_request("Info", self.udid, source_udid, options) self.work_loop() def list(self, options={}): source_udid = self.udid self.mobilebackup2_send_request("List", self.udid, source_udid, options) self.work_loop() def changepw(self,oldpw,newpw): options = { "OldPassword" : oldpw, "NewPassword" : newpw } self.mobilebackup2_send_request("ChangePassword", self.udid, "", options) self.work_loop() def unback(self, options={"Password": None}): source_udid = self.udid self.mobilebackup2_send_request("Unback", self.udid, source_udid, options) self.work_loop() def enableCloudBackup(self,options={"CloudBackupState": False}): self.mobilebackup2_send_request("EnableCloudBackup", self.udid, options) self.work_loop() def mobilebackup2_notify_cb(notification, data=None): if notification == NP_SYNC_CANCEL_REQUEST: self.logger.info("User has cancelled the backup process on the device.") elif notification == NP_BACKUP_DOMAIN_CHANGED: backup_domain_changed = 1 else: self.logger.info("Unhandled notification '%s'", notification)
class MobileBackup2(MobileBackup): def __init__(self, lockdown=None, backupPath=None, password="", udid=None, logger=None): self.logger = logger or logging.getLogger(__name__) self.backupPath = backupPath if backupPath else "backups" self.password = password self.lockdown = lockdown if lockdown else LockdownClient(udid=udid) if not self.lockdown: raise Exception("Unable to start lockdown") ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion and int( ProductVersion[:ProductVersion.find('.')]) < 5: raise DeviceVersionNotSupported self.start() def start(self): self.udid = lockdown.getValue("", "UniqueDeviceID") self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt") self.escrowBag = lockdown.getValue('', 'EscrowBag') # We need this to create lock files self.afc = AFCClient(self.lockdown) self.service = self.lockdown.startServiceWithEscrowBag( "com.apple.mobilebackup2", self.escrowBag) if not self.service: raise Exception( "MobileBackup2 init error : Could not start com.apple.mobilebackup2" ) if not os.path.isdir(self.backupPath): os.makedirs(self.backupPath, 0o0755) self.logger.info( "Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath) DLMessageVersionExchange = self.service.recvPlist() version_major = DLMessageVersionExchange[1] self.service.sendPlist( ["DLMessageVersionExchange", "DLVersionsOk", version_major]) DLMessageDeviceReady = self.service.recvPlist() if DLMessageDeviceReady and DLMessageDeviceReady[ 0] == "DLMessageDeviceReady": res = self.version_exchange() protocol_version = res.get('ProtocolVersion') self.logger.info("Negotiated Protocol Version %s", protocol_version[1]) else: raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady) def __del__(self): if self.service: self.service.sendPlist( ["DLMessageDisconnect", "___EmptyParameterString___"]) def internal_mobilebackup2_send_message(self, name, data): data["MessageName"] = name self.device_link_service_send_process_message(data) def internal_mobilebackup2_receive_message(self, name=None): res = self.device_link_service_receive_process_message() if res: if name and res["MessageName"] != name: self.logger.error("MessageName does not match %s %s", name, str(res)) return res def version_exchange(self): self.internal_mobilebackup2_send_message( "Hello", {"SupportedProtocolVersions": [2.0, 2.1]}) return self.internal_mobilebackup2_receive_message("Response") def mobilebackup2_send_request(self, request, target, source, options={}): d = { "TargetIdentifier": target, "SourceIdentifier": source, "Options": options } self.internal_mobilebackup2_send_message(request, d) def mobilebackup2_receive_message(self): return self.service.recvPlist() def mobilebackup2_send_status_response( self, status_code, status1="___EmptyParameterString___", status2={}): a = ["DLMessageStatusResponse", status_code, status1, status2] self.service.sendPlist(a) def mb2_handle_free_disk_space(self, msg): s = os.statvfs(self.backupPath) freeSpace = s.f_bsize * s.f_bavail res = [ "DLMessageStatusResponse", 0, "___EmptyParameterString___", freeSpace ] self.service.sendPlist(res) def mb2_multi_status_add_file_error(self, errplist, path, error_code, error_message): errplist[path] = { "DLFileErrorCode": error_code, "DLFileErrorString": error_message } def mb2_handle_copy_item(self, msg): src = self.check_filename(msg[1]) dst = self.check_filename(msg[2]) if os.path.isfile(src): data = self.read_file(src) self.write_file(dst, data) else: os.makedirs(dst) self.mobilebackup2_send_status_response(0) def mb2_handle_send_file(self, filename, errplist): self.service.send_raw(filename) if not filename.startswith(self.udid): filename = self.udid + "/" + filename data = self.read_file(self.check_filename(filename)) if data != None: self.logger.info("Sending %s to device", filename) self.service.send_raw(chr(CODE_FILE_DATA) + data) self.service.send_raw(chr(CODE_SUCCESS)) else: self.logger.warn("File %s requested from device not found", filename) self.service.send_raw(chr(CODE_ERROR_LOCAL)) self.mb2_multi_status_add_file_error( errplist, filename, ERROR_ENOENT, "Could not find the droid you were looking for ;)") def mb2_handle_send_files(self, msg): errplist = {} for f in msg[1]: self.mb2_handle_send_file(f, errplist) self.service.send("\x00\x00\x00\x00") if len(errplist): self.mobilebackup2_send_status_response(-13, "Multi status", errplist) else: self.mobilebackup2_send_status_response(0) def mb2_handle_list_directory(self, msg): path = msg[1] self.logger.info("List directory: %s" % path) dirlist = {} if path.find("../") != -1: raise Exception("HAX, sneaky dots in path %s" % name) for root, dirs, files in os.walk(os.path.join(self.backupPath, path)): for fname in files: fpath = os.path.join(root, fname) finfo = {} st = os.stat(fpath) ftype = "DLFileTypeUnknown" if S_ISDIR(st.st_mode): ftype = "DLFileTypeDirectory" elif S_ISREG(st.st_mode): ftype = "DLFileTypeRegular" finfo["DLFileType"] = ftype finfo["DLFileSize"] = st.st_size finfo["DLFileModificationDate"] = st.st_mtime dirlist[fname] = finfo self.mobilebackup2_send_status_response(0, status2=dirlist) def mb2_handle_make_directory(self, msg): dirname = self.check_filename(msg[1]) self.logger.info("Creating directory %s", dirname) if not os.path.isdir(dirname): os.makedirs(dirname) self.mobilebackup2_send_status_response(0, "") def mb2_handle_receive_files(self, msg): done = 0 while not done: device_filename = self.service.recv_raw() if device_filename == "": break backup_filename = self.service.recv_raw() self.logger.debug("Downloading: %s to %s", device_filename, backup_filename) filedata = "" last_code = 0x00 while True: stuff = self.service.recv_raw() if ord(stuff[0]) == CODE_FILE_DATA: filedata += stuff[1:] elif ord(stuff[0]) == CODE_SUCCESS: self.write_file(self.check_filename(backup_filename), filedata) break elif ord(stuff[0]) == CODE_ERROR_REMOTE: if last_code != CODE_FILE_DATA: self.logger.warn( "Received an error message from device: %s for:\n\t%s\n\t[%s]", ord(stuff[0]), device_filename, backup_filename) else: self.logger.warn("Unknown code: %s for:\n\t%s\n\t[%s]", ord(stuff[0]), device_filename, backup_filename) self.logger.warn(msg) # break last_code = ord(stuff[0]) self.mobilebackup2_send_status_response(0) def mb2_handle_move_files(self, msg): self.logger.info("Moving %d files", len(msg[1])) for k, v in list(msg[1].items()): self.logger.info("Renaming:\n\t%s \n\tto %s", self.check_filename(k), self.check_filename(v)) os.rename(self.check_filename(k), self.check_filename(v)) self.mobilebackup2_send_status_response(0) def mb2_handle_remove_files(self, msg): self.logger.info("Removing %d files", len(msg[1])) for filename in msg[1]: self.logger.info("Removing %s", self.check_filename(filename)) try: filename = self.check_filename(filename) if os.path.isfile(filename): os.unlink(filename) except Exception as e: self.logger.error(e) self.mobilebackup2_send_status_response(0) def work_loop(self): while True: msg = self.mobilebackup2_receive_message() if not msg: break assert (msg[0] in [ "DLMessageDownloadFiles", "DLContentsOfDirectory", "DLMessageCreateDirectory", "DLMessageUploadFiles", "DLMessageMoveFiles", "DLMessageMoveItems", "DLMessageRemoveFiles", "DLMessageRemoveItems", "DLMessageCopyItem", "DLMessageProcessMessage", "DLMessageGetFreeDiskSpace", "DLMessageDisconnect" ]) if msg[0] == "DLMessageDownloadFiles": self.mb2_handle_send_files(msg) elif msg[0] == "DLContentsOfDirectory": self.mb2_handle_list_directory(msg) elif msg[0] == "DLMessageCreateDirectory": self.mb2_handle_make_directory(msg) elif msg[0] == "DLMessageUploadFiles": self.mb2_handle_receive_files(msg) elif msg[0] in ["DLMessageMoveFiles", "DLMessageMoveItems"]: self.mb2_handle_move_files(msg) elif msg[0] in ["DLMessageRemoveFiles", "DLMessageRemoveItems"]: self.mb2_handle_remove_files(msg) elif msg[0] == "DLMessageCopyItem": self.mb2_handle_copy_item(msg) elif msg[0] == "DLMessageProcessMessage": errcode = msg[1].get("ErrorCode") if errcode == 0: m = msg[1].get("MessageName") if m != "Response": self.logger.warn(m) break if errcode == 1: self.logger.info("Please unlock your device and retry...") if errcode == 211: self.logger.info( "Please go to Settings->iClould->Find My iPhone and disable it" ) if errcode == 105: self.logger.info( "Not enough free space on device for restore") if errcode == 17: self.logger.info( "please press 'trust this computer' in your device") if errcode == 102: self.logger.info("Please reboot your device and try again") self.logger.error("Unknown error: %d : %s", errcode, msg[1].get("ErrorDescription", "")) raise Exception(errcode) elif msg[0] == "DLMessageGetFreeDiskSpace": self.mb2_handle_free_disk_space(msg) elif msg[0] == "DLMessageDisconnect": break def create_status_plist(self, fullBackup=True): # Creating Status file for backup statusDict = { 'UUID': str(uuid4()).upper(), 'BackupState': 'new', 'IsFullBackup': fullBackup, 'Version': '2.4', 'Date': datetime.datetime.fromtimestamp(mktime(gmtime())), 'SnapshotState': 'finished' } writePlist(statusDict, self.check_filename("Status.plist")) # def set_sync_lock(self): # #do_post_notification(device, NP_SYNC_WILL_START); # lockfile = self.afc.file_open("/com.apple.itunes.lock_sync") # if lockfile: # #do_post_notification(device, NP_SYNC_LOCK_REQUEST); # while True: # res = afc_file_lock(afc, lockfile, AFC_LOCK_EX); # if res == AFC_E_SUCCESS: # #do_post_notification(device, NP_SYNC_DID_START); # break # elif res == AFC_E_OP_WOULD_BLOCK): # sleep(0.5) # continue # else: # print "ERROR: could not lock file! error: %d\n" % res # self.afc.file_close(lockfile); # lockfile = 0; # cmd = CMD_LEAVE; # # if i == LOCK_ATTEMPTS: # print "ERROR: timeout while locking for sync" # self.afc.file_close(afc, lockfile) # lockfile = 0 # cmd = CMD_LEAVE # break # # def remove_sync_lock(self): # pass def create_info_plist(self): # Get device information device_info = self.lockdown.allValues # Get a list of installed user applications instpxy = installation_proxy(self.lockdown) apps = instpxy.browse( {"ApplicationType": "User"}, ["CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata"]) # Create new info.plits info = { "BuildVersion": device_info.get("BuildVersion") or "", "DeviceName": device_info.get("DeviceName") or "", "Display Name": device_info.get("DeviceName") or "", "GUID": "---", "Product Name": device_info.get("ProductName" or ""), "ProductType": device_info.get("ProductType") or "", "ProductVersion": device_info.get("ProductVersion") or "", "Serial Number": device_info.get("SerialNumber") or "", "Unique Identifier": self.udid.upper(), "Target Identifier": self.udid, "Target Type": "Device", "iTunes Version": "10.0.1", "MEID": device_info.get("MobileEquipmentIdentifier") or "", "Phone Number": device_info.get("PhoneNumber") or "", } info["ICCID"] = device_info.get("IntegratedCircuitCardIdentity") or "" info["IMEI"] = device_info.get( "InternationalMobileEquipmentIdentity") or "" info["Last Backup Date"] = datetime.datetime.now() # Starting SpringBoard service to retrieve icons position self.sbs = SBServiceClient(self.lockdown) installed_apps = [] apps_data = {} for app_entry in apps: tmp = {} bid = app_entry.get("CFBundleIdentifier") if bid: installed_apps.append(bid) pngdata = self.sbs.get_icon_pngdata(bid) if pngdata: tmp["PlaceholderIcon"] = pngdata tmp["iTunesMetadata"] = app_entry.get("iTunesMetadata") tmp["ApplicationSINF"] = app_entry.get("ApplicationSINF") apps_data[bid] = tmp info["Applications"] = apps_data info["Installed Applications"] = installed_apps # Handling itunes files iTunesFiles = [ "ApertureAlbumPrefs", "IC-Info.sidb", "IC-Info.sidv", "PhotosFolderAlbums", "PhotosFolderName", "PhotosFolderPrefs", "VoiceMemos.plist", "iPhotoAlbumPrefs", "iTunesApplicationIDs", "iTunesPrefs", "iTunesPrefs.plist" ] iTunesFilesDict = {} for i in iTunesFiles: data = self.afc.get_file_contents("/iTunes_Control/iTunes/" + i) if data: iTunesFilesDict[i] = plistlib.Data(data) info["iTunesFiles"] = iTunesFilesDict iBooksData2 = self.afc.get_file_contents("/Books/iBooksData2.plist") if iBooksData2: info["iBooks Data 2"] = plistlib.Data(iBooksData2) info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes") self.logger.info("Creating %s", os.path.join(self.udid, "Info.plist")) self.write_file(os.path.join(self.udid, "Info.plist"), plistlib.writePlistToString(info)) def backup(self, fullBackup=True): # TODO set_sync_lock self.logger.info("Starting %s backup...", ("Encrypted " if self.willEncrypt else "")) if not os.path.isdir(os.path.join(self.backupPath, self.udid)): os.makedirs(os.path.join(self.backupPath, self.udid)) self.logger.info("Backup mode: %s", "Full backup" if fullBackup else "Incremental backup") self.create_info_plist() options = {"ForceFullBackup": fullBackup} self.mobilebackup2_send_request("Backup", self.udid, options) self.work_loop() def restore( self, options={ "RestoreSystemFiles": True, "RestoreShouldReboot": True, "RestorePreserveCameraRoll": True, "RemoveItemsNotRestored": False, "RestoreDontCopyBackup": True, "RestorePreserveSettings": True }, password=None): self.logger.info("Starting restoration...") m = os.path.join(self.backupPath, self.udid, "Manifest.plist") try: manifest = readPlist(m) except IOError: self.logger.error("not a valid backup folder") return -1 if manifest.get("IsEncrypted"): print("Backup is encrypted, enter password : "******"Password"] = self.password self.mobilebackup2_send_request("Restore", self.udid, self.udid, options) self.work_loop() def info(self, options={}): source_udid = self.udid self.mobilebackup2_send_request("Info", self.udid, source_udid, options) self.work_loop() def list(self, options={}): source_udid = self.udid self.mobilebackup2_send_request("List", self.udid, source_udid, options) self.work_loop() def changepw(self, oldpw, newpw): options = {"OldPassword": oldpw, "NewPassword": newpw} self.mobilebackup2_send_request("ChangePassword", self.udid, "", options) self.work_loop() def unback(self, options={"Password": None}): source_udid = self.udid self.mobilebackup2_send_request("Unback", self.udid, source_udid, options) self.work_loop() def enableCloudBackup(self, options={"CloudBackupState": False}): self.mobilebackup2_send_request("EnableCloudBackup", self.udid, options) self.work_loop() def mobilebackup2_notify_cb(notification, data=None): if notification == NP_SYNC_CANCEL_REQUEST: self.logger.info( "User has cancelled the backup process on the device.") elif notification == NP_BACKUP_DOMAIN_CHANGED: backup_domain_changed = 1 else: self.logger.info("Unhandled notification '%s'", notification)