Exemple #1
0
    def trace(self, ea):
        '''
        Given an EA where an argument register is set, attempt to trace what
        function call that argument is passed to.

        @ea - The address of an instruction that modifies a function argument
        register.

        Returns a tuple of (function EA, argv index, argument register name) on
        success.
        Returns None on failure.
        '''
        insn = ida_shims.decode_insn(ea)
        features = ida_shims.get_canon_feature(insn)

        if self.arch.unknown:
            return (None, None, None)

        for n in range(0, len(self.CHANGE_OPND)):
            ops = ida_shims.get_operands(insn)
            if ops[n].type in [idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase]:
                try:
                    regname = self.arch.registers[ops[n].reg]
                    index = self.arch.argv.index(regname)
                except ValueError:
                    continue

                if features & self.CHANGE_OPND[n]:
                    ea = ea - (self.arch.delay_slot * self.arch.insn_size)

                    while True:
                        insn = ida_shims.decode_insn(ea)

                        if idaapi.is_call_insn(ea):
                            for xref in idautils.XrefsFrom(ea):
                                if xref.type in [idaapi.fl_CF, idaapi.fl_CN]:
                                    return (xref.to, index, regname)
                            # If we couldn't figure out where the function call
                            # was going to, just quit
                            break

                        try:
                            is_block_end = idaapi.is_basic_block_end(ea)
                        except TypeError:
                            is_block_end = idaapi.is_basic_block_end(ea, True)

                        if is_block_end:
                            break

                        # TODO: Use idc.NextHead(ea) instead...
                        ea += self.arch.insn_size

        return (None, None, None)
Exemple #2
0
    def argv(self, func):
        '''
        Attempts to identify what types of arguments are passed to a given
        function. Currently unused.
        '''
        args = [None for x in self.arch.argv]

        if not self.arch.unknown:
            start_ea = ida_shims.start_ea(func)
            for xref in idautils.XrefsTo(start_ea):
                if idaapi.is_call_insn(xref.frm):
                    insn = ida_shims.decode_insn(xref.frm)

                    ea = xref.frm + (self.arch.delay_slot *
                                     self.arch.insn_size)
                    end_ea = (xref.frm - (self.arch.insn_size * 10))

                    while ea >= end_ea:
                        if idaapi.is_basic_block_end(ea) or \
                                (ea != xref.frm and idaapi.is_call_insn(ea)):
                            break

                        insn = ida_shims.decode_insn(ea)
                        features = ida_shims.get_canon_feature(insn)

                        for n in range(0, len(self.CHANGE_OPND)):
                            ops = ida_shims.get_operands(insn)
                            if ops[n].type in [
                                    idaapi.o_reg, idaapi.o_displ,
                                    idaapi.o_phrase
                            ]:
                                try:
                                    regname = self.arch.registers[ops[n].reg]
                                    index = self.arch.argv.index(regname)
                                except ValueError:
                                    continue

                                if features & self.CHANGE_OPND[n]:
                                    for xref in idautils.XrefsFrom(ea):
                                        # TODO: Where is this xref type defined?
                                        if xref.type == 1:
                                            string = \
                                                ida_shims.get_strlit_contents(
                                                    xref.to)
                                            if string and len(string) > 4:
                                                args[index] = str
                                            break

                        ea -= self.arch.insn_size

                yield args
Exemple #3
0
    def _find_leafs(self):
        # Loop through every function
        for func_ea in idautils.Functions():
            # Count the number of xrefs to this function
            func = idaapi.get_func(func_ea)
            if func:
                leaf_function = True
                ea = ida_shims.start_ea(func)
                end_ea = ida_shims.end_ea(func)

                # Loop through all instructions in this function looking
                # for call instructions; if found, then this is not a leaf.
                while ea <= end_ea:
                    insn = ida_shims.decode_insn(ea)
                    if idaapi.is_call_insn(ea):
                        leaf_function = False
                        break

                    ea = ida_shims.next_head(ea)

                if leaf_function:
                    self.functions.append(
                        Function(start=ida_shims.start_ea(func),
                                 end=ida_shims.end_ea(func),
                                 leaf=True,
                                 loop=self.has_loop(func),
                                 argc=self.argp.argc(func)))

        # Sort leafs by xref count, largest first
        self.functions.sort(key=lambda f: f.xrefs, reverse=True)
