Example #1
0
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
Example #2
0
 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)
Example #3
0
 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
Example #4
0
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
Example #5
0
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 ""
Example #6
0
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