def react_operator(idx, ctx): print '%x' % (idx.ea) fcn_object = ctx.get_obj("function") """next line was working on ELF""" demangled = ida_name.demangle_name( ida_name.get_name(fcn_object.addr)[1:], 0) """next line was working on MACH-O""" #demangled = ida_name.demangle_name(ida_name.get_name(fcn_object.addr), 0) print demangled if "operator<<" in demangled: arg2 = ctx.get_expr('arg2')[0] arg1 = ctx.get_expr('arg1')[0] arg1_repr = get_string_repr(arg1, ctx) arg2_repr = get_string_repr(arg2, ctx) var = ctx.get_var("res") #varname = ctx.get_var_name(var.idx) varexp = make_var_expr(var.idx, var.typ, var.mba) #varexp = make_var_expr(var2.idx, var2.typ, var2.mba, arg=True) arglist = ida_hexrays.carglist_t() arglist.push_back(arg2) helper = ida_hexrays.call_helper(ida_hexrays.dummy_ptrtype(4, False), arglist, "{} << ".format(arg1_repr)) insn = make_cexpr_insn(idx.ea, make_asgn_expr(varexp, helper)) idx.cleanup() idaapi.qswap(idx, insn) # del original inst because we swapped them on previous line del insn
def get_typeinfo_name(self, typeinfo_ea): name_ea = utils.get_ptr(typeinfo_ea + self.CLASS_TYPE_NAME_OFFSET) if name_ea is None or name_ea == BADADDR: mangled_class_name = ida_name.get_ea_name(typeinfo_ea) else: mangled_class_name = "_Z" + idc.get_strlit_contents(name_ea).decode() class_name = ida_name.demangle_name(mangled_class_name, idc.INF_LONG_DN) return GccRTTIParser.strip_class_name(class_name)
def getFunctionSymbols(self): function_symbols = {} function_offsets = self.getFunctions() for function_offset in function_offsets: function_name = ida_funcs.get_func_name(function_offset) # apply demangling if required if "@" in function_name: demangled = ida_name.demangle_name(function_name, 0) if demangled: function_name = demangled if not re.match("sub_[0-9a-fA-F]+", function_name): function_symbols[function_offset] = function_name return function_symbols
def react_operator2(idx, ctx): print '%x' % (idx.ea) fcn_object = ctx.get_obj("function") """next line was working on ELF""" demangled = ida_name.demangle_name( ida_name.get_name(fcn_object.addr)[1:], 0) """next line was working on MACH-O""" #demangled = ida_name.demangle_name(ida_name.get_name(fcn_object.addr), 0) print demangled if "operator<<" in demangled: arg1 = ctx.get_expr('arg1')[0] arg1_repr = get_string_repr(arg1, ctx) arg2 = ctx.get_expr('arg2')[0] #varexp = make_var_expr(var2.idx, var2.typ, var2.mba, arg=True) arglist = ida_hexrays.carglist_t() arglist.push_back(arg2) val = ida_hexrays.call_helper(ida_hexrays.dummy_ptrtype(4, False), arglist, "{} << ".format(arg1_repr)) insn = make_cexpr_insn(idx.ea, val) idx.cleanup() idaapi.qswap(idx, insn) del insn
def get_string_repr(obj, ctx): if obj.opname == "cast": obj = obj.x else: pass if obj.opname == "obj": if obj.type.dstr() == "char *": return repr(ida_bytes.get_strlit_contents(obj.obj_ea, 256, -1)) else: name = ida_name.get_name(obj.obj_ea).split("@@")[0] print name if name[0] == ".": name = name[1:] if "endl" in name: return "std::endl" return ida_name.demangle_name(name, 0) elif obj.opname == "ref": return "&" + get_string_repr(obj.x, ctx) elif obj.opname == "var": return ctx.get_var_name(obj.v.idx) # elif else: print obj.opname return ""
def main(): global til til = ida_typeinf.get_idati() capstone_init() unicorn_init() all_names = list(idautils.Names()) # First, get inheritance tree for all classes # We do this in two passes: # First pass: for each class, gather class name, parent class name, # and superclass name, and connect them # Second pass: for each inheritance hierarchy object, we check # if the parent pointer is None (signaling a top-level # class), and add that to the `ihs` list # We can do this whole thing by processing xrefs to OSMetaClass::OSMetaClass OSMetaClass_ctor = get_OSMetaClass_ctor() if OSMetaClass_ctor == 0: print("Could not find OSMetaClass::OSMetaClass") return print("Got OSMetaClass::OSMetaClass at {}".format(hex(OSMetaClass_ctor))) # key,value pairs of all inheritance hierarchy objects ih_dict = {} # only those inheritance hierarchy objects which represent # a top-level parent class (aka inheriting from OSObject) ihs = [] xrefs = list(idautils.XrefsTo(OSMetaClass_ctor)) num = 0 for xref in xrefs: frm = xref.frm # test # frm = 0x63920 # print("xref from {}".format(hex(frm))) args = get_OSMetaClass_ctor_args(frm) pname = args[0] cname = args[1] if cname == "OSMetaClassBase": print("xref from {}".format(hex(frm))) print(args) if pname == cname: continue csz = args[2] # if pname == "AUAUnitDictionary" and cname == "AUAMixerUnitDictionary": # print(args) # return new_parent = pname is not None and pname not in ih_dict new_child = cname not in ih_dict if new_parent: ih_dict[pname] = InheritanceHierarchy(None, pname, csz, csz) if new_child: ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: # Update class size for only child classes ih_dict[cname].sz = csz if pname == None: # If this class has no superclass, it must be parent class, # so make its InheritanceHierarchy object ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: child_ih = ih_dict[cname] parent_ih = ih_dict[pname] parent_ih.add_child(child_ih) child_ih.parent = parent_ih child_ih.totsz = child_ih.sz + parent_ih.totsz # if cname == "AUAUnitDictionary": # print("AUAUnitDictionary sz: {}".format(ih_dict[pname].sz)) # print(args) # return # if cname == "AUAMixerUnitDictionary": # print("AUAMixerUnitDictionary sz: {}".format(ih_dict[cname].sz)) # print(args) # return num += 1 # if num == 10: # break print("First pass: {} classes processed".format(num)) num = 0 # Second pass for ih in ih_dict.values(): if ih.parent == None: # print("Adding {} to the ihs list".format(ih.name)) num += 1 ihs.append(ih) print("Second pass: {} classes added to ihs list".format(num)) num = 0 wants_class_hierarchy = ida_kernwin.ask_yn(0, "Dump class hierarchy?") if wants_class_hierarchy: hierch_file = open( "{}/iOS/Scripts/iokit_hier.txt".format(str(Path.home())), "w") for ih in ihs: dump_hierarchy_to_file(hierch_file, ih, 0) print("File written to {}".format(hierch_file.name)) hierch_file.close() return vtables = [] for cur_name in all_names: ea = cur_name[0] name = cur_name[1] if "ZTV" in name: vtables.append(cur_name) struct_file = open( "{}/iOS/Scripts/structs_from_vtabs.h".format(str(Path.home())), "w") is_standalone_kext = ida_kernwin.ask_yn(0, "Standalone kext?") if is_standalone_kext: # If this is from a standalone kext, I need to write some # definitions for common objects that follow my struct format, # otherwise, things get really screwed struct_file.write( "struct __cppobj ExpansionData {};\n\n" "struct __cppobj OSMetaClassBase_vtbl;\n\n" "struct __cppobj OSMetaClassBase_mbrs {};\n\n" "struct __cppobj OSMetaClassBase {\n" "\tOSMetaClassBase_vtbl *__vftable /*VFT*/;\n" "\tOSMetaClassBase_mbrs m;\n" "};\n\n" "struct __cppobj OSObject_mbrs : OSMetaClassBase_mbrs {\n" "\tint retainCount;\n" "};\n\n" "struct __cppobj OSObject_vtbl : OSMetaClassBase_vtbl {};\n\n" "struct __cppobj OSObject {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSObject_mbrs m;\n" "};\n\n" "struct __cppobj OSMetaClass_mbrs : OSMetaClassBase_mbrs {\n" "\tExpansionData *reserved;\n" "\tconst OSMetaClass *superClassLink;\n" "\tconst OSSymbol *className;\n" "\tunsigned int classSize;\n" "\tunsigned int instanceCount;\n" "};\n\n" "struct __cppobj OSMetaClass {\n" "\tOSMetaClassBase_vtbl *__vftable;\n" "\tOSMetaClass_mbrs m;\n" "};\n\n" "struct __cppobj OSCollection_vtbl;\n" "struct __cppobj OSCollection_mbrs : OSObject_mbrs {\n" "\tunsigned int updateStamp;\n" "\tunsigned int fOptions;\n" "};\n\n" "struct __cppobj OSCollection {\n" "\tOSCollection_vtbl *__vftable;\n" "\tOSCollection_mbrs m;\n" "};\n\n" "struct __cppobj OSArray_vtbl;\n" "struct __cppobj OSArray_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tvoid *array;\n" "};\n\n" "struct __cppobj OSArray {\n" "\tOSArray_vtbl *__vftable;\n" "\tOSArray_mbrs m;\n" "};\n\n" "struct __cppobj OSDictionary::dictEntry {\n" "\tconst OSSymbol *key;\n" "\tconst OSMetaClassBase *value;\n" "};\n\n" "struct __cppobj OSDictionary_vtbl;\n" "struct __cppobj OSDictionary_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tOSDictionary::dictEntry *dict;\n" "};\n\n" "struct __cppobj OSDictionary {\n" "\tOSDictionary_vtbl *__vftable;\n" "\tOSDictionary_mbrs m;\n" "};\n\n" "struct __cppobj OSSet_vtbl;\n" "struct __cppobj OSSet_mbrs : OSCollection_mbrs {\n" "\tOSArray *members;\n" "};\n\n" "struct __cppobj OSSet {\n" "\tOSSet_vtbl *__vftable;\n" "\tOSSet_mbrs m;\n" "};\n\n" "struct __cppobj OSString_mbrs : OSObject_mbrs {\n" "\tunsigned __int32 flags : 14;\n" "\tunsigned __int32 length : 18;\n" "\tchar *string;\n" "};\n\n" "struct __cppobj OSString {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSString_mbrs m;\n" "};\n\n" "struct __cppobj OSSymbol : OSString {};\n\n") num_failed_get_type = 0 cnt = 0 for vtable in vtables: demangled_name = idc.demangle_name(vtable[1], get_inf_attr(idc.INF_LONG_DN)) # unless this is a vtable for OSMetaClassBase, OSMetaClassMeta, # or OSMetaClass, skip anything metaclass related if "::MetaClass" in demangled_name: continue class_name = ida_name.extract_name(demangled_name, len("`vtable for'")) if class_name in BLACKLIST: continue ea = vtable[0] #+ 8; while ida_bytes.get_qword(ea) == 0: ea += 8 # print("EA: {}".format(hex(ea))) if is_unknown(ida_bytes.get_flags(ea)): continue # if class_name != "IOSkywalkPacket": # continue # if class_name != "AHTHSBufferStatic": # continue # if class_name != "HSMSPITest": # continue # if class_name != "AppleMesa": # continue # if class_name != "AppleUSBHostController": # continue # if class_name != "AppleEmbeddedPCIE": # continue # if class_name != "SimpleEval": # continue # if class_name != "AppleUSBCDCControl": # continue # if class_name != "IOHDCP2TransmitterAuthSession": # continue # if class_name != "IOAVService::DisplayIDParser::readDisplayID": # continue # if class_name != "IOMFB::TypedProp": # continue # if class_name != "IOMFB::UPBlock_GenPipe_v2": # continue # if class_name != "AppleMesaSEPDriver": # continue # if class_name != "IOAVController": # continue # if class_name != "AppleConvergedIPCICEBBBTIInterface": # continue # if class_name != "ApplePPM": # continue cnt += 1 # print("{}".format(class_name)) # skip NULL pointers until we hit a function while ida_bytes.get_qword(ea) == 0: ea += 8 num_virts = 0 num_dtors = 0 num_untyped = 0 num_noname = 0 fxn_name_list = [] fxn_name_dict = {} struct_fields = [] fwd_decls = set() fwd_decls.add(class_name) svt = SymbolicatedVtable(class_name) ioc = IOKitClass(svt) ioc.svt = svt # vtables seem to be NULL terminated while True: fxn_ea = ida_bytes.get_qword(ea) # end of vtable for this class if fxn_ea == 0: break # print("Type for {}/{} @ {}: {}".format(hex(fxn_ea), fxn_name, hex(ea), fxn_type)) # if fxn_type == None: # num_failed_get_type += 1 # ea += 8 # continue # default to this for ___cxa_pure_virtual fxn_args = "void" fxn_call_conv = "" fxn_mangled_name = ida_name.get_ea_name(fxn_ea) fxn_name = ida_name.demangle_name(fxn_mangled_name, get_inf_attr(idc.INF_LONG_DN)) if fxn_name == None: # ___cxa_pure_virtual fxn_name = ida_name.get_ea_name(fxn_ea) # some other thing? if len(fxn_name) == 0: fxn_name = "noname{}".format(num_noname) else: fxn_type = idc.get_type(fxn_ea) if fxn_type == None: # sometimes this happens, don't know why # the only thing fxn_type would have provided was # the calling convention, so we need to manually # reconstruct parameter list, and assume calling # convention is __fastcall # if mangled_fxn_name == "__ZN25IOGeneralMemoryDescriptor7doUnmapEP7_vm_mapyy": # exit(0) fxn_return_type = "__int64" fxn_call_conv = "__fastcall" fxn_args_string = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # if fxn_args_string == "IOService *, unsigned int, void *, void (*)(OSObject *, AppleUSBCDCControl*, void *, USBCDCNotification *)": # # print("Hello") # fxn_args_string = "IOService *, unsigned int, void *, void (*)(OSObject *, void (*)(TestType *, AppleUSBCDCControl*, void *, USBCDCNotification *), AppleUSBCDCControl*, void *, USBCDCNotification *)" # print("fxn args: {}".format(fxn_args_string)) # if fxn_args_string == "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *": # fxn_args_string = "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *, unsigned long long" fxn_args_string = fxn_args_string.replace( "{block_pointer}", "*") if fxn_args_string.find(",") != -1: # print("More than one arg: {}".format(fxn_args_list)) # extra comma makes the parser happy fxn_args_types_list = get_arg_type_list( fxn_args_string + ",") # print("More than one arg for {}: {}".format(fxn_name, fxn_args_types_list)) # print() fxn_args = "" argnum = 0 # print(type(fxn_args_types_list)) to_fwd_decl = get_fwd_decls(fxn_args_types_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) for arg_type in fxn_args_types_list: if argnum == 0: fxn_args += "{} *__hidden this, ".format( class_name) else: fxn_args += "{}, ".format(fix_type(arg_type)) argnum += 1 fxn_args = fxn_args[:-2] else: fxn_args = "{} *__hidden this".format(class_name) arg_type = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # print("Only one arg for {}: {}".format(fxn_name, arg_type)) arg_type_list = [arg_type] # print("Only one arg: {}".format(arg_type_list)) to_fwd_decl = get_fwd_decls(arg_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) if arg_type != "void" and len(arg_type) > 0: fxn_args += ", {}".format(fix_type(arg_type)) else: all_except_args = fxn_type[:fxn_type.find("(")] # first, if there's no spaces, there's no calling # convention specifed if all_except_args.find(" ") == -1: fxn_return_type = all_except_args # Also, this having no spaces could mean IDA messed # up, so we should use the demangled name instead # and parse that fxn_type = "(" + fxn_name[fxn_name.find("(") + 1:] # print("No spaces in args, using {} as fxn_type".format(fxn_type)) else: double_underscore = all_except_args.rfind("__") if double_underscore != -1: fxn_return_type = all_except_args[: double_underscore] fxn_call_conv = all_except_args[double_underscore:] else: fxn_return_type = all_except_args # get args # print("fxn_type: {}".format(fxn_type)) fxn_args = fxn_type[fxn_type.find("(") + 1:fxn_type.rfind(")")] fxn_args_type_list = get_arg_type_list(fxn_args + ",") fixed_fxn_args_type_list = [] # Fix up args for arg_type in fxn_args_type_list: # Remove __hidden arg_type = arg_type.replace("__hidden", "") # Check for a pointer. This is an easy case, we # just delete everything from the first * star = arg_type.find("*") if star != -1: arg_type = arg_type[0:star] else: # Otherwise, find the last space, and delete # from there # But in case there was no name for this # parameter, check if the token after the last # space is not an IDA type or primitive type lspace = arg_type.rfind(" ") if lspace != -1: token = arg_type[lspace:].replace(" ", "") if not is_primitive_type( token) and not is_ida_type(token): arg_type = arg_type[0:lspace] # print("arg_type: {}".format(arg_type)) fixed_fxn_args_type_list.append(arg_type) # to_fwd_decl = get_fwd_decls(fxn_args_type_list) to_fwd_decl = get_fwd_decls(fixed_fxn_args_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) # print("fxn_type is not None for {}: fxn args: {}".format(fxn_name, fxn_args_type_list)) # print("fxn_type is not None: will fwd declare: {}".format(to_fwd_decl)) # get function name # remove 'classname::' and params fxn_name = fxn_name[fxn_name.find("::") + 2:fxn_name.find("(") + 1] fxn_name = fxn_name[:fxn_name.find("(")] # replace any '~' fxn_name = fxn_name.replace("~", "DTOR{}_".format(num_dtors)) # remove any < and > fxn_name = fxn_name.replace("<", "") fxn_name = fxn_name.replace(">", "") if fxn_name in list(fxn_name_dict.keys()): fxn_name_dict[fxn_name] += 1 fxn_name += "_{}".format(fxn_name_dict[fxn_name]) else: fxn_name_dict[fxn_name] = -1 if "DTOR" in fxn_name: num_dtors += 1 curfield = "" if fxn_name == "___cxa_pure_virtual": # struct_fields.append("\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format(num_virts, # fxn_args)) curfield = "\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format( num_virts, fxn_args) num_virts += 1 else: # struct_fields.append("\t{} ({} *{})({});".format(fxn_return_type, # fxn_call_conv, fxn_name, fxn_args)) curfield = "\t{} ({} *{})({});".format(fxn_return_type, fxn_call_conv, fxn_name, fxn_args) svt.add(curfield) ea += 8 # return # Some classes won't have xrefs to OSMetaClass::OSMetaClass, # like OSMetaClassBase if class_name in ih_dict: ih_dict[class_name].ioc = ioc # Just write forward decls for now for decl in fwd_decls: struct_file.write("struct __cppobj {};\n".format(decl)) struct_file.write("\n") # cnt += 1 # if cnt == 5: # break print("{} IOKit vtables".format(len(vtables))) # Now create the header file to import into IDA # for ih in ihs: # dump_ih(ih, 0) generate_header_file(struct_file, ihs) # fixup_header_file(struct_file) struct_file.close()
def get_func_name(ea): name = ida_funcs.get_func_name(ea) demangled = ida_name.demangle_name(name, idc.get_inf_attr(idc.INF_LONG_DN)) return demangled or name