Exemple #4
0
    def argc(self, function):
        '''
        Counts the number of arguments used by the specified function.
        '''
        argv = set()
        notargv = set()
        ea = ida_shims.start_ea(function)
        end_ea = ida_shims.end_ea(function)

        if self.arch.unknown:
            return 0

        while ea < end_ea:
            insn = ida_shims.decode_insn(ea)
            features = ida_shims.get_canon_feature(insn)

            for n in range(0, len(self.USE_OPND)):
                ops = ida_shims.get_operands(insn)
                if ops[n].type in [
                        idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase
                ]:
                    try:
                        regname = self.arch.registers[ops[n].reg]
                        index = self.arch.argv.index(regname)
                    except ValueError:
                        continue

                    if features & self.USE_OPND[n] and regname not in notargv:
                        argv.update(self.arch.argv[:index + 1])

            for n in range(0, len(self.CHANGE_OPND)):
                ops = ida_shims.get_operands(insn)
                if ops[n].type in [
                        idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase
                ]:
                    try:
                        regname = self.arch.registers[ops[n].reg]
                        index = self.arch.argv.index(regname)
                    except ValueError:
                        continue

                    if regname not in argv:
                        notargv.update(self.arch.argv[index:])

            if argv.union(notargv) == set(self.arch.argv):
                break

            # TODO: Use idc.NextHead(ea) instead...
            ea += self.arch.insn_size

        return len(argv)
Exemple #5
0
    def _is_bad_instruction(self, ea, bad_instructions=['j', 'b'],
                            no_clobber=[]):
        bad = False
        mnem = ida_shims.print_insn_mnem(ea)

        if mnem and mnem[0] in bad_instructions:
            bad = True
        else:
            insn = ida_shims.decode_insn(ea)
            feature = ida_shims.get_canon_feature(insn)
            for register in no_clobber:
                if (feature & idaapi.CF_CHG1) == idaapi.CF_CHG1:
                    if ida_shims.print_operand(ea, 0) == register:
                        bad = True

        return bad
Exemple #6
0
    def _fix_data_offsets(self):
        ea = 0
        count = 0

        print("Fixing unresolved offset xrefs...", end=' ')

        while ea != idaapi.BADADDR:
            (ea, n) = idaapi.find_notype(ea, idaapi.SEARCH_DOWN)
            if ida_shims.can_decode(ea):
                insn = ida_shims.decode_insn(ea)
                ops = ida_shims.get_operands(insn)
                for i in range(0, len(ops)):
                    op = ops[i]
                    if op.type == idaapi.o_imm and idaapi.getseg(op.value):
                        idaapi.add_dref(ea, op.value,
                                        (idaapi.dr_O | idaapi.XREF_USER))
                        count += 1

        print("created %d new data xrefs" % count)
Exemple #7
0
    def _does_instruction_match(self, ea, instruction, regex=False):
        i = 0
        op_cnt = 0
        op_ok_cnt = 0
        match = False
        insn = ida_shims.decode_insn(ea)
        mnem = ida_shims.print_insn_mnem(ea)
        if (not instruction.mnem) or (instruction.mnem == mnem) or \
                (regex and re.match(instruction.mnem, mnem)):
            for operand in instruction.operands:
                if operand:
                    op_cnt += 1
                    op = ida_shims.print_operand(ea, i)

                    if regex:
                        if re.match(operand, op):
                            op_ok_cnt += 1
                    elif operand == op:
                        op_ok_cnt += 1
                i += 1

            if op_cnt == op_ok_cnt:
                match = True
        return match
Exemple #8
0
    def block(self, block):
        '''
        Returns a tuple:
        ([formal, block, signatures], [fuzzy, block, signatures],
        set([unique, immediate, values]), [called, function, names])
        '''
        formal = []
        fuzzy = []
        functions = []
        immediates = []

        ea = ida_shims.start_ea(block)
        while ea < ida_shims.end_ea(block):
            insn = ida_shims.decode_insn(ea)

            # Get a list of all data/code refs from the current instruction
            drefs = [x for x in idautils.DataRefsFrom(ea)]
            crefs = [x for x in idautils.CodeRefsFrom(ea, False)]

            # Add all instruction mnemonics to the formal block hash
            formal.append(ida_shims.print_insn_mnem(ea))

            # If this is a call instruction, be sure to note the name of the
            # function being called. This is used to apply call-based
            # signatures to functions.
            #
            # For fuzzy signatures, we can't use the actual name or EA of the
            # function, but rather just want to note that a function call was
            # made.
            #
            # Formal signatures already have the call instruction mnemonic,
            # which is more specific than just saying that a call was made.
            if idaapi.is_call_insn(ea):
                for cref in crefs:
                    func_name = ida_shims.get_name(cref)
                    if func_name:
                        functions.append(func_name)
                        fuzzy.append("funcref")
            # If there are data references from the instruction, check to see
            # if any of them are strings. These are looked up in the
            # pre-generated strings dictionary.
            #
            # String values are easily identifiable, and are used as part of
            # both the fuzzy and the formal signatures.
            #
            # It is more difficult to determine if non-string values are
            # constants or not; for both fuzzy and formal signatures, just use
            # "data" to indicate that some data was referenced.
            elif drefs:
                for dref in drefs:
                    if self.strings.has_key(dref):
                        formal.append(self.strings[dref].value)
                        fuzzy.append(self.strings[dref].value)
                    else:
                        formal.append("dataref")
                        fuzzy.append("dataref")
            # If there are no data or code references from the instruction, use
            # every operand as part of the formal signature.
            #
            # Fuzzy signatures are only concerned with interesting immediate
            # values, that is, values that are greater than 65,535, are not
            # memory addresses, and are not displayed as negative values.
            elif not drefs and not crefs:
                ops = ida_shims.get_operands(insn)
                for n in range(0, len(ops)):
                    opnd_text = ida_shims.print_operand(ea, n)
                    formal.append(opnd_text)
                    if ops[n].type == idaapi.o_imm and \
                            not opnd_text.startswith('-'):
                        if ops[n].value >= 0xFFFF:
                            if ida_shims.get_full_flags(ops[n].value) == 0:
                                fuzzy.append(str(ops[n].value))
                                immediates.append(ops[n].value)

            ea = ida_shims.next_head(ea)

        return (self.sighash(''.join(formal)), self.sighash(''.join(fuzzy)),
                immediates, functions)
