class FileRelay(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.file_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion[0] >= "8": raise DeviceVersionNotSupported self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def request_sources(self, sources=["UserDatabases"]): self.service.sendPlist({"Sources": sources}) while 1: res = self.service.recvPlist() if res: s = res.get("Status") if s == "Acknowledged": z = "" while True: x = self.service.recv() if not x: break z += x return z else: print res.get("Error") break return None
class FileRelay(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.file_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion[0] >= "8": raise DeviceVersionNotSupported self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print("Disconecting...") self.service.close() def request_sources(self, sources=["UserDatabases"]): self.service.sendPlist({"Sources": sources}) while 1: res = self.service.recvPlist() if res: s = res.get("Status") if s == "Acknowledged": z = "" while True: x = self.service.recv() if not x: break z += x return z else: print(res.get("Error")) break return None
class MobileBackup(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion[0] >= "5": raise DeviceVersionNotSupported self.service = self.lockdown.startService("com.apple.mobilebackup") self.udid = self.lockdown.udid 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": print "Got DLMessageDeviceReady" def check_filename(self, name): if name.find("../") != -1: raise Exception("HAX, sneaky dots in path %s" % name) if not name.startswith(self.backupPath): if name.startswith(self.udid): name = os.path.join(self.backupPath, name) return name name = os.path.join(self.backupPath, self.udid, name) return name return name def read_file(self, filename): filename = self.check_filename(filename) if os.path.isfile(filename): with open(filename, 'rb') as f: data = f.read() f.close() return data return None def write_file(self, filename, data): filename = self.check_filename(filename) with open(filename, 'wb') as f: f.write(data) f.close() 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.writePlistToString(info)) def ping(self, message): self.service.sendPlist(["DLMessagePing", message]) print "ping response", self.service.recvPlist() def device_link_service_send_process_message(self, msg): return self.service.sendPlist(["DLMessageProcessMessage", msg]) def device_link_service_receive_process_message(self): req = self.service.recvPlist() if req: assert req[0] == "DLMessageProcessMessage" return req[1] def send_file_received(self): return self.device_link_service_send_process_message( {"BackupMessageTypeKey": "kBackupMessageBackupFileReceived"}) def request_backup(self): req = { "BackupComputerBasePathKey": "/", "BackupMessageTypeKey": "BackupMessageBackupRequest", "BackupProtocolVersion": "1.6" } self.create_info_plist() self.device_link_service_send_process_message(req) res = self.device_link_service_receive_process_message() if not res: return if res["BackupMessageTypeKey"] != "BackupMessageBackupReplyOK": print res return self.device_link_service_send_process_message(res) filedata = "" f = None outpath = None while True: res = self.service.recvPlist() if not res or res[0] != "DLSendFile": if res[0] == "DLMessageProcessMessage": if res[1].get("BackupMessageTypeKey" ) == "BackupMessageBackupFinished": print "Backup finished OK !" #TODO BackupFilesToDeleteKey plistlib.writePlist( res[1]["BackupManifestKey"], self.check_filename("Manifest.plist")) break data = res[1].data info = res[2] if not f: outpath = self.check_filename(info.get("DLFileDest")) print info["DLFileAttributesKey"]["Filename"], info.get( "DLFileDest") f = open(outpath + ".mddata", "wb") f.write(data) if info.get( "DLFileStatusKey") == DEVICE_LINK_FILE_STATUS_LAST_HUNK: self.send_file_received() f.close() if not info.get("BackupManifestKey", False): plistlib.writePlist(info.get("BackupFileInfo"), outpath + ".mdinfo") f = None
class MobileBackup(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion[0] >= "5": raise DeviceVersionNotSupported self.service = self.lockdown.startService("com.apple.mobilebackup") self.udid = self.lockdown.udid 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": print("Got DLMessageDeviceReady") def check_filename(self, name): if name.find("../") != -1: raise Exception("HAX, sneaky dots in path %s" % name) if not name.startswith(self.backupPath): if name.startswith(self.udid): name = os.path.join(self.backupPath, name) return name name = os.path.join(self.backupPath, self.udid, name) return name return name def read_file(self, filename): filename = self.check_filename(filename) if os.path.isfile(filename): with open(filename, 'rb') as f: data = f.read() f.close() return data return None def write_file(self, filename, data): filename = self.check_filename(filename) with open(filename, 'wb') as f: f.write(data) f.close() 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 ping(self, message): self.service.sendPlist(["DLMessagePing", message]) print("ping response", self.service.recvPlist()) def device_link_service_send_process_message(self, msg): return self.service.sendPlist(["DLMessageProcessMessage", msg]) def device_link_service_receive_process_message(self): req = self.service.recvPlist() if req: assert req[0] == "DLMessageProcessMessage" return req[1] def send_file_received(self): return self.device_link_service_send_process_message({"BackupMessageTypeKey": "kBackupMessageBackupFileReceived"}) def request_backup(self): req = {"BackupComputerBasePathKey": "/", "BackupMessageTypeKey": "BackupMessageBackupRequest", "BackupProtocolVersion": "1.6" } self.create_info_plist() self.device_link_service_send_process_message(req) res = self.device_link_service_receive_process_message() if not res: return if res["BackupMessageTypeKey"] != "BackupMessageBackupReplyOK": print(res) return self.device_link_service_send_process_message(res) filedata = "" f = None outpath = None while True: res = self.service.recvPlist() if not res or res[0] != "DLSendFile": if res[0] == "DLMessageProcessMessage": if res[1].get("BackupMessageTypeKey") == "BackupMessageBackupFinished": print("Backup finished OK !") # TODO BackupFilesToDeleteKey plistlib.writePlist( res[1]["BackupManifestKey"], self.check_filename("Manifest.plist")) break data = res[1].data info = res[2] if not f: outpath = self.check_filename(info.get("DLFileDest")) print(info["DLFileAttributesKey"][ "Filename"], info.get("DLFileDest")) f = open(outpath + ".mddata", "wb") f.write(data) if info.get("DLFileStatusKey") == DEVICE_LINK_FILE_STATUS_LAST_HUNK: self.send_file_received() f.close() if not info.get("BackupManifestKey", False): plistlib.writePlist( info.get("BackupFileInfo"), outpath + ".mdinfo") f = None
class MobileBackup2(MobileBackup): service = None def __init__(self, lockdown=None, backupPath=None, password=""): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() ProductVersion = self.lockdown.getValue("", "ProductVersion") if ProductVersion[0] < "5": raise DeviceVersionNotSupported self.udid = lockdown.getValue("", "UniqueDeviceID") self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt") self.escrowBag = lockdown.getValue('', 'EscrowBag') 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 backupPath: self.backupPath = backupPath else: self.backupPath = "backups" if not os.path.isdir(self.backupPath): os.makedirs(self.backupPath, 0o0755) print( "Starting new com.apple.mobilebackup2 service with working dir: %s" % self.backupPath) self.password = password 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": self.version_exchange() 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: print("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 a = ["DLMessageStatusResponse", 0, freeSpace] self.service.sendPlist(a) 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: print("Sending %s to device" % filename) self.service.send_raw(chr(CODE_FILE_DATA) + data) self.service.send_raw(chr(CODE_SUCCESS)) else: print("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] dirlist = {} self.mobilebackup2_send_status_response(0, status2=dirlist) def mb2_handle_make_directory(self, msg): dirname = self.check_filename(msg[1]) print("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() filedata = "" 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 else: print("Unknown code", ord(stuff[0])) break self.mobilebackup2_send_status_response(0) def mb2_handle_move_files(self, msg): for k, v in list(msg[1].items()): print("Renaming %s to %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): for filename in msg[1]: print("Removing ", self.check_filename(filename)) try: filename = self.check_filename(filename) if os.path.isfile(filename): os.unlink(filename) except Exception as e: print(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": print(m) if errcode == 1: print(msg[1].get("ErrorDescription")) print("Please unlock your device and retry...") break 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': '82D108D4-521C-48A5-9C42-79C5E654B98F', # FixMe We Should USE an UUID generator uuid.uuid3(uuid.NAMESPACE_DNS, hostname) 'BackupState': 'new', 'IsFullBackup': fullBackup, 'Version': '2.4', 'Date': datetime.datetime.fromtimestamp(mktime(gmtime())), 'SnapshotState': 'finished' } writePlist(statusDict, self.check_filename("Status.plist")) def backup(self, fullBackup=True): print("Starting%sbackup..." % (" Encrypted " if self.willEncrypt else "")) options = {} if not os.path.isdir(os.path.join(self.backupPath, self.udid)): os.makedirs(os.path.join(self.backupPath, self.udid)) self.create_info_plist() if fullBackup == True: options["ForceFullBackup"] = True self.mobilebackup2_send_request("Backup", self.udid, options) self.work_loop() def restore( self, options={ "RestoreSystemFiles": True, "RestoreShouldReboot": False, "RestoreDontCopyBackup": True, "RestorePreserveSettings": True }, password=None): print("Starting restoration...") m = os.path.join(self.backupPath, self.udid, "Manifest.plist") manifest = readPlist(m) 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={}): self.mobilebackup2_send_request("Info", self.udid, options) info = self.work_loop() if info: pprint(info.get("Content")) return info def list(self, options={}): self.mobilebackup2_send_request("List", self.udid, options) z = self.work_loop() if z: print(z["Content"]) return z def changepw(self, oldpw, newpw): options = {"OldPassword": oldpw, "NewPassword": newpw} self.mobilebackup2_send_request("ChangePassword", self.udid, "") z = self.work_loop() if z: print(z) return z def unback(self, options={"Password": None}): self.mobilebackup2_send_request("Unback", self.udid, options) print(self.work_loop()) def enableCloudBackup(self, options={"CloudBackupState": False}): self.mobilebackup2_send_request("EnableCloudBackup", self.udid, options) print(self.work_loop())