def mem_dump(debugger, command, exe_ctx, result, internal_dict): command_args = shlex.split(command, posix=False) _ = exe_ctx.target _ = exe_ctx.thread if len(command_args) != 3: utils.ELOG("[usage] mem_dump outFile addr size") return outfile = command_args[0] start_addr = utils.convertToInt(command_args[1]) size = eval(command_args[2]) if not start_addr: utils.ELOG("params format error") return utils.ILOG("default address will plus main image slide") slide = utils.get_image_slide(debugger, 0) start_addr = start_addr + slide cmd = "memory read --binary --outfile {} --count {} {}".format( outfile, size, start_addr) utils.ILOG("mem dump:{}".format(cmd)) ret = utils.exe_cmd(debugger, cmd) result.AppendMessage(str(ret)) return
def debugme(debugger): utils.ILOG("start patch ptrace funtion to bypass anti debug") patch_ptrace(debugger) utils.SLOG("ptrace funtion patach done") utils.ILOG("start patch svc ins to bypass anti debug") images = utils.get_all_image_of_app() for image in images: utils.ILOG("search svc from:{}".format(image["name"])) text_addr_arr_str = get_text_segment(debugger, image["idx"]) text_addr_arr = text_addr_arr_str.strip()[1:-1].split(",") if len(text_addr_arr) < 2: utils.ELOG("failed to get text segment of image:{}" + image["name"]) continue text_start = text_addr_arr[0] text_end = text_addr_arr[1] utils.ILOG("text start:{} end:{}".format(text_start, text_end)) svc_arr_str = lookup_svc_insn(debugger, text_start, text_end) if "<object returned empty description>" in svc_arr_str: utils.ILOG("not found svc ins, so don't need patch") continue svc_arr = svc_arr_str.strip() svc_arr = svc_arr.split() if len(svc_arr) < 1: utils.ELOG("not found svc ins, so don't need patch") continue for svc_addr in svc_arr: utils.ILOG("start hook svc at address:{}".format(svc_addr)) ret = xia0Hook(debugger, svc_addr) if ret: utils.SLOG("hook svc at address:{} done".format(svc_addr)) return
def mr(debugger, command, exe_ctx, result, internal_dict): command_args = shlex.split(command, posix=False) _ = exe_ctx.target _ = exe_ctx.thread if len(command_args) != 2: utils.ELOG("[usage] mr addr count") return start_addr = utils.convertToInt(command_args[0]) size = eval(command_args[1]) if not start_addr: utils.ELOG("params format error") return # utils.ILOG("default address will plus main image slide") # slide = utils.get_image_slide(debugger, 0) # start_addr = start_addr + slide cmd = "memory read {} --count {}".format(start_addr, size) utils.ILOG("mem read:{}".format(cmd)) ret = utils.exe_cmd(debugger, cmd) result.AppendMessage(str(ret)) return
def debugme(debugger): utils.ILOG("start patch ptrace funtion to bypass antiDebug") patch_ptrace(debugger) utils.SLOG("success ptrace funtion to bypass antiDebug") utils.ILOG("start patch svc ins to bypass antiDebug") # get text segment start/end address ret = get_text_segment(debugger) # remove " \n" ret = ret.strip() ret = ret[1:-1] textAddrs = ret.split(',') if len(textAddrs) < 2: utils.ELOG("failed to get text segment:" + str(textAddrs)) return textStart = ret.split(',')[0] textEnd = ret.split(',')[1] textStart = textStart.strip() textEnd = textEnd.strip() utils.SLOG("get text segment start address:{} and end address:{}".format( textStart, textEnd)) # lookup svc ins go through all text code ret = lookup_svc_insn(debugger, textStart, textEnd) if "<object returned empty description>" in ret: utils.ILOG("not found svc ins, so don't need patch") return svcAddrs = ret.strip() svcAddrs = svcAddrs.split() if len(svcAddrs) < 1: utils.ELOG("not found svc ins, so don't need patch") return for svcAddr in svcAddrs: utils.SLOG("found svc address:{}".format(svcAddr)) utils.ILOG("start hook svc at address:{}".format(svcAddr)) ret = xia0Hook(debugger, svcAddr) if ret: utils.SLOG("success hook svc at address:{}".format(svcAddr)) utils.ILOG("all patch done") return
def save_image(debugger, command, exe_ctx, result, internal_dict): command_args = shlex.split(command, posix=False) _ = exe_ctx.target _ = exe_ctx.thread if len(command_args) < 1: utils.ELOG("[usage] save_image UIImageObj") return image_obj_addr = command_args[0] script = '@import Foundation;' script += "UIImage* image = (UIImage*){}".format(image_obj_addr) script += ''' NSString* ret = @"DONE"; if (image != nil){ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString* path = [documentsDirectory stringByAppendingPathComponent: @"xia0.gif" ]; NSData* data = UIImagePNGRepresentation(image); [data writeToFile:path atomically:YES]; } ret ''' ret = utils.exe_script(debugger, script) result.AppendMessage(str(ret)) return
def methods(debugger, command, exe_ctx, result, internal_dict): def generate_option_parser(): usage = "usage: xmethods" parser = optparse.OptionParser(usage=usage, prog="lookup") parser.add_option("-n", "--name", action="store", default=None, dest="name", help="set the class name for methods") return parser command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return _ = exe_ctx.target _ = exe_ctx.thread if options.name: clzname = options.name try: clzname = re.search("^\"(.*)\"$", clzname).group(1) except: utils.ELOG("input format error! need \"class name\"") return utils.ILOG("will get methods for class:\"{}\"".format(clzname)) code = ''' Class clz = objc_getClass(\"{}\"); id ret = [clz _shortMethodDescription]; ret '''.format(clzname) ret = utils.exe_script(debugger, code) result.AppendMessage(ret) return result clz = args[0] code = ''' id ret = [{} _shortMethodDescription]; ret '''.format(clz) ret = utils.exe_script(debugger, code) result.AppendMessage(ret) return result
def get_module_info_by_name(debugger, moduleName): command_script = '@import Foundation;NSString* moduleName = @"' + moduleName + '";' command_script += r''' NSMutableString* retStr = [NSMutableString string]; uint32_t count = (uint32_t)_dyld_image_count(); for(uint32_t i = 0; i < count; i++){ char* curModuleName_cstr = (char*)_dyld_get_image_name(i); long slide = (long)_dyld_get_image_vmaddr_slide(i); uintptr_t baseAddr = (uintptr_t)_dyld_get_image_header(i); NSString* curModuleName = @(curModuleName_cstr); if([curModuleName containsString:moduleName]) { [retStr appendString:@"\n=======\nModule Path : "]; [retStr appendString:@(curModuleName_cstr)]; [retStr appendString:@"\nModule Silde: "]; [retStr appendString:(id)[@(slide) stringValue]]; [retStr appendString:@"\nModule base : "]; [retStr appendString:(id)[@(baseAddr) stringValue]]; } } retStr ''' retStr = utils.exe_script(debugger, command_script) if "error" in retStr: utils.ELOG("something error in OC script # " + retStr.strip()) utils.ILOG("so use command to get info") ret = utils.exe_cmd(debugger, "im li -o -f") pattern = ".*" + moduleName.replace("\"", "") match = re.search(pattern, ret) # TODO: more strict if match: found = match.group(0) else: utils.ELOG("not found image:" + moduleName) return return found return utils.hex_int_in_str(retStr)
def handle_command(debugger, command, exe_ctx, result, internal_dict): command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, _) = parser.parse_args(command_args) except: result.SetError(parser.usage) return _ = exe_ctx.target _ = exe_ctx.thread if options.patchInstrument: if options.patchAddress: patch_addr = int(options.patchAddress, 16) else: ret = utils.exe_cmd(debugger, "p/x $pc") ret = ret.strip() pattern = '0x[0-9a-f]+' match = re.search(pattern, ret) if match: found = match.group(0) else: utils.ELOG("not get address:" + ret) return utils.ILOG( "you not set patch address, default is current pc address:{}". format(found)) patch_addr = int(found, 16) patch_ins = options.patchInstrument # default instrument size is 1 patch_size = 0x1 patch_ins = patch_ins.replace("\"", "") patch_ins = patch_ins.replace("'", "") if options.patchSize: patch_size = int(options.patchSize) ret = patcher(debugger, patch_ins, patch_addr, patch_size) result.AppendMessage(str(ret)) else: result.AppendMessage("[-] args error, check it !") return
def patcher(debugger, ins, addr, size): if is_raw_data(ins): utils.ILOG("detect you manual set ins data:{}".format(ins)) utils.ILOG( "start patch text at address:{} size:{} to ins data:{}".format( hex(addr), size, ins)) patch_code(debugger, hex(addr), ins, size) return "[x] power by xia0@2019" supportInsList = { 'nop': '0x1f, 0x20, 0x03, 0xd5 ', 'ret': '0xc0, 0x03, 0x5f, 0xd6', 'mov0': '0x00, 0x00, 0x80, 0xd2', 'mov1': '0x20, 0x00, 0x80, 0xd2' } if ins not in supportInsList.keys(): utils.ELOG("patcher not support this ins type:{}".format(ins)) return "[x] power by xia0@2019" utils.ILOG( "start patch text at address:{} size:{} to ins:\"{}\" and data:{}". format(hex(addr), size, ins, supportInsList[ins])) # for i in range(size): # patch_code(debugger, hex(curPatchAddr), supportInsList[ins]) # utils.SLOG("current patch address:{} patch done".format(hex(curPatchAddr))) # curPatchAddr += 4 ins_data = "" for i in range(size): ins_data += supportInsList[ins] if i != size - 1: ins_data += "," build_ins_data = "{" + ins_data + "}" utils.ILOG("make ins data:\n{}".format(build_ins_data)) patch_code(debugger, hex(addr), build_ins_data, size) utils.SLOG("patch done") return "[x] power by xia0@2019"
def dumpdecrypted(debugger, modulePath=None, moduleIdx=None): # must delete all breakpoints. utils.ILOG("delete all breakpoints") utils.exe_cmd(debugger, "br de -f") #dump_macho_to_file(debugger,) if modulePath and moduleIdx: print(dump_macho_to_file(debugger, moduleIdx, modulePath)) else: mainImagePath = get_main_image_path(debugger) appDir = os.path.dirname(mainImagePath) appImagesStr = get_all_image_of_app(debugger, appDir) appImagesArr = appImagesStr.split("#") for imageInfo in appImagesArr: if not imageInfo or not "," in imageInfo: utils.ELOG("image info is null, skip image # " + imageInfo) continue utils.ILOG("now is image: " + imageInfo) info = imageInfo.split(",") if len(info) == 2: utils.ILOG("start dump [" + info[0] + "] image:" + info[1]) # print "idx:" + info[0] # print "path:" + info[1] if info[1] == mainImagePath: entryAddrStr = get_macho_entry_offset(debugger) entryAddr_int = int(entryAddrStr.strip()[1:-1], 16) utils.SLOG("fix main addr:" + hex(entryAddr_int)) print( dump_macho_to_file(debugger, info[0], info[1], entryAddr_int)) continue print(dump_macho_to_file(debugger, info[0], info[1])) return '\n\n[*] Developed By xia0@2019'
def xbr(debugger, command, result, dict): raw_args = create_command_arguments(command) command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return # check is options? if options.address: targetAddr = options.address if targetAddr.startswith("0x"): targetAddr_int = int(targetAddr, 16) else: targetAddr_int = int(targetAddr, 10) utils.ILOG("breakpoint at address:{}".format(hex(targetAddr_int))) lldb.debugger.HandleCommand ('breakpoint set --address %d' % targetAddr_int) return if options.entryAddress: if options.entryAddress == "main": entryAddrStr = get_macho_entry_offset(debugger) entryAddr_int = int(entryAddrStr.strip()[1:-1], 16) utils.ILOG("breakpoint at main function:{}".format(hex(entryAddr_int))) lldb.debugger.HandleCommand ('breakpoint set --address %d' % entryAddr_int) elif options.entryAddress == "init": initFunAddrStr = get_macho_mod_init_first_func(debugger) initFunAddr_int = int(initFunAddrStr.strip()[1:-1], 16) utils.ILOG("breakpoint at mod int first function:{}".format(hex(initFunAddr_int))) lldb.debugger.HandleCommand ('breakpoint set --address %d' % initFunAddr_int) elif options.entryAddress == "load": ret = get_all_class_plus_load_methods(debugger) if "<object returned empty description>" in ret: utils.ILOG("not found +[* load] method") return all_load_addrs_str_arr = ret.strip().split(",") all_load_addrs = [] for addr in all_load_addrs_str_arr: if addr != "": all_load_addrs.append(int(addr, 10)) utils.ILOG("will set breakpoint at all +[* load] methold, count:{}".format(len(all_load_addrs))) for addr in all_load_addrs: lldb.debugger.HandleCommand ('breakpoint set --address %d' % addr) utils.SLOG("set br at:{}".format(hex(addr))) # utils.ILOG("load:\n{}\n".format([hex(addr) for addr in all_load_addrs])) else: utils.ELOG("you should special the -E options:[main/init/load]") return # check is arg is address ? mean auto add slide if is_just_address_cmd(args): if options.modulePath: modulePath = options.modulePath utils.ILOG("you special the module:" + modulePath) else: utils.ILOG("you not special the module, default is main module") modulePath = None targetAddr = args[0] if targetAddr.startswith("0x"): targetAddr_int = int(targetAddr, 16) else: targetAddr_int = int(targetAddr, 10) moduleSlide = get_process_module_slide(debugger, modulePath) if "error" in moduleSlide: utils.ELOG("error in oc script # " + moduleSlide.strip()) if modulePath: targetImagePath = modulePath else: mainImagePath = get_main_image_path(debugger) if "no value available" in mainImagePath or "error" in mainImagePath: ret = utils.exe_cmd(debugger, "target list") # pylint: disable=anomalous-backslash-in-string pattern = '/.*\(' match = re.search(pattern, ret) # TODO: more strict if match: found = match.group(0) found = found.split("(")[0] found = found.strip() else: utils.ELOG("failed to auto get main module, use -m option") return mainImagePath = found print("[+] use \"target list\" to get main module:" + mainImagePath) else: mainImagePath = mainImagePath.strip()[1:-1] targetImagePath = mainImagePath ret = utils.exe_cmd(debugger, "image list -o -f") pattern = '0x.*?' + targetImagePath.replace("\"", "") match = re.search(pattern, ret) # TODO: more strict if match: found = match.group(0) else: utils.ELOG("not found image:"+targetImagePath) return moduleSlide = found.split()[0] utils.ILOG("use \"image list -o -f\" cmd to get image slide:"+moduleSlide) moduleSlide = int(moduleSlide, 16) else: moduleSlide = int(moduleSlide, 10) brAddr = moduleSlide + targetAddr_int utils.ILOG("ida's address:{} module slide:{} target breakpoint address:{}".format(hex(targetAddr_int), hex(moduleSlide), hex(brAddr))) lldb.debugger.HandleCommand ('breakpoint set --address %d' % brAddr) return # check is breakpoint at all methods address(IMP) for given classname if is_br_all_cmd_x(args): classname = args[0] begin = classname.find('$') end = classname.rfind('$') classname = classname[begin+1 : end] utils.ILOG("classname:{}".format(classname)) ret = get_all_method_address_of_class(debugger, classname) addrArr = ret.split('-')[:-1] for addr in addrArr: address = int(addr) if address: lldb.debugger.HandleCommand ('breakpoint set --address %x' % address) result.AppendMessage("Set %ld breakpoints of %s" % (len(addrArr),classname)) return if is_br_all_cmd(args): classname = args[0] ret = get_all_method_address_of_class(debugger, classname) addrArr = ret.split('-')[:-1] for addr in addrArr: address = int(addr) if address: lldb.debugger.HandleCommand ('breakpoint set --address %x' % address) result.AppendMessage("Set %ld breakpoints of %s" % (len(addrArr),classname)) return if not is_command_valid(raw_args): print('please specify the param, for example: "-[UIView initWithFrame:]"') return arg_ = raw_args[0] class_name = get_class_name(arg_) method_name = get_method_name(arg_) # xlog = 'className:'+ str(class_name) + '\tmethodName:' + str(method_name) utils.ILOG("className:{} methodName:{}".format(class_name, method_name)) # print class_name, method_name address = 0 if is_class_method(arg_): address = get_class_method_address(class_name, method_name) else: address = get_instance_method_address(class_name, method_name) utils.SLOG('found method address:0x%x' % address) if address: lldb.debugger.HandleCommand ('breakpoint set --address %x' % address) else: utils.ELOG("fail, please check the arguments")
def objc_dump_protocol(debugger, protocol_name): command_script = '@import Foundation;const char * name = (const char *)\"' + protocol_name + '\";' command_script += r''' NSMutableString* retStr = [NSMutableString string]; struct objc_method_description { SEL _Nullable name; /**< The name of the method */ char * _Nullable types; /**< The types of the method arguments */ }; unsigned int protocolCount; Protocol * * __protocols = (Protocol **)objc_copyProtocolList (&protocolCount); for (int i = 0; i < protocolCount; i++) { const char *protocolName = (const char * )protocol_getName (__protocols[i]); if (strcmp(name, protocolName) == 0) { unsigned int adopteeCount; Protocol ** adoptees = (Protocol **)protocol_copyProtocolList (__protocols[i], &adopteeCount); free (adoptees); struct objc_method_description *methods; unsigned int count; unsigned int requiredCount = 0; unsigned int optionalCount = 0; methods = (struct objc_method_description *)protocol_copyMethodDescriptionList (__protocols[i], YES, YES, &count); for (int i = 0; i < count; i++) { [retStr appendString:@"-"]; [retStr appendString:@","]; [retStr appendString:NSStringFromSelector(methods[i].name)]; [retStr appendString:@","]; [retStr appendString:[NSString stringWithUTF8String:methods[i].types]]; [retStr appendString:@"||"]; } requiredCount += count; free (methods); methods = (struct objc_method_description *)protocol_copyMethodDescriptionList (__protocols[i], YES, NO, &count); for (int i = 0; i < count; i++) { [retStr appendString:@"+"]; [retStr appendString:@","]; [retStr appendString:NSStringFromSelector(methods[i].name)]; [retStr appendString:@","]; [retStr appendString:[NSString stringWithUTF8String:methods[i].types]]; [retStr appendString:@"||"]; } requiredCount += count; free (methods); methods = (struct objc_method_description *)protocol_copyMethodDescriptionList (__protocols[i], NO, YES, &count); for (int i = 0; i < count; i++) { [retStr appendString:@"-"]; [retStr appendString:@","]; [retStr appendString:NSStringFromSelector(methods[i].name)]; [retStr appendString:@","]; [retStr appendString:[NSString stringWithUTF8String:methods[i].types]]; [retStr appendString:@"||"]; } optionalCount += count; free (methods); methods = (struct objc_method_description *)protocol_copyMethodDescriptionList (__protocols[i], NO, NO, &count); for (int i = 0; i < count; i++) { [retStr appendString:@"+"]; [retStr appendString:@","]; [retStr appendString:NSStringFromSelector(methods[i].name)]; [retStr appendString:@","]; [retStr appendString:[NSString stringWithUTF8String:methods[i].types]]; [retStr appendString:@"||"]; } optionalCount += count; free (methods); break; } } free (__protocols); retStr ''' retStr = utils.exe_script(debugger, command_script) arr = retStr.strip().split("||") retArr = [] for item in arr: if len(item) <= 0: continue protocolInfo = item.split(",") if len(protocolInfo) != 3: utils.ELOG("Error for protocolInfo") break retArr.append([protocolInfo[0], protocolInfo[1], protocolInfo[2]]) return retArr
def xmethods(debugger, command, exe_ctx, result, internal_dict): def generate_option_parser(): usage = "usage: xmethods" parser = optparse.OptionParser(usage=usage, prog="lookup") parser.add_option("-a", "--address", action="store", default=None, dest="address", help="set a breakpoint at absolute address") return parser command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return _ = exe_ctx.target _ = exe_ctx.thread def is_address(args): if len(args) == 0: return False arg = args[0] if len(arg) == 0: return False ret = re.match('^0x[0-9a-fA-F]+$', arg) if not ret: return False return True clz = args[0] obj = clz if is_address(args): clz = objc_obj_name(debugger, args[0]) ret = objc_dump_methods(debugger, clz) utils.ILOG("Dump methods for {}({})".format(obj, clz)) # print(ret) for method in ret: if len(method) < 4: utils.ELOG("Error method!") break addr = hex(utils.convertToInt(method[2])) retType = method[3] retTypeList = objc_parse_typesign(method[3]) if retTypeList and len(retTypeList) > 0: retType = retTypeList[0] selname = method[1] argCount = len(method) - 4 if argCount > 0: global methodArgIdx arr = method[4:] methodArgIdx = 0 def handler(reobj): global methodArgIdx r = reobj.group(0) argType = arr[methodArgIdx] argTypeList = objc_parse_typesign(argType) if argTypeList and len(argTypeList) > 0: argType = argTypeList[0] r = r + "(" + argType + ")" + "a" + str(methodArgIdx) + " " methodArgIdx = methodArgIdx + 1 return r selname = re.sub(":", handler, selname, flags=0) line = "\t{} ({}){};// {}".format(method[0], retType, selname, addr) print(line) # result.AppendMessage("command is still developing. please wait...\n") return parser
def pblock(debugger, command, exe_ctx, result, internal_dict): command_args = shlex.split(command, posix=False) _ = exe_ctx.target _ = exe_ctx.thread block_addr_raw = command_args[0] block_addr = utils.convertToInt(block_addr_raw) if block_addr: utils.ILOG("block addr:{}".format(hex(block_addr))) else: utils.ELOG("block addr format err:{}".format(block_addr_raw)) return header = ''' enum { BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code BLOCK_IS_GLOBAL = (1 << 28), BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30), }; struct Block_literal_1 { void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor_1 { unsigned long int reserved; // NULL unsigned long int size; // sizeof(struct Block_literal_1) // optional helper functions void (*copy_helper)(void *dst, void *src); // IFF (1<<25) void (*dispose_helper)(void *src); // IFF (1<<25) // required ABI.2010.3.16 const char *signature; // IFF (1<<30) } *descriptor; // imported variables }; ''' code = header code += 'struct Block_literal_1 real = *((struct Block_literal_1 *)(void*){});'.format( block_addr) code += ''' NSString* ret = @""; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"]; #if 0 if (real.flags & BLOCK_HAS_SIGNATURE) { char *signature; if (real.flags & BLOCK_HAS_COPY_DISPOSE) { signature = (char *)(real.descriptor)->signature; } else { signature = (char *)(real.descriptor)->copy_helper; } NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:signature]; NSMutableArray *types = [NSMutableArray array]; [types addObject:[NSString stringWithUTF8String:(char *)[sig methodReturnType]]]; for (NSUInteger i = 0; i < sig.numberOfArguments; i++) { char *type = (char *)[sig getArgumentTypeAtIndex:i]; [types addObject:[NSString stringWithUTF8String:type]]; } [dict setObject:types forKey:@"signature"]; } NSMutableArray* sigArr = dict[@"signature"]; if(!sigArr){ ret = [NSString stringWithFormat:@"Imp: 0x%lx", [dict[@"invoke"] longValue]]; }else{ NSMutableString* sig = [NSMutableString stringWithFormat:@"%@ ^(", decode(sigArr[0])]; for (int i = 2; i < sigArr.count; i++) { if(i == sigArr.count - 1){ [sig appendFormat:@"%@", decode(sigArr[i])]; }else{ [sig appendFormat:@"%@ ,", decode(sigArr[i])]; } } [sig appendString:@");"]; ret = [NSString stringWithFormat:@"Imp: 0x%lx Signature: %s", [dict[@"invoke"] longValue], [sig UTF8String]]; } ret #else dict #endif ''' ret = utils.exe_script(debugger, code) print(ret)