Exemple #9
0
    def _profile_function(self):
        current_ea = ida_shims.get_screen_ea()
        current_function = ida_shims.get_func_name(current_ea)
        current_function_ea = ida_shims.get_name_ea_simple(current_function)

        if current_function:
            self.function = current_function

        ea = ida_shims.get_func_attr(current_function_ea, idc.FUNCATTR_START)
        end_ea = ida_shims.get_func_attr(current_function_ea, idc.FUNCATTR_END)

        self.highlighted = ida_shims.get_highlighted_identifier()

        while ea < end_ea and ea != idc.BADADDR and self.highlighted:
            i = 0
            match = False
            optype = self.READ

            insn = ida_shims.decode_insn(ea)

            mnem = ida_shims.print_insn_mnem(ea)

            if self.highlighted in mnem:
                match = True
            elif idaapi.is_call_insn(ea):
                for xref in idautils.XrefsFrom(ea):
                    if xref.type != 21:
                        name = ida_shims.get_name(xref.to)
                        if name and self.highlighted in name:
                            match = True
                            break
            else:
                while True:
                    opnd = ida_shims.print_operand(ea, i)
                    if opnd:
                        if self.highlighted in opnd:
                            canon_feature = ida_shims.get_canon_feature(insn)
                            match = True
                            if canon_feature & self.OPND_WRITE_FLAGS[i]:
                                optype = self.WRITE
                        i += 1
                    else:
                        break

            if not match:
                comment = idc.GetCommentEx(ea, 0)
                if comment and self.highlighted in comment:
                    match = True
                else:
                    comment = idc.GetCommentEx(ea, 1)
                    if comment and self.highlighted in comment:
                        match = True
                    else:
                        comment = None

            if match:
                if ea > current_ea:
                    direction = self.DOWN
                elif ea < current_ea:
                    direction = self.UP
                else:
                    direction = self.THIS

                self.xrefs[ea] = {
                    'offset': ida_shims.get_func_off_str(ea),
                    'mnem': mnem,
                    'type': optype,
                    'direction': direction,
                    'text': idc.GetDisasm(ea),
                }

            ea = ida_shims.next_head(ea)
Exemple #10
0
    def _find_controllable_jumps(self, start_ea, end_ea):
        controllable_jumps = []
        t9_controls = [
            MIPSInstruction("move", "\$t9"),
            MIPSInstruction("addiu", "\$t9", "^\$"),
        ]
        t9_jumps = [
            MIPSInstruction("jr", "\$t9"),
        ]
        ra_controls = [
            MIPSInstruction("lw", "\$ra"),
        ]
        ra_jumps = [
            # TODO: Search for jumps to registers other than $ra.
            MIPSInstruction("jr", "\$ra"),
        ]
        t9_musnt_clobber = ["$t9"]
        ra_musnt_clobber = ["$ra"]

        if idaapi.IDA_SDK_VERSION >= 740:
            t9_jumps.append(MIPSInstruction("jalr", "\$ra"))
        else:
            t9_jumps.append(MIPSInstruction("jalr", "\$t9"))

        for possible_control_instruction in t9_controls + ra_controls:
            ea = start_ea

            if possible_control_instruction in t9_controls:
                jumps = t9_jumps
                musnt_clobber = t9_musnt_clobber
            else:
                jumps = ra_jumps
                musnt_clobber = ra_musnt_clobber

            while ea <= end_ea:
                ea = self._find_next_instruction_ea(
                    ea, possible_control_instruction, end_ea, regex=True)
                if ea != idc.BADADDR:
                    insn = ida_shims.decode_insn(ea)

                    control_instruction = self._get_instruction(ea)
                    control_register = control_instruction.operands[1]

                    if control_register:
                        for jump in jumps:
                            jump_ea = self._find_next_instruction_ea(
                                ea + insn.size,
                                jump,
                                end_ea,
                                no_baddies=True,
                                regex=True,
                                dont_overwrite=musnt_clobber)

                            if jump_ea != idc.BADADDR:
                                jump_instruction = self._get_instruction(
                                    jump_ea)
                                controllable_jumps.append(
                                    ROPGadget(control_instruction,
                                              jump_instruction,
                                              description="Controllable Jump",
                                              base=self.base))
                                ea = jump_ea

                    ea += insn.size

        return controllable_jumps