class screenshotr(object): def __init__(self, lockdown=None, serviceName='com.apple.mobile.screenshotr'): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() #Starting Screenshot service self.service = self.lockdown.startService(serviceName) #hand check DLMessageVersionExchange = self.service.recvPlist() #assert len(DLMessageVersionExchange) == 2 version_major = DLMessageVersionExchange[1] self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major ]) DLMessageDeviceReady = self.service.recvPlist() def stop_session(self): self.service.close() def take_screenshot(self): self.service.sendPlist(['DLMessageProcessMessage', {'MessageType': 'ScreenShotRequest'}]) res = self.service.recvPlist() assert len(res) == 2 assert res[0] == "DLMessageProcessMessage" if res[1].get('MessageType') == 'ScreenShotReply': data = res[1]['ScreenShotData'].data return data return None
class screenshotrClient(object): def __init__(self, lockdown=None, serviceName='com.apple.mobile.screenshotr'): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() #Starting Screenshot service self.service = self.lockdown.startService(serviceName) #hand check DLMessageVersionExchange = self.service.recvPlist() #assert len(DLMessageVersionExchange) == 2 version_major = DLMessageVersionExchange[1] self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major ]) DLMessageDeviceReady = self.service.recvPlist() def stop_session(self): self.service.close() def take_screenshot(self): self.service.sendPlist(['DLMessageProcessMessage', {'MessageType': 'ScreenShotRequest'}]) res = self.service.recvPlist() assert len(res) == 2 assert res[0] == "DLMessageProcessMessage" if res[1].get('MessageType') == 'ScreenShotReply': data = res[1]['ScreenShotData'].data return data return None
class Syslog(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.c = self.lockdown.startService("com.apple.syslog_relay") if self.c: self.c.send("watch") else: sys.exit(1) def watch(self, procName=None, logFile=None): while True: d = self.c.recv(4096) if not d: break if procName: procFilter = re.compile(procName, re.IGNORECASE) if len(d.split(" ")) > 4 and not procFilter.search(d): continue print d.strip("\n\x00\x00") if logFile: with open(logFile, 'a') as f: f.write(d.replace("\x00", ""))
class FileRelayClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.file_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() 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"]): print "Downloading sources ", sources self.service.sendPlist({"Sources":sources}) res = self.service.recvPlist() if res: if res.has_key("Status"): if res["Status"] == "Acknowledged": z = "" while True: x = self.service.recv() if not x: break z += x return z return None
class SpringboardClient(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService("com.apple.springboardservices") def get_iconstate(self): return self.service.sendRequest({ 'command': 'getIconState', 'formatVersion': '2' })[0] def set_iconstate(self, state): self.service.sendPlist({ 'command': 'setIconState', 'iconState': state }) def get_iconpngdata(self, bundleid): return self.service.sendRequest({ 'command': 'getIconPNGData', 'bundleId': bundleid })['pngData'].data def get_interface_orientation(self): response = self.service.sendRequest({'command': 'getInterfaceOrientation'}) if response is None or 'interfaceOrientation' not in response: raise RuntimeError('Unable to retrieve interface orientation') return response['interfaceOrientation'] def get_wallpaper_pngdata(self): return self.service.sendRequest({'command': 'getHomeScreenWallpaperPNGData'})['pngData'].data
class SpringboardClient(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService( "com.apple.springboardservices") def get_iconstate(self): return self.service.sendRequest({ 'command': 'getIconState', 'formatVersion': '2' })[0] def set_iconstate(self, state): self.service.sendPlist({'command': 'setIconState', 'iconState': state}) def get_iconpngdata(self, bundleid): return self.service.sendRequest({ 'command': 'getIconPNGData', 'bundleId': bundleid })['pngData'].data def get_interface_orientation(self): response = self.service.sendRequest( {'command': 'getInterfaceOrientation'}) if response is None or 'interfaceOrientation' not in response: raise RuntimeError('Unable to retrieve interface orientation') return response['interfaceOrientation'] def get_wallpaper_pngdata(self): return self.service.sendRequest( {'command': 'getHomeScreenWallpaperPNGData'})['pngData'].data
class Syslog(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.c = self.lockdown.startService("com.apple.syslog_relay") self.c.send("watch") def watch(self,procName=None,logFile=None): while True: d = self.c.recv(4096) if not d: break if procName: procFilter = re.compile(procName,re.IGNORECASE) if len(d.split(" ")) > 4 and not procFilter.search(d): continue print d.strip("\n\x00\x00") if logFile: with open(logFile, 'a') as f: f.write(d.replace("\x00", ""))
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)
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() error = res.get("Error") if error: print res["Error"] return None return AFCClient(lockdown, service=mis)
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() error = res.get("Error") if error: print res["Error"] return None return AFCClient(lockdown, service=mis)
class NPClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.notification_proxy"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def post_notification(self, notification): #Sends a notification to the device's notification_proxy. self.service.sendPlist({ "Command": "PostNotification", #} "Name": notification }) res = self.service.recvPlist() pprint(res) return res def observe_notification(self, notification): #Tells the device to send a notification on the specified event self.service.sendPlist({ "Command": "ObserveNotification", #} "Name": notification }) res = self.service.recvPlist() pprint(res) return res def get_notification(self, notification): #Checks if a notification has been sent by the device res = self.service.recvPlist() pprint(res) return res
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() pprint(res) 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 NPClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.notification_proxy"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def post_notification(self, notification): #Sends a notification to the device's notification_proxy. self.service.sendPlist({"Command": "PostNotification",#} "Name": notification}) res = self.service.recvPlist() pprint(res) return res def observe_notification(self, notification): #Tells the device to send a notification on the specified event self.service.sendPlist({"Command": "ObserveNotification",#} "Name": notification}) res = self.service.recvPlist() pprint(res) return res def get_notification(self, notification): #Checks if a notification has been sent by the device res = self.service.recvPlist() pprint(res) return res
class AFCClient(object): def __init__(self, lockdown=None, serviceName="com.apple.afc", service=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() if service: self.service = service else: self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def dispatch_packet(self, operation, data, this_length=0): afcpack = Container(magic=AFCMAGIC, entire_length=40 + len(data), this_length=40 + len(data), packet_num=self.packet_num, operation=operation) if this_length: afcpack.this_length = this_length header = AFCPacket.build(afcpack) self.packet_num += 1 self.service.send(header + data) def receive_data(self): res = self.service.recv(40) status = AFC_E_SUCCESS data = "" if res: res = AFCPacket.parse(res) assert res["entire_length"] >= 40 length = res["entire_length"] - 40 data = self.service.recv_exact(length) if res.operation == AFC_OP_STATUS: if length != 8: print "Status length != 8" status = struct.unpack("<Q", data[:8])[0] elif res.operation != AFC_OP_DATA: pass#print "error ?", res return status, data def do_operation(self, opcode, data=""): try: self.dispatch_packet(opcode, data) return self.receive_data() except: self.lockdown = LockdownClient() self.service = lockdown.startService(serviceName) return self.do_operation(opcode, data) def list_to_dict(self, d): t = d.split("\x00") t = t[:-1] assert len(t) % 2 == 0 res = {} for i in xrange(len(t)/2): res[t[i*2]] = t[i*2 + 1] return res def get_device_infos(self): status, infos = self.do_operation(AFC_OP_GET_DEVINFO) if status == AFC_E_SUCCESS: return self.list_to_dict(infos) def read_directory(self, dirname): status, data = self.do_operation(AFC_OP_READ_DIR, dirname) if status == AFC_E_SUCCESS: return filter(lambda x:x!="", data.split("\x00")) return [] def make_directory(self, dirname): status, data = self.do_operation(AFC_OP_MAKE_DIR, dirname) return status def remove_directory(self, dirname): info = self.get_file_info(dirname) if not info or info.get("st_ifmt") != "S_IFDIR": #print "remove_directory: %s not S_IFDIR" % dirname return for d in self.read_directory(dirname): if d == "." or d == ".." or d == "": continue info = self.get_file_info(dirname + "/" + d) if info.get("st_ifmt") == "S_IFDIR": self.remove_directory(dirname + "/" + d) else: print dirname + "/" + d self.file_remove(dirname + "/" + d) assert len(self.read_directory(dirname)) == 2 #.. et . return self.file_remove(dirname) def get_file_info(self, filename): status, data = self.do_operation(AFC_OP_GET_FILE_INFO, filename) if status == AFC_E_SUCCESS: return self.list_to_dict(data) def make_link(self, target, linkname, type=AFC_SYMLINK): status, data = self.do_operation(AFC_OP_MAKE_LINK, struct.pack("<Q", type) + target + "\x00" + linkname + "\x00") print "make_link", status return status def file_open(self, filename, mode=AFC_FOPEN_RDONLY): status, data = self.do_operation(AFC_OP_FILE_OPEN, struct.pack("<Q", mode) + filename + "\x00") if data: handle = struct.unpack("<Q", data)[0] return handle def file_close(self, handle): status, data = self.do_operation(AFC_OP_FILE_CLOSE, struct.pack("<Q", handle)) return status def file_remove(self, filename): status, data = self.do_operation(AFC_OP_REMOVE_PATH, filename + "\x00") return status def file_rename(self, old, new): status, data = self.do_operation(AFC_OP_RENAME_PATH, old + "\x00" + new + "\x00") return status def file_read(self, handle, sz): MAXIMUM_READ_SIZE = 1 << 16 data = "" while sz > 0: if sz > MAXIMUM_READ_SIZE: toRead = MAXIMUM_READ_SIZE else: toRead = sz try: self.dispatch_packet(AFC_OP_READ, struct.pack("<QQ", handle, toRead)) s, d = self.receive_data() except: self.lockdown = LockdownClient() self.service = self.lockdown.startService("com.apple.afc") return self.file_read(handle, sz) if s != AFC_E_SUCCESS: break sz -= toRead data += d return data def file_write(self, handle, data): MAXIMUM_WRITE_SIZE = 1 << 15 hh = struct.pack("<Q", handle) segments = len(data) / MAXIMUM_WRITE_SIZE try: for i in xrange(segments): self.dispatch_packet(AFC_OP_WRITE, hh + data[i*MAXIMUM_WRITE_SIZE:(i+1)*MAXIMUM_WRITE_SIZE], this_length=48) s, d = self.receive_data() if s != AFC_E_SUCCESS: print "file_write error %d" % s break if len(data) % MAXIMUM_WRITE_SIZE: self.dispatch_packet(AFC_OP_WRITE, hh + data[segments*MAXIMUM_WRITE_SIZE:], this_length=48) s, d = self.receive_data() #print s,d except: self.lockdown = LockdownClient() self.service = lockdown.startService(serviceName) self.file_write(handle,data) return s def get_file_contents(self, filename): info = self.get_file_info(filename) if info: if info['st_ifmt'] == 'S_IFLNK': filename = info['LinkTarget'] if info['st_ifmt'] == 'S_IFDIR': print "%s is directory..." % filename return print "Reading %s" % filename h = self.file_open(filename) if not h: return d = self.file_read(h, int(info["st_size"])) self.file_close(h) return d return def set_file_contents(self, filename, data): h = self.file_open(filename, AFC_FOPEN_WR) if not h: return d = self.file_write(h, data) self.file_close(h) def dir_walk(self,dir,file_list=[]): d = os.path.abspath(dir) file_list = [] for file in [file for file in self.read_directory(d) if not file in [".",".."]]: path = os.path.join(d,file) info = self.get_file_info(path) if info: if info['st_ifmt'] == 'S_IFDIR': file_list += self.dir_walk(path,file_list) info['path'] = path file_list.append(info) return file_list
class InstallationProxy(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService( 'com.apple.mobile.installation_proxy') def install_ipa(self, ipaPath): #Start afc service & upload ipa filename = os.path.basename(ipaPath) with AFCFile(name='/' + filename, mode='wb', afc=AFCClient(self.lockdown)) as f: f.write(open(ipaPath, 'rb').read()) self.service.sendPlist({'Command': 'Install', 'PackagePath': filename}) while True: response = self.service.recvPlist() if not response: break completion = response.get('PercentComplete') if completion: print 'Installing, %s: %s %% Complete' % (ipaPath, completion) status = response.get('Status') if status == 'Complete': print 'Installation %s' % status break def app_info(self): return self.service.sendRequest({'Command': 'Lookup'})['LookupResult'] def list_user_apps(self): return [[ app['CFBundleIdentifier'], app.get('CFBundleDisplayName'), app.get('Container') ] for app in self.app_info().values() if app.get('ApplicationType') == 'User'] def list_system_apps(self): return [[app['CFBundleIdentifier'], app.get('CFBundleDisplayName')] for app in self.app_info().values() if app.get('ApplicationType') == 'System'] def list_user_apps_BundleID(self): return [ app['CFBundleIdentifier'] for app in self.app_info().values() if app.get('ApplicationType') == 'User' ] def list_system_apps_BundleID(self): return [ app['CFBundleIdentifier'] for app in self.app_info().values() if app.get('ApplicationType') == 'System' ] def list_all_apps_BundleID(self): return self.app_info().keys() def close(self): self.service.close() def __del__(self): self.close()
class installation_proxy(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService( "com.apple.mobile.installation_proxy") def watch_completion(self, handler=None, *args): while True: z = self.service.recvPlist() if not z: break completion = z.get("PercentComplete") if completion: if handler: print "calling handler" handler(completion, *args) print "%s %% Complete" % z.get("PercentComplete") if z.get("Status") == "Complete": return z.get("Status") return "Error" def send_cmd_for_bid(self, bundleID, cmd="Archive", options=None, handler=None, *args): cmd = {"Command": cmd, "ApplicationIdentifier": bundleID} if options: cmd.update(options) self.service.sendPlist(cmd) #print "%s : " % (cmd, bundleID) print "%s : %s\n" % (cmd, self.watch_completion(handler, *args)) def uninstall(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Uninstall", options, 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(self, ipaPath, options=None, handler=None, *args): return self.install_or_upgrade(ipaPath, "Install", client_options, handler, args) def upgrade(self, ipaPath, options=None, handler=None, *args): return self.install_or_upgrade(ipaPath, "Upgrade", client_options, handler, args) def apps_info(self): self.service.sendPlist({"Command": "Lookup"}) return self.service.recvPlist().get('LookupResult') def archive(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Archive", options, handler, args) def restore_archive(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Restore", client_options, handler, args) def remove_archive(self, bundleID, options={}, handler=None, *args): self.send_cmd_for_bid(bundleID, "RemoveArchive", options, handler, args) def archives_info(self): return self.service.sendRequest({ "Command": "LookupArchive" }).get("LookupResult") def search_path_for_bid(self, bid): path = None for a in self.get_apps(appTypes=["User", "System"]): if a.get("CFBundleIdentifier") == bid: path = a.get("Path") + "/" + a.get("CFBundleExecutable") return path def get_apps(self, appTypes=["User"]): return [ app for app in self.apps_info().values() if app.get("ApplicationType") in appTypes ] def print_apps(self, appType=["User"]): for app in self.get_apps(appType): print("%s : %s => %s" % (app.get("CFBundleDisplayName"), app.get("CFBundleIdentifier"), app.get("Path") if app.get("Path") else app.get("Container"))).encode('utf-8') def get_apps_bid(self, appTypes=["User"]): return [ app["CFBundleIdentifier"] for app in self.get_apps() if app.get("ApplicationType") in appTypes ] def close(self): self.service.close() def __del__(self): self.close()
class NPClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.notification_proxy"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) def stop_session(self): print "Disconecting..." self.service.close() def post_notification(self, notification): #Sends a notification to the device's notification_proxy. self.service.sendPlist({ "Command": "PostNotification", "Name": notification }) self.service.sendPlist({"Command": "Shutdown"}) res = self.service.recvPlist() pprint(res) if res: if res.get("Command") == "ProxyDeath": return res.get("Command") else: print "Got unknown NotificationProxy command %s" % res.get( "Command") pprint(res) return None def observe_notification(self, notification): #Tells the device to send a notification on the specified event print "Observing %s" % notification self.service.sendPlist({ "Command": "ObserveNotification", "Name": notification }) def get_notification(self, notification): #Checks if a notification has been sent by the device res = self.service.recvPlist() if res: if res.get("Command") == "RelayNotification": if res.get("Name"): return res.get("Name") elif res.get("Command") == "ProxyDeath": print "NotificationProxy died!" else: print "Got unknown NotificationProxy command %s" % res.get( "Command") pprint(res) return None def notifier(self, name, args=None): if args == None: return None self.observe_notification(args.get("notification")) while args.get("running") == True: np_name = self.get_notification(args.get("notification")) if np_name: userdata = args.get("userdata") try: thread.start_new_thread(args.get("callback"), ( np_name, userdata, )) except: print "Error: unable to start thread" def subscribe(self, notification, cb, data=None): np_data = { "running": True, "notification": notification, "callback": cb, "userdata": data, } try: thread.start_new_thread(self.notifier, ( "NotificationProxyNotifier_" + notification, np_data, )) except: print "Error: unable to start thread" while (1): time.sleep(1)
class DIAGClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.diagnostics_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def query_mobilegestalt(self, MobileGestalt=MobileGestaltKeys.split("\n")): self.service.sendPlist({"Request": "MobileGestalt", "MobileGestaltKeys": MobileGestalt}) res = self.service.recvPlist() d = res.get("Diagnostics") if d: return d.get("MobileGestalt") return None def action(self, action="Shutdown", flags=None): self.service.sendPlist({"Request": action }) res = self.service.recvPlist() return res.get("Diagnostics") def restart(self): return self.action("Restart") def shutdown(self): return self.action("Shutdown") def diagnostics(self, diagType="All"): self.service.sendPlist({"Request": diagType}) res = self.service.recvPlist() if res: return res.get("Diagnostics") return None def ioregistry_entry(self, name=None, ioclass=None): d = {} if name: d["EntryName"] = name if ioclass: d["EntryClass"] = ioclass d["Request"] = "IORegistry" self.service.sendPlist(d) res = self.service.recvPlist() pprint(res) if res: return res.get("Diagnostics") return None def ioregistry_plane(self, plane=""): d = {} if plane: d["CurrentPlane"] = plane else: d["CurrentPlane"] = "" d["Request"] = "IORegistry" self.service.sendPlist(d) res = self.service.recvPlist() dd = res.get("Diagnostics") if dd: return dd.get("IORegistry") return None
(options, args) = parser.parse_args() if options.output: output = options.output elif sys.platform == "win32": import win32pipe, win32file output = Win32Pipe() else: _,path = mkstemp(prefix="device_dump_",suffix=".pcap",dir=".") print "Recording data to: %s" % path output = PcapOut(path) lockdown = LockdownClient() pcap = lockdown.startService("com.apple.pcapd") while True: d = pcap.recvPlist() if not d: break data = d.data hdrsize, xxx, packet_size = struct.unpack(">LBL", data[:9]) flags1, flags2, offset_to_ip_data, zero = struct.unpack(">LLLL", data[9:0x19]) assert hdrsize >= 0x19 interfacetype= data[0x19:hdrsize].strip("\x00") t = time.time() print interfacetype, packet_size, t packet = data[hdrsize:]
class DIAGClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.diagnostics_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def query_mobilegestalt(self, MobileGestalt=MobileGestaltKeys): self.service.sendPlist({"Request": "MobileGestalt", "MobileGestaltKeys": MobileGestalt}) res = self.service.recvPlist() #pprint(res) if "Diagnostics" in res: return res def action(self, action="Shutdown", flags=None): self.service.sendPlist({"Request": action}) res = self.service.recvPlist() #pprint(res) return res def restart(self): return self.action("Restart") def shutdown(self): return self.action("Shutdown") def diagnostics(self, diagType="All"): self.service.sendPlist({"Request": diagType}) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res def ioregistry_entry(self, name=None, ioclass=None): req = {"Request": "IORegistry"} if name: req["EntryName"] = name if ioclass: req["EntryClass"] = ioclass self.service.sendPlist(req) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res def ioregistry_plane(self, plane, ioclass): req = {"Request": "IORegistry", "CurrentPlane": ioclass} self.service.sendPlist(req) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res
class NPClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.notification_proxy"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) def stop_session(self): print "Disconecting..." self.service.close() def post_notification(self, notification): #Sends a notification to the device's notification_proxy. self.service.sendPlist({"Command": "PostNotification", "Name": notification}) self.service.sendPlist({"Command": "Shutdown"}) res = self.service.recvPlist() pprint(res) if res: if res.get("Command") == "ProxyDeath": return res.get("Command") else: print "Got unknown NotificationProxy command %s" % res.get("Command") pprint(res) return None def observe_notification(self, notification): #Tells the device to send a notification on the specified event print "Observing %s" % notification self.service.sendPlist({"Command": "ObserveNotification", "Name": notification}) def get_notification(self, notification): #Checks if a notification has been sent by the device res = self.service.recvPlist() if res: if res.get("Command") == "RelayNotification": if res.get("Name"): return res.get("Name") elif res.get("Command") == "ProxyDeath": print "NotificationProxy died!" else: print "Got unknown NotificationProxy command %s" % res.get("Command") pprint(res) return None def notifier(self, name, args=None): if args == None: return None self.observe_notification(args.get("notification")) while args.get("running") == True: np_name = self.get_notification(args.get("notification")) if np_name: userdata = args.get("userdata") try: thread.start_new_thread( args.get("callback") , (np_name, userdata, ) ) except: print "Error: unable to start thread" def subscribe(self, notification, cb, data=None): np_data = { "running": True, "notification": notification, "callback": cb, "userdata": data, } try: thread.start_new_thread( self.notifier, ("NotificationProxyNotifier_"+notification, np_data, ) ) except: print "Error: unable to start thread" while(1): time.sleep(1)
class AFCClient(object): """ Creates a connection to the iDevice using AFC protocol. Attributes: `lockdown`: The `LockdownClient` class that should be used for almost everything. `serviceName`: The service ID of the protocol. `service`: The plist service which is running in the background. `packet_num`: Used to track number of packets sent during lifetime of the client. """ def __init__(self, lockdown=None, serviceName='com.apple.afc', service=None): """ Constructor method of `AFCClient`. Note: `serviceName` is obsolete when `service` parameter is used. Although it will be saved as attribute. Args: `lockdown` (optional): The `LockdownClient` class that should be used for almost everything. `serviceName` (optional): Service ID of the protocol, defaults to 'com.apple.afc'. Used for abstract class purposes although you can modify if you have good reasons. `service` (optional): Useful when you already have a service running. """ if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() if service: self.service = service else: self.service = self.lockdown.startService(serviceName) self.serviceName = serviceName self.packet_num = 0 def stop_session(self): """Disconnects from iDevice.""" print "Disconecting..." self.service.close() def __del__(self): self.stop_session() def dispatch_packet(self, operation, data, this_length=0): """ Dispatches an AFC packet over a client. Args: `operation`: The operation to do. See the source code for the list of constants. `data`: The data to send with header. `this_length` (optional): Not sure but, according to C libimobiledevice, it looks like size of packet + data length. """ afcpack = Container(magic=AFCMAGIC, entire_length=40 + len(data), this_length=40 + len(data), packet_num=self.packet_num, operation=operation) if this_length: afcpack.this_length = this_length header = AFCPacket.build(afcpack) self.packet_num += 1 self.service.send(header + data) def receive_data(self): """ Receives data through an AFC client. Returns: The response of the iDevice. Raises: `AFCError` if the operation was not successful. """ res = self.service.recv(40) status = AFC_E_SUCCESS data = "" if res: res = AFCPacket.parse(res) assert res["entire_length"] >= 40 length = res["entire_length"] - 40 data = self.service.recv_exact(length) if res.operation == AFC_OP_STATUS: assert length == 8 status = int(struct.unpack("<Q", data[:8])[0]) if status != AFC_E_SUCCESS: raise AFCError(status) elif res.operation != AFC_OP_DATA: pass # print "error ?", res return data def do_operation(self, operation, data="", this_length=0): """ Dispatches a packet and returns the response. Args: `operation`: The operation to perform. `data` (optional): The data to send. `this_length` (optional): Not sure but, according to C libimobiledevice, it looks like size of packet + data length. Returns: The data that is recieved after the operation is done. """ self.dispatch_packet(operation, data, this_length) return self.receive_data() def list_to_dict(self, d): """Converts a primitive key, value array to Python compatible dictionary.""" it = iter(d.rstrip("\x00").split("\x00")) return dict(zip(it, it)) def get_device_infos(self): infos = self.do_operation(AFC_OP_GET_DEVINFO) return self.list_to_dict(infos) def get_file_info(self, filename): data = self.do_operation(AFC_OP_GET_FILE_INFO, filename) return self.list_to_dict(data) def file_open(self, filename, mode=AFC_FOPEN_RDONLY): #if filename.startswith('/var/Mobile/Media') and self.serviceName == 'com.apple.afc': # filename = filename.replace('/var/Mobile/Media', '') data = self.do_operation(AFC_OP_FILE_OPEN, struct.pack("<Q", mode) + filename + "\x00") return struct.unpack("<Q", data)[0] def file_close(self, handle): self.do_operation(AFC_OP_FILE_CLOSE, struct.pack("<Q", handle)) def file_seek(self, handle, offset, whence=os.SEEK_SET): self.do_operation(AFC_OP_FILE_SEEK, struct.pack("<QQq", handle, whence, offset)) def file_tell(self, handle): data = self.do_operation(AFC_OP_FILE_TELL, struct.pack("<Q", handle)) return struct.unpack("<Q", data)[0] def file_truncate(self, handle, size=None): if size is None: size = self.file_tell(handle) self.do_operation(AFC_OP_FILE_SET_SIZE, struct.pack("<QQ", handle, size)) def file_read(self, handle, sz): MAXIMUM_READ_SIZE = 1 << 16 data = "" while sz > 0: if sz > MAXIMUM_READ_SIZE: toRead = MAXIMUM_READ_SIZE else: toRead = sz d = self.do_operation(AFC_OP_READ, struct.pack("<QQ", handle, toRead)) sz -= toRead data += d return data def file_write(self, handle, data): MAXIMUM_WRITE_SIZE = 1 << 15 hh = struct.pack("<Q", handle) segments = len(data) / MAXIMUM_WRITE_SIZE for i in xrange(segments): self.do_operation(AFC_OP_WRITE, hh + data[i*MAXIMUM_WRITE_SIZE:(i+1)*MAXIMUM_WRITE_SIZE], this_length=48) if len(data) % MAXIMUM_WRITE_SIZE: self.do_operation(AFC_OP_WRITE, hh + data[segments*MAXIMUM_WRITE_SIZE:], this_length=48) def make_link(self, target, linkname, link_type=AFC_SYMLINK): self.do_operation(AFC_OP_MAKE_LINK, struct.pack("<Q", link_type) + target + "\x00" + linkname + "\x00") def remove_path(self, path): self.do_operation(AFC_OP_REMOVE_PATH, path + "\x00") def rename_path(self, old, new): self.do_operation(AFC_OP_RENAME_PATH, old + "\x00" + new + "\x00") def read_directory(self, dirname): data = self.do_operation(AFC_OP_READ_DIR, dirname) return data.rstrip("\x00").split("\x00") def make_directory(self, dirname): self.do_operation(AFC_OP_MAKE_DIR, dirname) def dir_walk(self, dirname): # root = dirname dirs = [] files = [] for fd in self.read_directory(dirname): if fd in ('.', '..', ''): # is it ever be '' ? continue if self.get_file_info(posixpath.join(dirname, fd)).get('st_ifmt') == 'S_IFDIR': dirs.append(fd) else: files.append(fd) yield dirname, dirs, files # if dirs != [], continue iterating using recursion if dirs: for d in dirs: for walk_result in self.dir_walk(posixpath.join(dirname, d)): yield walk_result def remove_directory(self, dirname): for d in self.read_directory(dirname): if d in ('.', '..', ''): continue path = posixpath.join(dirname, d) if self.get_file_info(path).get("st_ifmt") == "S_IFDIR": self.remove_directory(path) else: self.remove_path(path) assert len(self.read_directory(dirname)) == 2 # "." and ".." self.remove_path(dirname)
(options, args) = parser.parse_args() if sys.platform == "win32": import win32pipe, win32file output = Win32Pipe() else: if options.output: path = options.output else: _, path = mkstemp(prefix="device_dump_", suffix=".pcap", dir=".") print "Recording data to: %s" % path output = PcapOut(path) lockdown = LockdownClient() pcap = lockdown.startService("com.apple.pcapd") while True: d = pcap.recvPlist() if not d: break data = d.data hdrsize, xxx, packet_size = struct.unpack(">LBL", data[:9]) flags1, flags2, offset_to_ip_data, zero = struct.unpack( ">LLLL", data[9:0x19]) assert hdrsize >= 0x19 interfacetype = data[0x19:hdrsize].strip("\x00") t = time.time() print interfacetype, packet_size, t
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"), "DeviceName": root_node.get("DeviceName"), "Display Name": root_node.get("DeviceName"), "GUID": "---", "ProductType": root_node.get("ProductType"), "ProductVersion": root_node.get("ProductVersion"), # "Serial Number": root_node.get("SerialNumber"), "Unique Identifier": self.udid.upper(), "Target Identifier": self.udid, "Target Type": "Device", "iTunes Version": "10.0.1", } info["ICCID"] = root_node.get("IntegratedCircuitCardIdentity") info["IMEI"] = root_node.get("InternationalMobileEquipmentIdentity") info["Last Backup Date"] = datetime.datetime.now() iTunesFiles = [ "ApertureAlbumPrefs", "IC-Info.sidb", "IC-Info.sidv", "PhotosFolderAlbums", "PhotosFolderName", "PhotosFolderPrefs", "iPhotoAlbumPrefs", "iTunesApplicationIDs", "iTunesPrefs", "iTunesPrefs.plist", ] 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 AFCClient(object): def __init__(self, lockdown=None, serviceName="com.apple.afc", service=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() if service: self.service = service else: self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def dispatch_packet(self, operation, data, this_length=0): afcpack = Container(magic=AFCMAGIC, entire_length=40 + len(data), this_length=40 + len(data), packet_num=self.packet_num, operation=operation) if this_length: afcpack.this_length = this_length header = AFCPacket.build(afcpack) self.packet_num += 1 self.service.send(header + data) def receive_data(self): res = self.service.recv(40) status = AFC_E_SUCCESS data = "" if res: res = AFCPacket.parse(res) assert res["entire_length"] >= 40 length = res["entire_length"] - 40 data = self.service.recv_exact(length) if res.operation == AFC_OP_STATUS: if length != 8: print "Status length != 8" status = struct.unpack("<Q", data[:8])[0] elif res.operation != AFC_OP_DATA: pass #print "error ?", res return status, data def do_operation(self, opcode, data=""): try: self.dispatch_packet(opcode, data) return self.receive_data() except: self.lockdown = LockdownClient() self.service = lockdown.startService(serviceName) return self.do_operation(opcode, data) def list_to_dict(self, d): t = d.split("\x00") t = t[:-1] assert len(t) % 2 == 0 res = {} for i in xrange(len(t) / 2): res[t[i * 2]] = t[i * 2 + 1] return res def get_device_infos(self): status, infos = self.do_operation(AFC_OP_GET_DEVINFO) if status == AFC_E_SUCCESS: return self.list_to_dict(infos) def read_directory(self, dirname): status, data = self.do_operation(AFC_OP_READ_DIR, dirname) if status == AFC_E_SUCCESS: return filter(lambda x: x != "", data.split("\x00")) return [] def make_directory(self, dirname): status, data = self.do_operation(AFC_OP_MAKE_DIR, dirname) return status def remove_directory(self, dirname): info = self.get_file_info(dirname) if not info or info.get("st_ifmt") != "S_IFDIR": #print "remove_directory: %s not S_IFDIR" % dirname return for d in self.read_directory(dirname): if d == "." or d == ".." or d == "": continue info = self.get_file_info(dirname + "/" + d) if info.get("st_ifmt") == "S_IFDIR": self.remove_directory(dirname + "/" + d) else: print dirname + "/" + d self.file_remove(dirname + "/" + d) assert len(self.read_directory(dirname)) == 2 #.. et . return self.file_remove(dirname) def get_file_info(self, filename): status, data = self.do_operation(AFC_OP_GET_FILE_INFO, filename) if status == AFC_E_SUCCESS: return self.list_to_dict(data) def make_link(self, target, linkname, type=AFC_SYMLINK): status, data = self.do_operation( AFC_OP_MAKE_LINK, struct.pack("<Q", type) + target + "\x00" + linkname + "\x00") print "make_link", status return status def file_open(self, filename, mode=AFC_FOPEN_RDONLY): status, data = self.do_operation( AFC_OP_FILE_OPEN, struct.pack("<Q", mode) + filename + "\x00") if data: handle = struct.unpack("<Q", data)[0] return handle def file_close(self, handle): status, data = self.do_operation(AFC_OP_FILE_CLOSE, struct.pack("<Q", handle)) return status def file_remove(self, filename): status, data = self.do_operation(AFC_OP_REMOVE_PATH, filename + "\x00") return status def file_rename(self, old, new): status, data = self.do_operation(AFC_OP_RENAME_PATH, old + "\x00" + new + "\x00") return status def file_read(self, handle, sz): MAXIMUM_READ_SIZE = 1 << 16 data = "" while sz > 0: if sz > MAXIMUM_READ_SIZE: toRead = MAXIMUM_READ_SIZE else: toRead = sz try: self.dispatch_packet(AFC_OP_READ, struct.pack("<QQ", handle, toRead)) s, d = self.receive_data() except: self.lockdown = LockdownClient() self.service = self.lockdown.startService("com.apple.afc") return self.file_read(handle, sz) if s != AFC_E_SUCCESS: break sz -= toRead data += d return data def file_write(self, handle, data): MAXIMUM_WRITE_SIZE = 1 << 15 hh = struct.pack("<Q", handle) segments = len(data) / MAXIMUM_WRITE_SIZE try: for i in xrange(segments): self.dispatch_packet( AFC_OP_WRITE, hh + data[i * MAXIMUM_WRITE_SIZE:(i + 1) * MAXIMUM_WRITE_SIZE], this_length=48) s, d = self.receive_data() if s != AFC_E_SUCCESS: print "file_write error %d" % s break if len(data) % MAXIMUM_WRITE_SIZE: self.dispatch_packet(AFC_OP_WRITE, hh + data[segments * MAXIMUM_WRITE_SIZE:], this_length=48) s, d = self.receive_data() #print s,d except: self.lockdown = LockdownClient() self.service = lockdown.startService(serviceName) self.file_write(handle, data) return s def get_file_contents(self, filename): info = self.get_file_info(filename) if info: if info['st_ifmt'] == 'S_IFLNK': filename = info['LinkTarget'] if info['st_ifmt'] == 'S_IFDIR': print "%s is directory..." % filename return print "Reading %s" % filename h = self.file_open(filename) if not h: return d = self.file_read(h, int(info["st_size"])) self.file_close(h) return d return def set_file_contents(self, filename, data): h = self.file_open(filename, AFC_FOPEN_WR) if not h: return d = self.file_write(h, data) self.file_close(h) def dir_walk(self, dir, file_list=[]): d = os.path.abspath(dir) file_list = [] for file in [ file for file in self.read_directory(d) if not file in [".", ".."] ]: path = os.path.join(d, file) info = self.get_file_info(path) if info: if info['st_ifmt'] == 'S_IFDIR': file_list += self.dir_walk(path, file_list) info['path'] = path file_list.append(info) return file_list
class MobileBackup2(MobileBackup): service = None def __init__(self, lockdown=None, backupPath=None): 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.service = self.lockdown.startService("com.apple.mobilebackup2") 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 = "" 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 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, e: print e self.mobilebackup2_send_status_response(0)
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 DIAGClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.diagnostics_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def query_mobilegestalt(self, MobileGestalt=MobileGestaltKeys.split("\n")): self.service.sendPlist({ "Request": "MobileGestalt", "MobileGestaltKeys": MobileGestalt }) res = self.service.recvPlist() d = res.get("Diagnostics") if d: return d.get("MobileGestalt") return None def action(self, action="Shutdown", flags=None): self.service.sendPlist({"Request": action}) res = self.service.recvPlist() return res.get("Diagnostics") def restart(self): return self.action("Restart") def shutdown(self): return self.action("Shutdown") def diagnostics(self, diagType="All"): self.service.sendPlist({"Request": diagType}) res = self.service.recvPlist() pprint(res) if res: return res.get("Diagnostics") return None def ioregistry_entry(self, name=None, ioclass=None): d = {} if name: d["EntryName"] = name if ioclass: d["EntryClass"] = ioclass d["Request"] = "IORegistry" self.service.sendPlist(d) res = self.service.recvPlist() pprint(res) if res: return res.get("Diagnostics") return None def ioregistry_plane(self, plane=""): d = {} if plane: d["CurrentPlane"] = plane else: d["CurrentPlane"] = "" d["Request"] = "IORegistry" self.service.sendPlist(d) res = self.service.recvPlist() dd = res.get("Diagnostics") if dd: return dd.get("IORegistry") return None
class DIAGClient(object): def __init__(self, lockdown=None, serviceName="com.apple.mobile.diagnostics_relay"): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService(serviceName) self.packet_num = 0 def stop_session(self): print "Disconecting..." self.service.close() def query_mobilegestalt(self, MobileGestalt=MobileGestaltKeys): self.service.sendPlist({ "Request": "MobileGestalt", "MobileGestaltKeys": MobileGestalt }) res = self.service.recvPlist() #pprint(res) if "Diagnostics" in res: return res def action(self, action="Shutdown", flags=None): self.service.sendPlist({"Request": action}) res = self.service.recvPlist() #pprint(res) return res def restart(self): return self.action("Restart") def shutdown(self): return self.action("Shutdown") def diagnostics(self, diagType="All"): self.service.sendPlist({"Request": diagType}) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res def ioregistry_entry(self, name=None, ioclass=None): req = {"Request": "IORegistry"} if name: req["EntryName"] = name if ioclass: req["EntryClass"] = ioclass self.service.sendPlist(req) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res def ioregistry_plane(self, plane, ioclass): req = {"Request": "IORegistry", "CurrentPlane": ioclass} self.service.sendPlist(req) res = self.service.recvPlist() pprint(res) if "Diagnostics" in res: return res
class installation_proxy(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService("com.apple.mobile.installation_proxy") def watch_completion(self, handler=None, *args): while True: z = self.service.recvPlist() if not z: break completion = z.get("PercentComplete") if completion: if handler: print "calling handler" handler(completion, *args) print "%s %% Complete" % z.get("PercentComplete") if z.get("Status") == "Complete": return z.get("Status") return "Error" def send_cmd_for_bid(self, bundleID, cmd="Archive", options=None, handler=None, *args): cmd = {"Command": cmd, "ApplicationIdentifier": bundleID} if options: cmd.update(options) self.service.sendPlist(cmd) # print "%s : " % (cmd, bundleID) print "%s : %s\n" % (cmd, self.watch_completion(handler, *args)) def uninstall(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Uninstall", options, 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(self, ipaPath, options=None, handler=None, *args): return self.install_or_upgrade(ipaPath, "Install", client_options, handler, args) def upgrade(self, ipaPath, options=None, handler=None, *args): return self.install_or_upgrade(ipaPath, "Upgrade", client_options, handler, args) def apps_info(self): self.service.sendPlist({"Command": "Lookup"}) return self.service.recvPlist().get("LookupResult") def archive(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Archive", options, handler, args) def restore_archive(self, bundleID, options=None, handler=None, *args): self.send_cmd_for_bid(bundleID, "Restore", client_options, handler, args) def remove_archive(self, bundleID, options={}, handler=None, *args): self.send_cmd_for_bid(bundleID, "RemoveArchive", options, handler, args) def archives_info(self): return self.service.sendRequest({"Command": "LookupArchive"}).get("LookupResult") def search_path_for_bid(self, bid): path = None for a in self.get_apps(appTypes=["User", "System"]): if a.get("CFBundleIdentifier") == bid: path = a.get("Path") + "/" + a.get("CFBundleExecutable") return path def get_apps(self, appTypes=["User"]): return [app for app in self.apps_info().values() if app.get("ApplicationType") in appTypes] def print_apps(self, appType=["User"]): for app in self.get_apps(appType): print ( "%s : %s => %s" % ( app.get("CFBundleDisplayName"), app.get("CFBundleIdentifier"), app.get("Path") if app.get("Path") else app.get("Container"), ) ).encode("utf-8") def get_apps_bid(self, appTypes=["User"]): return [app["CFBundleIdentifier"] for app in self.get_apps() if app.get("ApplicationType") in appTypes] def close(self): self.service.close() def __del__(self): self.close()
class InstallationProxy(object): def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.service = self.lockdown.startService('com.apple.mobile.installation_proxy') def install_ipa(self, ipaPath): #Start afc service & upload ipa filename = os.path.basename(ipaPath) with AFCFile(name='/'+filename, mode='wb', afc=AFCClient(self.lockdown)) as f: f.write(open(ipaPath, 'rb').read()) self.service.sendPlist({ 'Command': 'Install', 'PackagePath': filename }) while True: response = self.service.recvPlist() if not response: break completion = response.get('PercentComplete') if completion: print 'Installing, %s: %s %% Complete' % (ipaPath, completion) status = response.get('Status') if status == 'Complete': print 'Installation %s' % status break def app_info(self): return self.service.sendRequest({'Command': 'Lookup'})['LookupResult'] def list_user_apps(self): return [[app['CFBundleIdentifier'], app.get('CFBundleDisplayName'), app.get('Container')] for app in self.app_info().values() if app.get('ApplicationType') == 'User'] def list_system_apps(self): return [[app['CFBundleIdentifier'], app.get('CFBundleDisplayName')] for app in self.app_info().values() if app.get('ApplicationType') == 'System'] def list_user_apps_BundleID(self): return [app['CFBundleIdentifier'] for app in self.app_info().values() if app.get('ApplicationType') == 'User'] def list_system_apps_BundleID(self): return [app['CFBundleIdentifier'] for app in self.app_info().values() if app.get('ApplicationType') == 'System'] def list_all_apps_BundleID(self): return self.app_info().keys() def close(self): self.service.close() def __del__(self): self.close()
class MobileBackup2Client(MobileBackupClient): def __init__(self, lockdown = None,backupPath = None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() try: self.udid = lockdown.getValue("", "UniqueDeviceID")#lockdown.udid except: self.lockdown = LockdownClient() self.udid = self.lockdown.getValue("", "UniqueDeviceID") self.service = self.lockdown.startService("com.apple.mobilebackup2") if not self.service: raise Exception("MobileBackup2Client init error : Could not start com.apple.mobilebackup2") if backupPath: self.backupPath = backupPath else: self.backupPath = "backups" #self.udid 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 = "" DLMessageVersionExchange = self.service.recvPlist() #print DLMessageVersionExchange version_major = DLMessageVersionExchange[1] self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major]) DLMessageDeviceReady = self.service.recvPlist() #print DLMessageDeviceReady if DLMessageDeviceReady and DLMessageDeviceReady[0] == "DLMessageDeviceReady": #print "Got DLMessageDeviceReady" self.version_exchange() else: raise Exception("MobileBackup2Client init error %s" % DLMessageDeviceReady) def __del__(self): if self.service: #print "Disconnecting" self.service.sendPlist(["DLMessageDisconnect", "___EmptyParameterString___"]) #print self.service.recvPlist() 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} #pprint(d) 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): #DRK s = os.statvfs(self.backupPath) freeSpace = s.f_bsize * s.f_bavail #print "freeSpage %s" % freeSpace 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 #print "Reading",self.check_filename(filename) #FIXME data = self.read_file(self.check_filename(filename)) if data != None: #print hexdump(data) print "Sending %s to device" % filename self.service.send_raw(chr(CODE_FILE_DATA) + data) self.service.send_raw(chr(CODE_SUCCESS)) else: #print "DATA %s" % hexdump(data) 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() #print device_filename, backup_filename filedata = "" while True: stuff = self.service.recv_raw() if ord(stuff[0]) == CODE_FILE_DATA: filedata += stuff[1:] elif ord(stuff[0]) == CODE_SUCCESS: #print "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 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, e: print e self.mobilebackup2_send_status_response(0)
class Syslog(object): ''' 查看系统日志 ''' def __init__(self, lockdown=None): if lockdown: self.lockdown = lockdown else: self.lockdown = LockdownClient() self.c = self.lockdown.startService("com.apple.syslog_relay") if self.c: self.c.send("watch") else: exit(1) def watch(self, watchtime, logFile=None, procName=None): '''查看日志 :param watchtime: 时间(秒) :type watchtime: int :param logFile: 日志文件完整路径 :type logFile: str :param procName: 进程名 :type proName: str ''' begin = time.strftime(TIME_FORMAT) while True: d = self.c.recv(4096) if procName: procFilter = re.compile(procName,re.IGNORECASE) if len(d.split(" ")) > 4 and not procFilter.search(d): continue s = d.strip("\n\x00\x00") print s if logFile: with open(logFile, 'a') as f: f.write(d.replace("\x00", "")) now = self.time_match(s[7:15]) if now: time_spend = self.time_caculate(str(begin), now) if time_spend > watchtime : break def time_match(self, str_time): '''判断时间格式是否匹配 ''' pattern = re.compile(r'\d{2}:\d{2}:\d{2}') match = pattern.match(str_time) if match: return str_time else: return False def time_caculate(self, a, b): ''' 计算两个字符串的时间差 ''' time_a = int(a[6:8])+60*int(a[3:5])+3600*int(a[0:2]) time_b = int(b[6:8])+60*int(b[3:5])+3600*int(b[0:2]) return time_b - time_a