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_all_class_plus_load_methods(debugger): command_script = '@import Foundation;' command_script += r''' NSMutableString* retStr = [NSMutableString string]; unsigned int c_size = 0; const char *path = (char *)[[[NSBundle mainBundle] executablePath] UTF8String]; const char **allClasses = (const char **)objc_copyClassNamesForImage(path, &c_size); for (int i = 0; i < c_size; i++) { Class cls = objc_getClass(allClasses[i]); unsigned int cm_size = 0; struct objc_method **classMethods = (struct objc_method **)class_copyMethodList((Class)objc_getMetaClass((const char *)class_getName(cls)), &cm_size); for (int k = 0; k < cm_size; k++) { struct objc_method * meth = classMethods[k]; id implementation = (id)method_getImplementation(meth); NSString* cm_name = NSStringFromSelector((SEL)method_getName(meth)); if([cm_name isEqualToString:@"load"]){ [retStr appendString:(id)[@((uintptr_t)implementation) stringValue]]; [retStr appendString:@","]; } } free(classMethods); } free(allClasses); retStr ''' return utils.exe_script(debugger, command_script)
def get_main_image_macho_header(debugger): command_script = r''' typedef integer_t cpu_type_t; typedef integer_t cpu_subtype_t; typedef integer_t cpu_threadtype_t; struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; struct mach_header_64* header = (struct mach_header_64*)_dyld_get_image_header(0); uint64_t header_int = (uint64_t)header; header_int ''' # is in executable path? ret = utils.exe_script(debugger, command_script) return hex(int(ret, 10))
def is_main_module_from_address(target, debugger, address): # get moduleName of address addr = target.ResolveLoadAddress(address) moduleName = addr.module.file.basename modulePath = str(addr.module.file) # get executable path getExecutablePathScript = r''' const char *path = (char *)[[[NSBundle mainBundle] executablePath] UTF8String]; path ''' # is in executable path? path = utils.exe_script(debugger, getExecutablePathScript) appDir = os.path.dirname(path.strip()[1:-1]) if not moduleName or not str(path): return False if appDir in modulePath: return True else: return False if moduleName in str(path): return True else: return False
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 get_address_info_by_address(debugger, address): command_script = 'void * targetAddr = (void*)' + address + ';' command_script += r''' NSMutableString* retStr = [NSMutableString string]; typedef struct dl_info { const char *dli_fname; /* Pathname of shared object */ void *dli_fbase; /* Base address of shared object */ const char *dli_sname; /* Name of nearest symbol */ void *dli_saddr; /* Address of nearest symbol */ } Dl_info; Dl_info dl_info; dladdr(targetAddr, &dl_info); char* module_path = (char*)dl_info.dli_fname; uintptr_t module_base = (uintptr_t)dl_info.dli_fbase; char* symbol_name = (char*)dl_info.dli_sname; uintptr_t symbol_addr = (uintptr_t)dl_info.dli_saddr; [retStr appendString:@"Module path: "]; [retStr appendString:@(module_path)]; [retStr appendString:@"\nModule base: "]; [retStr appendString:(id)[@(module_base) stringValue]]; long slide = 0; NSString* targetModulePath = @(module_path); 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); slide = (long)_dyld_get_image_vmaddr_slide(i); uintptr_t baseAddr = (uintptr_t)_dyld_get_image_header(i); NSString* curModuleName = @(curModuleName_cstr); if([curModuleName isEqualToString:targetModulePath]) { [retStr appendString:@"\nModule slide: "]; [retStr appendString:(id)[@(slide) stringValue]]; break; } } [retStr appendString:@"\ntarget addr: "]; [retStr appendString:(id)[@((uintptr_t)targetAddr) stringValue]]; uintptr_t target_file_addr = (uintptr_t)((uint64_t)targetAddr - slide); [retStr appendString:@"\nFile addr: "]; [retStr appendString:(id)[@(target_file_addr) stringValue]]; [retStr appendString:@"\nSymbol name: "]; [retStr appendString:@(symbol_name)]; [retStr appendString:@"\nSymbol addr: "]; [retStr appendString:(id)[@(symbol_addr) stringValue]]; retStr ''' retStr = utils.exe_script(debugger, command_script) return utils.hex_int_in_str(retStr)
def get_main_image_path(debugger): command_script = r''' const char *path = (char *)[[[NSBundle mainBundle] executablePath] UTF8String]; path ''' # is in executable path? ret = utils.exe_script(debugger, command_script) ret = ret.strip() return ret[1:-1]
def get_func_info_by_name(debugger, funcName): command_script = 'const char * func_name = "' + funcName + '";' command_script += r''' NSMutableString* retStr = [NSMutableString string]; #define RTLD_LAZY 0x1 #define RTLD_NOW 0x2 #define RTLD_LOCAL 0x4 #define RTLD_GLOBAL 0x8 typedef struct dl_info { const char *dli_fname; /* Pathname of shared object */ void *dli_fbase; /* Base address of shared object */ const char *dli_sname; /* Name of nearest symbol */ void *dli_saddr; /* Address of nearest symbol */ } Dl_info; Dl_info dl_info; void* handle = (void*)dlopen(0, RTLD_GLOBAL | RTLD_NOW); void* target_ptr = (void*)dlsym(handle, func_name); if(target_ptr){ uintptr_t target_addr = (uintptr_t)target_ptr; dladdr(target_ptr, &dl_info); char* module_path = (char*)dl_info.dli_fname; uintptr_t module_base = (uintptr_t)dl_info.dli_fbase; char* symbol_name = (char*)dl_info.dli_sname; uintptr_t symbol_addr = (uintptr_t)dl_info.dli_saddr; [retStr appendString:@"Func name: "]; [retStr appendString:@((char*)func_name)]; [retStr appendString:@"\nFunc addr: "]; [retStr appendString:(id)[@(target_addr) stringValue]]; [retStr appendString:@"\nModule Path: "]; [retStr appendString:@(module_path)]; [retStr appendString:@"\nModule base: "]; [retStr appendString:(id)[@(module_base) stringValue]]; [retStr appendString:@"\nSymbol name: "]; [retStr appendString:@(symbol_name)]; [retStr appendString:@"\nSymbol addr: "]; [retStr appendString:(id)[@(symbol_addr) stringValue]]; }else{ [retStr appendString:@"[-] dlsym not found symbol:"]; [retStr appendString:@((char*)func_name)]; } retStr ''' retStr = utils.exe_script(debugger, command_script) return utils.hex_int_in_str(retStr)
def objc_obj_name(debugger, obj_addr): command_script = '@import Foundation;NSObject* obj = (NSObject*)' + obj_addr + ';' command_script += r''' Class clz = [obj class]; const char * clz_name = (const char *)class_getName(clz); NSString* clzName = [NSString stringWithUTF8String:clz_name]; clzName ''' retStr = utils.exe_script(debugger, command_script) return str(retStr.strip())
def get_userdefaults_info_by_key(debugger, key): command_script = r''' NSArray *keys = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]; NSArray *values = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allValues]; NSMutableDictionary *retDic = [NSMutableDictionary dictionaryWithObjects:values forKeys:keys] retDic ''' return utils.exe_script(debugger, command_script)
def get_main_image_path(debugger): command_script = '@import Foundation;' command_script += r''' // const char *path = (char *)[[[NSBundle mainBundle] executablePath] UTF8String]; id bundle = objc_msgSend((Class)objc_getClass("NSBundle"), @selector(mainBundle)); id exePath = objc_msgSend((id)bundle, @selector(executablePath)); const char *path = (char *)objc_msgSend((id)exePath, @selector(UTF8String)); path ''' retStr = utils.exe_script(debugger, command_script) return retStr
def objc_dump_ivars(debugger, obj_addr): command_script = '@import Foundation;NSObject* obj = (NSObject*)' + obj_addr + ';' command_script += r''' NSMutableString* retStr = [NSMutableString string]; typedef struct objc_ivar *Ivar; Class clz = [obj class]; unsigned int count = 0; Ivar *vars = (Ivar *)class_copyIvarList(clz, &count); for (int i=0; i<count; i++) { Ivar var = vars[i]; long offset = (long)ivar_getOffset(var); NSString* varName = [NSString stringWithUTF8String:(const char *)ivar_getName(var)]; NSString* varTypeStr = [NSString stringWithUTF8String:(const char *)ivar_getTypeEncoding(var)]; //NSString* dumpStr = [NSString stringWithFormat:@"-> %@ %@; // %p -> %p", ParseTypeString(varTypeStr)[0], varName, varAddr, *varAddr]; void** varAddr = (void**)((unsigned char *)(__bridge void *)obj + offset); [retStr appendString:varName]; [retStr appendString:@","]; [retStr appendString:varTypeStr]; [retStr appendString:@","]; [retStr appendString:(id)[@((long)varAddr) stringValue]]; [retStr appendString:@"||"]; } retStr ''' retStr = utils.exe_script(debugger, command_script) arr = retStr.strip().split("||") retArr = [] for item in arr: if len(item) <= 0: continue info = item.split(",") if len(info) != 3: continue retArr.append([info[0], info[1], hex(utils.convertToInt(info[2]))]) return retArr
def get_all_image_of_app(debugger, appDir): command_script = '@import Foundation;NSString* appDir = @"' + appDir + '";' 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:appDir]) { [retStr appendString:(id)[@(i) stringValue]]; [retStr appendString:@","]; [retStr appendString:@(curModuleName_cstr)]; [retStr appendString:@"#"]; } } retStr ''' ret = utils.exe_script(debugger, command_script) return ret
def get_all_method_address_of_class(debugger, classname): command_script = '@import Foundation;const char* className = "' + classname + '";' command_script += r''' //NSMutableArray *mAddrArr = [NSMutableArray array]; NSMutableString* retStr = [NSMutableString string]; unsigned int m_size = 0; Class cls = objc_getClass(className); struct objc_method ** metholds = (struct objc_method **)class_copyMethodList(cls, &m_size); for (int j = 0; j < m_size; j++) { struct objc_method * meth = metholds[j]; id implementation = (id)method_getImplementation(meth); NSString* m_name = NSStringFromSelector((SEL)method_getName(meth)); //[mAddrArr addObject:(id)[@((uintptr_t)implementation) stringValue]]; NSNumber* implementationNum = [NSNumber numberWithUnsignedLongLong:(uintptr_t)implementation]; [retStr appendString:(id)[implementationNum stringValue]]; [retStr appendString:@"-"]; } unsigned int cm_size = 0; struct objc_method **classMethods = (struct objc_method **)class_copyMethodList((Class)objc_getMetaClass((const char *)class_getName(cls)), &cm_size); for (int k = 0; k < cm_size; k++) { struct objc_method * meth = classMethods[k]; id implementation = (id)method_getImplementation(meth); NSString* cm_name = NSStringFromSelector((SEL)method_getName(meth)); //[mAddrArr addObject:(id)[@((uintptr_t)implementation) stringValue]]; NSNumber* implementationNum = [NSNumber numberWithUnsignedLongLong:(uintptr_t)implementation]; [retStr appendString:(id)[implementationNum stringValue]]; [retStr appendString:@"-"]; } retStr ''' retStr = utils.exe_script(debugger, command_script) return retStr
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 get_process_module_slide(debugger, modulePath): command_script = '@import Foundation;' command_script += r''' uint32_t count = (uint32_t)_dyld_image_count(); NSMutableString* retStr = [NSMutableString string]; uint32_t idx = 0; NSString* image_name = @""; const char *path = (char *)[[[NSBundle mainBundle] executablePath] UTF8String]; ''' if modulePath: command_script += 'NSString* modulePath = @"{}"\n'.format(modulePath) else: command_script += 'NSString* modulePath = [[NSString alloc] initWithUTF8String:path];' command_script += r''' NSString* imagePath = modulePath; for(uint32_t i = 0; i < count; i++){ const char* imageName = (const char*)_dyld_get_image_name(i); NSString* imageNameStr = [[NSString alloc] initWithUTF8String:imageName]; if([imageNameStr isEqualToString:imagePath]){ idx = i; image_name = imageNameStr; break; } } uintptr_t slide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx); NSString *slideStr = [@(slide) stringValue]; [retStr appendString:image_name]; [retStr appendString:@"#"]; [retStr appendString:slideStr]; slideStr ''' slide = utils.exe_script(debugger, command_script) return slide
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 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)
def symbolish_stack_trace_frame(debugger, target, thread): frame_string = '' idx = 0 for f in thread.frames: function = f.GetFunction() # mem address load_addr = f.addr.GetLoadAddress(target) if not function: # file address file_addr = f.addr.GetFileAddress() # offset start_addr = f.GetSymbol().GetStartAddress().GetFileAddress() symbol_offset = file_addr - start_addr modulePath = str(f.addr.module.file) # is_main_module_from_address? findname : symbol name if is_main_module_from_address(target, debugger, load_addr): if idx + 2 == len(thread.frames): metholdName = 'main + ' + str(symbol_offset) else: command_script = find_symbol_from_address_script( load_addr, modulePath) one = utils.exe_script(debugger, command_script) # is set the block file path if BLOCK_JSON_FILE and len(BLOCK_JSON_FILE) > 0: two = find_block_symbol_from_adress(file_addr) response = choose_best(one, two) else: response = one response = check_if_analysis_error(response) metholdName = str(response).replace("\n", "") frame_string += ' frame #{num}: [file:{f_addr} mem:{m_addr}] {mod}`{symbol}\n'.format( num=idx, f_addr=colorme.attr_str(str(hex(file_addr)), 'cyan'), m_addr=colorme.attr_str(hex(load_addr), 'grey'), mod=colorme.attr_str(str(f.addr.module.file.basename), 'yellow'), symbol=colorme.attr_str(metholdName, 'green')) else: metholdName = f.addr.symbol.name frame_string += ' frame #{num}: [file:{f_addr} mem:{m_addr}] {mod}`{symbol} + {offset} \n'.format( num=idx, f_addr=colorme.attr_str(str(hex(file_addr)), 'cyan'), m_addr=colorme.attr_str(hex(load_addr), 'grey'), mod=colorme.attr_str(str(f.addr.module.file.basename), 'yellow'), symbol=metholdName, offset=symbol_offset) else: frame_string += ' frame #{num}: {addr} {mod}`{func} at {file}\n'.format( num=idx, addr=hex(load_addr), mod=colorme.attr_str(str(f.addr.module.file.basename), 'yellow'), func='%s [inlined]' % function if f.IsInlined() else function, file=f.addr.symbol.name) idx = idx + 1 return frame_string
def dump_macho_to_file(debugger, machoIdx, machoPath, fix_addr=0): command_script_header = r''' // header #define MH_MAGIC 0xfeedface /* the mach magic number */ #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ #define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ #define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ #define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */ #define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */ #define FAT_MAGIC 0xcafebabe #define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */ #define O_RDONLY 0x0000 /* open for reading only */ #define O_WRONLY 0x0001 /* open for writing only */ #define O_RDWR 0x0002 /* open for reading and writing */ #define O_ACCMODE 0x0003 /* mask for above modes */ #define SEEK_CUR 1 /* set file offset to current plus offset */ #define SEEK_SET 0 #define O_CREAT 0x0200 /* create if nonexistant */ #define O_TRUNC 0x0400 /* truncate to zero length */ #define O_EXCL 0x0800 /* error if already exists */ #define SEEK_END 2 // #define errno (*__error()) typedef long long __int64_t; typedef __int64_t __darwin_off_t; /* [???] Used for file sizes */ typedef __darwin_off_t off_t; typedef int integer_t; typedef integer_t cpu_type_t; typedef integer_t cpu_subtype_t; typedef integer_t cpu_threadtype_t; #define swap32(value) (((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24) ) struct mach_header { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ }; struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ }; struct encryption_info_command { uint32_t cmd; /* LC_ENCRYPTION_INFO */ uint32_t cmdsize; /* sizeof(struct encryption_info_command) */ uint32_t cryptoff; /* file offset of encrypted range */ uint32_t cryptsize; /* file size of encrypted range */ uint32_t cryptid; /* which enryption system,0 means not-encrypted yet */ }; struct fat_header { uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */ uint32_t nfat_arch; /* number of structs that follow */ }; struct fat_arch { cpu_type_t cputype; /* cpu specifier (int) */ cpu_subtype_t cpusubtype; /* machine specifier (int) */ uint32_t offset; /* file offset to this object file */ uint32_t size; /* size of this object file */ uint32_t align; /* alignment as a power of 2 */ }; ''' command_script_init = 'struct mach_header* mh = (struct mach_header*)_dyld_get_image_header({});'.format( machoIdx) command_script_init += 'const char *path = "{}";'.format(machoPath) command_script_init += 'uint64_t main_addr = (uint64_t){};'.format( fix_addr) command_script = command_script_header + command_script_init command_script += r''' struct load_command *lc; struct encryption_info_command *eic; struct fat_header *fh; struct fat_arch *arch; char x_buffer[1024]; char rpath[4096],npath[4096]; /* should be big enough for PATH_MAX */ unsigned int fileoffs = 0, off_cryptid = 0, restsize; int i,fd,outfd,r,n,toread; char *tmp; if ((char*)realpath(path, rpath) == NULL) { strlcpy(rpath, path, sizeof(rpath)); } /* extract basename */ tmp = (char*)strrchr(rpath, '/'); //printf("\n\n"); if (tmp == NULL) { printf("[-] Unexpected error with filename.\n"); _exit(1); } else { printf("[+] Dumping %s\n", tmp+1); } /* detect if this is a arm64 binary */ if (mh->magic == MH_MAGIC_64) { lc = (struct load_command *)((unsigned char *)mh + sizeof(struct mach_header_64)); printf("[+] detected 64bit ARM binary in memory.\n"); } else { /* we might want to check for other errors here, too */ lc = (struct load_command *)((unsigned char *)mh + sizeof(struct mach_header)); printf("[+] detected 32bit ARM binary in memory.\n"); } /* searching all load commands for an LC_ENCRYPTION_INFO load command */ BOOL is_image_crypted = NO; for (i=0; i<mh->ncmds; i++) { if (lc->cmd == LC_ENCRYPTION_INFO || lc->cmd == LC_ENCRYPTION_INFO_64) { eic = (struct encryption_info_command *)lc; /* If this load command is present, but data is not crypted then exit */ if (eic->cryptid == 0) { break; } is_image_crypted = YES; off_cryptid=(off_t)((uint8_t*)&eic->cryptid - (uint8_t*)mh); printf("[+] offset to cryptid found: @%p(from %p) = %x\n", &eic->cryptid, mh, off_cryptid); printf("[+] Found encrypted data at address %08x of length %u bytes - type %u.\n", eic->cryptoff, eic->cryptsize, eic->cryptid); printf("[+] Opening %s for reading.\n", rpath); fd = (int)open(rpath, O_RDONLY); if (fd == -1) { printf("[-] Failed opening.\n"); break; } printf("[+] Reading header\n"); n = (long)read(fd, (void *)x_buffer, sizeof(x_buffer)); if (n != sizeof(x_buffer)) { printf("[W] Warning read only %d bytes\n", n); } printf("[+] Detecting header type\n"); fh = (struct fat_header *)x_buffer; /* Is this a FAT file - we assume the right endianess */ if (fh->magic == FAT_CIGAM) { printf("[+] Executable is a FAT image - searching for right architecture\n"); arch = (struct fat_arch *)&fh[1]; for (i=0; i<swap32(fh->nfat_arch); i++) { if ((mh->cputype == swap32(arch->cputype)) && (mh->cpusubtype == swap32(arch->cpusubtype))) { fileoffs = swap32(arch->offset); printf("[+] Correct arch is at offset %u in the file\n", fileoffs); break; } arch++; } if (fileoffs == 0) { printf("[-] Could not find correct arch in FAT image\n"); _exit(1); } } else if (fh->magic == MH_MAGIC || fh->magic == MH_MAGIC_64) { printf("[+] Executable is a plain MACH-O image\n"); } else { printf("[-] Executable is of unknown type\n"); break; } // NSDocumentDirectory == 9 NSUserDomainMask == 1 NSString *docPath = ((NSArray*)NSSearchPathForDirectoriesInDomains(9, 1, YES))[0]; strlcpy(npath, docPath.UTF8String, sizeof(npath)); strlcat(npath, tmp, sizeof(npath)); strlcat(npath, ".decrypted", sizeof(npath)); strlcpy(x_buffer, npath, sizeof(x_buffer)); printf("[+] Opening %s for writing.\n", npath); outfd = (int)open(npath, O_RDWR|O_CREAT|O_TRUNC, 0644); if (outfd == -1) { if ((int)strncmp("/private/var/mobile/Applications/", rpath, 33) == 0) { printf("[-] Failed opening. Most probably a sandbox issue. Trying something different.\n"); /* create new name */ strlcpy(npath, "/private/var/mobile/Applications/", sizeof(npath)); tmp = (char*)strchr(rpath+33, '/'); if (tmp == NULL) { printf("[-] Unexpected error with filename.\n"); return; } tmp++; *tmp++ = 0; strlcat(npath, rpath+33, sizeof(npath)); strlcat(npath, "tmp/", sizeof(npath)); strlcat(npath, x_buffer, sizeof(npath)); printf("[+] Opening %s for writing.\n", npath); outfd = (int)open(npath, O_RDWR|O_CREAT|O_TRUNC, 0644); } if (outfd == -1) { printf("[-] Failed opening\n"); break; } } /* calculate address of beginning of crypted data */ n = fileoffs + eic->cryptoff; restsize = (off_t)lseek(fd, 0, SEEK_END) - n - eic->cryptsize; (off_t)lseek(fd, 0, SEEK_SET); printf("[+] Copying the not encrypted start of the file\n"); /* first copy all the data before the encrypted data */ while (n > 0) { toread = (n > sizeof(x_buffer)) ? sizeof(x_buffer) : n; r = (long)read(fd, x_buffer, toread); if (r != toread) { printf("[-] Error reading file\n"); return; } n -= r; r = (long)write(outfd, x_buffer, toread); if (r != toread) { printf("[-] Error writing file\n"); return; } } /* now write the previously encrypted data */ printf("[+] Dumping the decrypted data into the file\n"); // (unsigned char *)mh + eic->cryptoff unsigned char * tmp_buf = (unsigned char *)malloc(eic->cryptsize); unsigned char * tmp_ptr = (unsigned char *)((unsigned char *)mh + eic->cryptoff); for(int i = 0; i < eic->cryptsize; i ++){ if(main_addr != 0 && main_addr == (uint64_t)tmp_ptr){ tmp_buf[i] = 0xF6; tmp_buf[++i] = 0x57; tmp_buf[++i] = 0xBD; tmp_buf[++i] = 0xA9; tmp_ptr += 4; continue; } tmp_buf[i] = *tmp_ptr; tmp_ptr ++; } r = (long)write(outfd, (unsigned char *)tmp_buf, eic->cryptsize); if (r != eic->cryptsize) { uint64_t flag = (uint64_t)(mh); // printf("Error no.%d: %s\n", errno, strerror(errno)); printf("[-] read memory from:0x%lx size:%ld\n", mh + eic->cryptoff, eic->cryptsize); printf("[-] Error writing file r=%lx offset=%lx size=%lx flag=%lx\n", r,eic->cryptoff, eic->cryptsize, flag); return; } free(tmp_buf); /* and finish with the remainder of the file */ n = restsize; (off_t)lseek(fd, eic->cryptsize, SEEK_CUR); printf("[+] Copying the not encrypted remainder of the file\n"); while (n > 0) { toread = (n > sizeof(x_buffer)) ? sizeof(x_buffer) : n; r = (long)read(fd, x_buffer, toread); if (r != toread) { printf("[-] Error reading file\n"); return; } n -= r; r = (long)write(outfd, x_buffer, toread); if (r != toread) { printf("[-] Error writing file\n"); return; } } if (off_cryptid) { uint32_t x_zero=0; off_cryptid+=fileoffs; printf("[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset %x\n", off_cryptid); if (((off_t)lseek(outfd, off_cryptid, SEEK_SET)) != off_cryptid || (long)write(outfd, &x_zero, 4) != 4) { printf("[-] Error writing cryptid value\n"); } } printf("[+] Closing original file\n"); close(fd); printf("[+] Closing dump file\n"); close(outfd); break; } lc = (struct load_command *)((unsigned char *)lc+lc->cmdsize); } NSMutableString* retStr = [NSMutableString string]; if(is_image_crypted){ printf("[*] This mach-o file decrypted done.\n"); [retStr appendString:@"[+] dump macho file at:"]; [retStr appendString:@(npath)]; }else{ printf("[*] this image is not crypted\n"); [retStr appendString:@"[+] this macho file at:"]; [retStr appendString:@(rpath)]; } retStr ''' ret = utils.exe_script(debugger, command_script) return ret
def choose(debugger, classname): command_script = 'NSString * className = @"' + classname + '";' command_script += r''' // define #define KERN_SUCCESS 0 // typedef typedef unsigned long uintptr_t; #define MALLOC_PTR_IN_USE_RANGE_TYPE 1 typedef int kern_return_t; typedef unsigned int mach_port_t; typedef mach_port_t task_t; typedef uintptr_t vm_offset_t; typedef vm_offset_t vm_address_t; typedef uintptr_t vm_size_t; typedef struct { vm_address_t address; vm_size_t size; } vm_range_t; typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory); typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size); typedef struct malloc_introspection_t { kern_return_t (*enumerator)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); /* enumerates all the malloc pointers in use */ } malloc_introspection_t; typedef struct _malloc_zone_t { void *reserved1; void *reserved2; size_t (*size)(struct _malloc_zone_t *zone, const void *ptr); void *(*malloc)(struct _malloc_zone_t *zone, size_t size); void *(*calloc)(struct _malloc_zone_t *zone, size_t num_items, size_t size); void *(*valloc)(struct _malloc_zone_t *zone, size_t size); void (*free)(struct _malloc_zone_t *zone, void *ptr); void *(*realloc)(struct _malloc_zone_t *zone, void *ptr, size_t size); void (*destroy)(struct _malloc_zone_t *zone); const char *zone_name; unsigned (*batch_malloc)(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); void (*batch_free)(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); struct malloc_introspection_t *introspect; unsigned version; void *(*memalign)(struct _malloc_zone_t *zone, size_t alignment, size_t size); void (*free_definite_size)(struct _malloc_zone_t *zone, void *ptr, size_t size); size_t (*pressure_relief)(struct _malloc_zone_t *zone, size_t goal); } malloc_zone_t; struct XZChoice { NSMutableArray * query_; // std::set<Class> query_; NSMutableArray * result_; // std::set<id> result_; }; struct XZObjectStruct { Class isa_; }; // function memory_reader_t memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t { *local_memory = (void*) remote_address; return KERN_SUCCESS; }; // function copy_class_list: get the class list typedef Class * (*copy_class_list_t)(size_t &size); copy_class_list_t copy_class_list = [](size_t &size) -> Class *{ size = (size_t)objc_getClassList(NULL, 0); Class * data = (Class *)(malloc(sizeof(Class) * size)); for (;;) { size_t writ = (size_t)objc_getClassList(data, (int)size); if (writ <= size) { size = writ; return data; } Class * copy = (Class *)(realloc(data, sizeof(Class) * writ)); if (copy == NULL) { free(data); return NULL; } data = copy; size = writ; } } // function void choose_(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) typedef void (*choose__t)(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count); choose__t choose_ = [](task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) -> void { XZChoice * choiz = (struct XZChoice *)(baton); for (unsigned i = 0; i < count; ++i) { vm_range_t &range = ranges[i]; void * data = (void *)(range.address); size_t size = range.size; if (size < sizeof(XZObjectStruct)) continue; uintptr_t * pointers = (uintptr_t *)(data); #ifdef __arm64__ struct objc_class * isa = (struct objc_class *)(pointers[0] & 0x1fffffff8); #else struct objc_class * isa = (struct objc_class *)(pointers[0]); #endif //uint64_t p = (uint64_t)isa; //[choiz->result_ addObject:[@(p) stringValue]]; size_t needed; for(unsigned i=0; i < [choiz->query_ count]; i++){ struct objc_class * result = (struct objc_class *)[choiz->query_ objectAtIndex:i]; uint64_t result_intv = (uint64_t)result; uint64_t isa_intv = (uint64_t)isa; uint64_t data_intv = (uint64_t)data; if(result_intv == isa_intv){ /* NSMutableString* tmpStr = [NSMutableString string]; [tmpStr appendString:@"isa:"]; [tmpStr appendString:[@(isa_intv) stringValue]]; [tmpStr appendString:@"query:"]; [tmpStr appendString:[@(result_intv) stringValue]]; [tmpStr appendString:@"object:"]; [tmpStr appendString:[@(data_intv) stringValue]]; [choiz->result_ addObject:tmpStr]; continue; */ size_t boundary = 496; #ifdef __LP64__ boundary *= 2; #endif needed = (size_t)class_getInstanceSize((Class)result)); if ((needed <= boundary && (needed + 15) / 16 * 16 != size) || (needed > boundary && (needed + 511) / 512 * 512 != size)){ continue; } [choiz->result_ addObject:(id)data]; } } } } XZChoice choice; choice.query_ = (NSMutableArray*)[NSMutableArray array]; choice.result_ = (NSMutableArray*)[NSMutableArray array]; Class _class = NSClassFromString(className); size_t number; Class * classes = copy_class_list(number); for (size_t i = 0; i != number; ++i) { for (Class current = classes[i]; current != Nil; current = (Class)class_getSuperclass(current)) { if (current == _class) { [choice.query_ addObject:classes[i]]; break; } } } free(classes); vm_address_t *zones = 0; unsigned int num_zones = 0; task_t task = 0; kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones); for (unsigned i = 0; i != num_zones; ++i) { const malloc_zone_t * zone = (const malloc_zone_t *)(zones[i]); if (zone == NULL || zone->introspect == NULL) continue; zone->introspect->enumerator((task_t)mach_task_self(), &choice, MALLOC_PTR_IN_USE_RANGE_TYPE, zones[i], task_peek, choose_); } NSArray* choosed = choice.result_; NSMutableString* retStr = [NSMutableString string]; unsigned choosedSize = [choosed count]; if(choosedSize == 0){ [retStr appendString:@"Not found any object of class: "]; [retStr appendString:className]; }else{ uint64_t objAdrr = (uint64_t)choosed; [retStr appendString:@"====>xia0LLDB NSArray Address: "]; [retStr appendString:[@(objAdrr) stringValue]]; [retStr appendString:@"\tsize: "]; [retStr appendString:[@(choosedSize) stringValue]]; [retStr appendString:@"\n"]; [retStr appendString:@"| | | | | | | | | | | | | | | | | | | | \n"]; [retStr appendString:@"V V V V V V V V V V V V V V V V V V V V \n"]; for (unsigned i = 0; i != [choosed count]; ++i) { if([(NSObject*)([choosed objectAtIndex:i]) isKindOfClass:[NSString class]]){ [retStr appendString: @"Now not support print the NSString, You can po it by yourself like below:"]; [retStr appendString:@"\n1. p/x "]; [retStr appendString:[@(objAdrr) stringValue]]; [retStr appendString:@"\n2. po (NSArray*)above_hex_address_vaule : print the NSArray contain NSString"]; [retStr appendString:@"\n3. po [(NSArray*)above_hex_address_vaule objectAtIndex:0] : print first NSString"]; break; }else{ uint64_t objAdrr = (uint64_t)[choosed objectAtIndex:i]; [retStr appendString:@"======>xia0LLDB Object Address: "]; [retStr appendString:[@(objAdrr) stringValue]]; [retStr appendString:@"\n"]; [retStr appendString:[(NSObject*)([choosed objectAtIndex:i]) description]]; } [retStr appendString:@"\n"]; } } retStr ''' retStr = utils.exe_script(debugger, command_script) return colorme.attr_str(utils.hex_int_in_str(retStr), 'green')
def objc_dump_methods(debugger, classname): command_script = '@import Foundation;char* classname = (char*)\"' + classname + '\";' command_script += r''' NSMutableString* retStr = [NSMutableString string]; typedef struct objc_method *Method; unsigned int m_size = 0; Class cls = objc_getClass(classname); struct objc_method ** metholds = (struct objc_method **)class_copyMethodList(cls, &m_size); for (int j = 0; j < m_size; j++) { struct objc_method * meth = metholds[j]; id implementation = (id)method_getImplementation(meth); NSString* m_name = NSStringFromSelector((SEL)method_getName(meth)); char buffer[100]; buffer[0] = '\0'; method_getReturnType (meth, buffer, sizeof(buffer)); NSString* retTypeStr =[NSString stringWithUTF8String:buffer]; //[mAddrArr addObject:(id)[@((uintptr_t)implementation) stringValue]]; NSNumber* implementationNum = [NSNumber numberWithUnsignedLongLong:(uintptr_t)implementation]; [retStr appendString:@"-"]; [retStr appendString:@","]; [retStr appendString:m_name]; [retStr appendString:@","]; [retStr appendString:(id)[implementationNum stringValue]]; [retStr appendString:@","]; [retStr appendString:retTypeStr]; [retStr appendString:@","]; unsigned int argumentCount = (unsigned int)method_getNumberOfArguments (meth); for (int i = 2; i < argumentCount; i++) { method_getArgumentType (meth, i, buffer, sizeof(buffer)); NSString* argTypeStr =[NSString stringWithUTF8String:buffer]; [retStr appendString:argTypeStr]; [retStr appendString:@","]; } [retStr appendString:@"||"]; } unsigned int cm_size = 0; struct objc_method **classMethods = (struct objc_method **)class_copyMethodList((Class)objc_getMetaClass((const char *)class_getName(cls)), &cm_size); for (int k = 0; k < cm_size; k++) { struct objc_method * meth = classMethods[k]; id implementation = (id)method_getImplementation(meth); NSString* cm_name = NSStringFromSelector((SEL)method_getName(meth)); char buffer[100]; buffer[0] = '\0'; method_getReturnType (meth, buffer, sizeof(buffer)); NSString* retTypeStr =[NSString stringWithUTF8String:buffer]; //[mAddrArr addObject:(id)[@((uintptr_t)implementation) stringValue]]; NSNumber* implementationNum = [NSNumber numberWithUnsignedLongLong:(uintptr_t)implementation]; [retStr appendString:@"+"]; [retStr appendString:@","]; [retStr appendString:cm_name]; [retStr appendString:@","]; [retStr appendString:(id)[implementationNum stringValue]]; [retStr appendString:@","]; [retStr appendString:retTypeStr]; [retStr appendString:@","]; unsigned int argumentCount = (unsigned int)method_getNumberOfArguments (meth); for (int i = 2; i < argumentCount; i++) { method_getArgumentType (meth, i, buffer, sizeof(buffer)); NSString* argTypeStr =[NSString stringWithUTF8String:buffer]; [retStr appendString:argTypeStr]; [retStr appendString:@","]; } [retStr appendString:@"||"]; } retStr ''' retStr = utils.exe_script(debugger, command_script) arr = retStr.strip().split("||") retArr = [] for item in arr: if len(item) <= 0: continue methodInfo = item.split(",") methodArr = [] for val in methodInfo: if len(val) <= 0: continue methodArr.append(val) retArr.append(methodArr) return retArr
def get_macho_entry_offset(debugger): command_script = '@import Foundation;' command_script += r''' //NSMutableString* retStr = [NSMutableString string]; #define MH_MAGIC_64 0xfeedfacf #define LC_SEGMENT_64 0x19 #define LC_REQ_DYLD 0x80000000 #define LC_MAIN (0x28|LC_REQ_DYLD) typedef int integer_t; typedef integer_t cpu_type_t; typedef integer_t cpu_subtype_t; typedef integer_t cpu_threadtype_t; struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ }; typedef int vm_prot_t; struct segment_command_64 { /* for 64-bit architectures */ uint32_t cmd; /* LC_SEGMENT_64 */ uint32_t cmdsize; /* includes sizeof section_64 structs */ char segname[16]; /* segment name */ uint64_t vmaddr; /* memory address of this segment */ uint64_t vmsize; /* memory size of this segment */ uint64_t fileoff; /* file offset of this segment */ uint64_t filesize; /* amount to map from the file */ vm_prot_t maxprot; /* maximum VM protection */ vm_prot_t initprot; /* initial VM protection */ uint32_t nsects; /* number of sections in segment */ uint32_t flags; /* flags */ }; struct section_64 { /* for 64-bit architectures */ char sectname[16]; /* name of this section */ char segname[16]; /* segment this section goes in */ uint64_t addr; /* memory address of this section */ uint64_t size; /* size in bytes of this section */ uint32_t offset; /* file offset of this section */ uint32_t align; /* section alignment (power of 2) */ uint32_t reloff; /* file offset of relocation entries */ uint32_t nreloc; /* number of relocation entries */ uint32_t flags; /* flags (section type and attributes)*/ uint32_t reserved1; /* reserved (for offset or index) */ uint32_t reserved2; /* reserved (for count or sizeof) */ uint32_t reserved3; /* reserved */ }; struct entry_point_command { uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ uint32_t cmdsize; /* 24 */ uint64_t entryoff; /* file (__TEXT) offset of main() */ uint64_t stacksize;/* if not zero, initial stack size */ }; int x_offset = 0; struct mach_header_64* header = (struct mach_header_64*)_dyld_get_image_header(0); if(header->magic != MH_MAGIC_64) { return ; } x_offset = sizeof(struct mach_header_64); int ncmds = header->ncmds; //uint64_t textStart = 0; //uint64_t textEnd = 0; uint64_t main_addr = 0; while(ncmds--) { /* go through all load command to find __TEXT segment*/ struct load_command * lcp = (struct load_command *)((uint8_t*)header + x_offset); x_offset += lcp->cmdsize; if(lcp->cmd == LC_MAIN) { uintptr_t slide = (uintptr_t)_dyld_get_image_vmaddr_slide(0); struct entry_point_command* main_cmd = (struct entry_point_command*)lcp; main_addr = (uint64_t)slide + main_cmd->entryoff + 0x100000000; break; } } char ret[50]; /* char textStartAddrStr[20]; sprintf(textStartAddrStr, "0x%016lx", textStart); char textEndAddrStr[20]; sprintf(textEndAddrStr, "0x%016lx", textEnd); char* splitStr = ","; strcpy(ret,textStartAddrStr); strcat(ret,splitStr); strcat(ret,textEndAddrStr); */ sprintf(ret, "0x%016lx", main_addr); ret ''' retStr = utils.exe_script(debugger, command_script) return retStr
def get_macho_mod_init_first_func(debugger): command_script = '@import Foundation;' command_script += r''' //NSMutableString* retStr = [NSMutableString string]; #define MH_MAGIC_64 0xfeedfacf #define LC_SEGMENT_64 0x19 typedef int integer_t; typedef integer_t cpu_type_t; typedef integer_t cpu_subtype_t; typedef integer_t cpu_threadtype_t; struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ }; typedef int vm_prot_t; struct segment_command_64 { /* for 64-bit architectures */ uint32_t cmd; /* LC_SEGMENT_64 */ uint32_t cmdsize; /* includes sizeof section_64 structs */ char segname[16]; /* segment name */ uint64_t vmaddr; /* memory address of this segment */ uint64_t vmsize; /* memory size of this segment */ uint64_t fileoff; /* file offset of this segment */ uint64_t filesize; /* amount to map from the file */ vm_prot_t maxprot; /* maximum VM protection */ vm_prot_t initprot; /* initial VM protection */ uint32_t nsects; /* number of sections in segment */ uint32_t flags; /* flags */ }; struct section_64 { /* for 64-bit architectures */ char sectname[16]; /* name of this section */ char segname[16]; /* segment this section goes in */ uint64_t addr; /* memory address of this section */ uint64_t size; /* size in bytes of this section */ uint32_t offset; /* file offset of this section */ uint32_t align; /* section alignment (power of 2) */ uint32_t reloff; /* file offset of relocation entries */ uint32_t nreloc; /* number of relocation entries */ uint32_t flags; /* flags (section type and attributes)*/ uint32_t reserved1; /* reserved (for offset or index) */ uint32_t reserved2; /* reserved (for count or sizeof) */ uint32_t reserved3; /* reserved */ }; int x_offset = 0; struct mach_header_64* header = (struct mach_header_64*)_dyld_get_image_header(0); if(header->magic != MH_MAGIC_64) { return ; } x_offset = sizeof(struct mach_header_64); int ncmds = header->ncmds; uint64_t modInitFirstAddr = 0; char* secName; while(ncmds--) { /* go through all load command to find __TEXT segment*/ struct load_command * lcp = (struct load_command *)((uint8_t*)header + x_offset); x_offset += lcp->cmdsize; if(lcp->cmd == LC_SEGMENT_64) { struct segment_command_64 * curSegment = (struct segment_command_64 *)lcp; struct section_64* curSection = (struct section_64*)((uint8_t*)curSegment + sizeof(struct segment_command_64)); if(!strcmp(curSection->segname, "__DATA")){ for (int i = 0; i < curSegment->nsects; i++) { if (!strcmp(curSection->sectname, "__mod_init_func")) { uint64_t memAddr = curSection->addr; uint64_t modInitAddrArr = memAddr + (uint64_t)_dyld_get_image_vmaddr_slide(0); modInitFirstAddr = *((uint64_t*)modInitAddrArr) break; } curSection = (struct section_64*)((uint8_t*)curSection + sizeof(struct section_64)); } break; } } } char ret[50]; sprintf(ret, "0x%016lx", modInitFirstAddr); ret ''' retStr = utils.exe_script(debugger, command_script) return utils.hex_int_in_str(retStr)
def patch_code(debugger, addr, ins, count): command_script = '@import Foundation;\n' command_script += 'uint64_t x_addr = {};\n'.format(addr) command_script += 'uint8_t patch_data[] = {};\n'.format(ins) command_script += 'int insCount = {};\n'.format(count) command_script += r''' NSMutableString* retStr = [NSMutableString string]; void * patch_addr = (void*)x_addr; //uint8_t patch_data[] = {0xc0, 0x03, 0x5f, 0xd6}; int patch_data_size = 4*insCount; // =====================================================patch code============================================= typedef bool (*patch_code_t)(void* patch_addr, uint8_t* patch_data, int patch_data_size); patch_code_t patch_code = [](void* patch_addr, uint8_t* patch_data, int patch_data_size) -> bool { #define PAGE_SIZE 0x0000000000004000 #define PAGE_MASK 0x0000000000003fff #define RTLD_LAZY 0x1 #define RTLD_NOW 0x2 #define RTLD_LOCAL 0x4 #define RTLD_GLOBAL 0x8 #define VM_PROT_READ ((vm_prot_t) 0x01) #define VM_PROT_WRITE ((vm_prot_t) 0x02) #define VM_PROT_EXECUTE ((vm_prot_t) 0x04) #define PROT_NONE 0x00 /* [MC2] no permissions */ #define PROT_READ 0x01 /* [MC2] pages can be read */ #define PROT_WRITE 0x02 /* [MC2] pages can be written */ #define PROT_EXEC 0x04 /* [MC2] pages can be executed */ #define MAP_SHARED 0x0001 #define MAP_ANON 0x1000 #define KERN_SUCCESS 0 typedef unsigned int mach_port_t; typedef int kern_return_t; typedef unsigned int vm_inherit_t; typedef mach_port_t task_t; typedef int vm_prot_t; typedef unsigned long uintptr_t; typedef uintptr_t vm_offset_t; typedef vm_offset_t vm_address_t; typedef uint64_t mach_vm_address_t; typedef int boolean_t; typedef int vm_behavior_t; typedef uint32_t vm32_object_id_t; typedef uintptr_t vm_size_t; typedef int *vm_region_recurse_info_t; typedef unsigned long long memory_object_offset_t; struct vm_region_submap_short_info_64 { vm_prot_t protection; /* present access protection */ vm_prot_t max_protection; /* max avail through vm_prot */ vm_inherit_t inheritance;/* behavior of map/obj on fork */ memory_object_offset_t offset; /* offset into object/map */ unsigned int user_tag; /* user tag on map entry */ unsigned int ref_count; /* obj/map mappers, etc */ unsigned short shadow_depth; /* only for obj */ unsigned char external_pager; /* only for obj */ unsigned char share_mode; /* see enumeration */ boolean_t is_submap; /* submap vs obj */ vm_behavior_t behavior; /* access behavior hint */ vm32_object_id_t object_id; /* obj/map name, not a handle */ unsigned short user_wired_count; }; typedef unsigned int __darwin_natural_t; typedef __darwin_natural_t natural_t; typedef natural_t mach_msg_type_number_t; typedef struct vm_region_submap_short_info_64 vm_region_submap_short_info_data_64_t; #define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 \ ((mach_msg_type_number_t) \ (sizeof (vm_region_submap_short_info_data_64_t) / sizeof (natural_t))) #define VM_FLAGS_OVERWRITE 0x4000 /* delete any existing mappings first */ typedef int __int32_t; typedef __int32_t __darwin_pid_t; typedef __darwin_pid_t pid_t; // init value kern_return_t kret; task_t self_task = (task_t)mach_task_self(); /* Set platform binary flag */ #define FLAG_PLATFORMIZE (1 << 1) // platformize_me // https://github.com/pwn20wndstuff/Undecimus/issues/112 /* void* handle = (void*)dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); if (!handle){ //[retStr appendString:@"[-] /usr/lib/libjailbreak.dylib dlopen failed!\n"]; return false; } // Reset errors (const char *)dlerror(); typedef void (*fix_entitle_prt_t)(pid_t pid, uint32_t what); fix_entitle_prt_t ptr = (fix_entitle_prt_t)dlsym(handle, "jb_oneshot_entitle_now"); const char *dlsym_error = (const char *)dlerror(); if (dlsym_error) return; ptr((pid_t)getpid(), FLAG_PLATFORMIZE); //[retStr appendString:@"\n[+] platformize me success!"]; */ void* target_addr = patch_addr; // 1. get target address page and patch offset unsigned long page_start = (unsigned long) (target_addr) & ~PAGE_MASK; unsigned long patch_offset = (unsigned long)target_addr - page_start; // map new page for patch void *new_page = (void *)mmap(NULL, PAGE_SIZE, 0x1 | 0x2, 0x1000 | 0x0001, -1, 0); if (!new_page ){ //[retStr appendString:@"[-] mmap failed!\n"]; return false; } kret = (kern_return_t)vm_copy(self_task, (unsigned long)page_start, PAGE_SIZE, (vm_address_t) new_page); if (kret != KERN_SUCCESS){ //[retStr appendString:@"[-] vm_copy faild!\n"]; return false; } // 4. start patch /* nop -> {0x1f, 0x20, 0x03, 0xd5} ret -> {0xc0, 0x03, 0x5f, 0xd6} */ // char patch_ins_data[4] = {0x1f, 0x20, 0x03, 0xd5}; // mach_vm_write(task_self, (vm_address_t)(new+patch_offset), patch_ret_ins_data, 4); memcpy((void *)((uint64_t)new_page+patch_offset), patch_data, patch_data_size); //[retStr appendString:@"[+] patch ret[0xc0 0x03 0x5f 0xd6] with memcpy\n"]; // set back to r-x (int)mprotect(new_page, PAGE_SIZE, PROT_READ | PROT_EXEC); //[retStr appendString:@"[*] set new page back to r-x success!\n"]; // remap vm_prot_t prot; vm_inherit_t inherit; // get page info vm_address_t region = (vm_address_t) page_start; vm_size_t region_len = 0; struct vm_region_submap_short_info_64 vm_info; mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; natural_t max_depth = 99999; kret = (kern_return_t)vm_region_recurse_64(self_task, ®ion, ®ion_len, &max_depth, (vm_region_recurse_info_t) &vm_info, &info_count); if (kret != KERN_SUCCESS){ //[retStr appendString:@"[-] vm_region_recurse_64 faild!\n"]; return false; } prot = vm_info.protection & (PROT_READ | PROT_WRITE | PROT_EXEC); inherit = vm_info.inheritance; //[retStr appendString:@"[*] get page info done.\n"]; vm_prot_t c; vm_prot_t m; mach_vm_address_t target = (mach_vm_address_t)page_start; kret = (kern_return_t)mach_vm_remap(self_task, &target, PAGE_SIZE, 0, VM_FLAGS_OVERWRITE, self_task, (mach_vm_address_t) new_page, true, &c, &m, inherit); if (kret != KERN_SUCCESS){ //[retStr appendString:@"[-] remap mach_vm_remap faild!\n"]; return false; } //[retStr appendString:@"[+] remap to target success!\n"]; // clear cache void* clear_start_ = (void*)(page_start + patch_offset); sys_icache_invalidate (clear_start_, 4); sys_dcache_flush (clear_start_, 4); return true; }; // =====================================================patch code============================================= patch_code(patch_addr, patch_data, patch_data_size); [retStr appendString:@"patch done."]; retStr ''' retStr = utils.exe_script(debugger, command_script) return utils.hex_int_in_str(retStr)