Beispiel #1
0
 def __call__(self):
     struc = ida_struct.get_struc_id(Event.encode(self.sname))
     sptr = ida_struct.get_struc(struc)
     cmt = Event.encode(self.cmt if self.cmt else "")
     if self.smname:
         mptr = ida_struct.get_member_by_name(sptr,
                                              Event.encode(self.smname))
         ida_struct.set_member_cmt(mptr, cmt, self.repeatable_cmt)
     else:
         ida_struct.set_struc_cmt(sptr.id, cmt, self.repeatable_cmt)
Beispiel #2
0
    def _prepare_ida_type(self):
        sptr = self.struct.sptr

        # Create structure member
        if ida_struct.add_struc_member(
                sptr, self.name, self.offset, 0, None,
                self.size) != ida_struct.STRUC_ERROR_MEMBER_OK:
            raise RuntimeError("Could not create struct member '{}'".format(
                self.name))

        # Get member pointer
        self.mptr = ida_struct.get_member_by_name(sptr, self.name)

        # set type
        ida_struct.set_member_tinfo(sptr, self.mptr, self.offset, self.tif, 0)

        self.log.info("Created struct member '%s' of size %d at offset %d",
                      self.hierarchy, self.size, self.offset)
    def _record_func_args(self):
        """
        Reset stack pointer reference and
        record function argument variables if we are executing the beginning of a function.
        """
        func_obj = ida_funcs.get_func(self.ip)
        if not func_obj or func_obj.start_ea != self.ip:
            return

        # Reset the sp_start
        self._cpu_context._sp_start = self._cpu_context.sp

        # Add the passed in arguments to the variables map.
        # (This also helps to standardize the argument names to "a*" instead of "arg_*")
        for arg in self._cpu_context.passed_in_args:
            addr = arg.addr
            # TODO: Support variables from registers?
            if addr is not None:
                if arg.is_stack:
                    try:
                        frame = ida_frame.get_frame(func_obj)
                        if not frame:
                            logger.warning(f"Failed to get frame for function argument: {repr(arg)}")
                            continue

                        # Getting member stack offset from name is more reliable then calculating
                        # it from the address.
                        member = ida_struct.get_member_by_name(frame, arg.name)
                        if not member:
                            logger.warning(f"Failed to get member for function argument: {repr(arg)}")
                            continue

                        self._cpu_context.variables.add(addr, frame_id=frame.id, stack_offset=member.soff)
                    except ValueError:
                        logger.warning(f"Failed to get stack information for function argument: {repr(arg)}")
                else:
                    self._cpu_context.variables.add(addr)
Beispiel #4
0
    def dump():
        ret = []

        for struct_idx, struct_id, struct_name in idautils.Structs():
            struct = ida_struct.get_struc(struct_id)

            members = [{
                'offset': offset,
                'name': name,
                'size': size
            } for offset, name, size in idautils.StructMembers(struct_id)]

            # Find all xrefs to any members of this struct.
            xrefs = []
            for offset, name, size in idautils.StructMembers(struct_id):
                member = ida_struct.get_member_by_name(struct, name)
                for xref in idautils.XrefsTo(member.id):
                    d = {
                        'from': xref.frm,
                        'type': xref.type,
                    }

                    # Get offset base if it's an offset xref.
                    if xref.type == 1:
                        d['offset'] = ida_offset.get_offbase(xref.frm, 1)

                    xrefs.append(d)

            ret.append({
                'idx': struct_idx,
                'name': struct_name,
                'members': members,
                'xrefs': xrefs,
            })

        return ret
Beispiel #5
0
    def execute(self, start=None, end=None, max_instructions=10000):
        """
        "Execute" the instruction at IP and store results in the context.
        The RIP/EIP register will be set to the value supplied in IP so that it is
        correct.

        :param start: instruction address to start execution (defaults to currently set ip)
        :param end: instruction to stop execution (not including)
            (defaults to only run start)
        :param max_instructions: Maximum number of instructions to execute before
            raising an RuntimeError

        :raises RuntimeError: If maximum number of instructions get hit.
        """
        if not start:
            start = self.ip

        # Set instruction pointer to where we are currently executing.
        self.ip = start

        # Extra processing if we are at the start of a function.
        func_obj = ida_funcs.get_func(self.ip)
        if func_obj.start_ea == self.ip:
            # Reset the sp_start
            self._sp_start = self.sp

            # Add the passed in arguments to the variables map.
            # (This also helps to standardize the argument names to "a*" instead of "arg_*")
            for arg in self.passed_in_args:
                addr = arg.addr
                # TODO: Support variables from registers?
                if addr is not None:
                    if arg.is_stack:
                        try:
                            frame = ida_frame.get_frame(func_obj)
                            if not frame:
                                logger.warning(
                                    f"Failed to get frame for function argument: {repr(arg)}"
                                )
                                continue

                            # Getting member stack offset from name is more reliable then calculating
                            # it from the address.
                            member = ida_struct.get_member_by_name(
                                frame, arg.name)
                            if not member:
                                logger.warning(
                                    f"Failed to get member for function argument: {repr(arg)}"
                                )
                                continue

                            self.variables.add(addr,
                                               frame_id=frame.id,
                                               stack_offset=member.soff)
                        except ValueError:
                            logger.warning(
                                f"Failed to get stack information for function argument: {repr(arg)}"
                            )
                    else:
                        self.variables.add(addr)

        # If end is provided, recursively run execute() until ip is end.
        if end is not None:
            count = max_instructions
            prev_ecx = self.registers.ecx
            prev_ecx_count = count
            while self.ip != end:
                if ida_ua.print_insn_mnem(self.ip) == 'retn':
                    return
                self.execute()
                # TODO: Re-enable this feature after rigorous testing.
                # # Dynamically allow more instructions to be executed if we detect we are in a loop
                # # and it is making progress.
                # # Ie. this will allow most while statements not to ding our max instruction quota.
                # if self.registers.ecx and (self.registers.ecx < prev_ecx or prev_ecx == 0):
                #     if prev_ecx:
                #         count += min(prev_ecx_count - count, 1000)
                #     prev_ecx = self.registers.ecx
                #     prev_ecx_count = count
                count -= 1
                if not count:
                    raise RuntimeError('Hit maximum number of instructions.')
            return

        # Determine if a rep* instruction and add termination condition.
        term_condition = None
        if idc.get_wide_byte(start) in (0xF2, 0xF3):
            insn = idc.GetDisasm(
                start)  # IDA pro never has operands for rep opcodes.
            if insn.startswith("rep "):
                term_condition = lambda: self.registers.ecx == 0
                term_condition.unconditional = True
            elif insn.startswith(("repe ", "repz ")):
                term_condition = lambda: self.registers.ecx == 0 or self.registers.zf == 0
                term_condition.unconditional = False
            elif insn.startswith(("repne ", "repnz ")):
                term_condition = lambda: self.registers.ecx == 0 or self.registers.zf == 1
                term_condition.unconditional = False

        # Emulate instruction.
        mnem = ida_ua.print_insn_mnem(start)

        # Log a header line for debug messages of this instruction.
        # This is simpler and faster then trying to include the information at each log line
        logger.debug("[0x%X %03X] :: %s", start, self.sp_diff, mnem)

        # Run any pre-hooks first.
        self.execute_instruction_hooks(start, mnem, pre=True)

        instruction = self.OPCODES.get(mnem)
        if instruction:
            operands = self.operands

            try:
                if term_condition:
                    if self.emulator.disabled_rep:
                        logger.debug("Ignoring rep instructions: DISABLED.")

                    # As a safety measure, don't allow rep instructions to surpass
                    # our max memory read limit.
                    # Only do this check if the terminating condition is unconditional, otherwise
                    # this number usually big because it expects zf to be toggled.
                    elif term_condition.unconditional and self.registers.ecx > self.memory.MAX_MEM_READ:
                        logger.warning(
                            "Emulation attempted to read %s instruction %d times. "
                            "Ignoring instruction.", mnem, self.registers.ecx)
                    else:
                        logger.debug("Emulating %s instruction %d times.",
                                     mnem, self.registers.ecx)
                        count = 0
                        while not term_condition():
                            instruction(self, start, mnem, operands)
                            self.registers.ecx -= 1
                            # Stop if we are iterating too much.
                            count += 1
                            if count > self.memory.MAX_MEM_READ:
                                logger.warning(
                                    "Looped too many times, exiting prematurely."
                                )
                                break
                else:
                    instruction(self, start, mnem, operands)
            except Exception:
                logger.exception("Failed to execute address 0x%X: %s", start,
                                 idc.GetDisasm(start))
        else:
            logger.debug("%s instruction not implemented.", mnem)

        # Record executed instruction.
        self.executed_instructions.append(start)

        # Run any post-hooks
        self.execute_instruction_hooks(start, mnem, pre=False)

        # Add a blank space to help visually separate logs for each instruction.
        logger.debug(" ")

        # After execution, set instruction pointer to next instruction assuming
        # standard code flow and if no jump was made.
        if self.ip == start:
            self.ip = idc.next_head(start)
Beispiel #6
0
def add_to_struct(
    struct,
    member_name,
    member_type=None,
    offset=BADADDR,
    is_offset=False,
    overwrite=False,
):
    mt = None
    flag = idaapi.FF_DWORD
    member_size = WORD_LEN
    if member_type is not None and (member_type.is_struct()
                                    or member_type.is_union()):
        logging.debug("Is struct!")
        substruct = extract_struct_from_tinfo(member_type)
        if substruct is not None:
            flag = idaapi.FF_STRUCT
            mt = ida_nalt.opinfo_t()
            mt.tid = substruct.id
            logging.debug(
                f"Is struct: {ida_struct.get_struc_name(substruct.id)}/{substruct.id}"
            )
            member_size = ida_struct.get_struc_size(substruct.id)
    elif WORD_LEN == 4:
        flag = idaapi.FF_DWORD
    elif WORD_LEN == 8:
        flag = idaapi.FF_QWORD
    if is_offset:
        flag |= idaapi.FF_0OFF
        mt = ida_nalt.opinfo_t()
        r = ida_nalt.refinfo_t()
        r.init(
            ida_nalt.get_reftype_by_size(WORD_LEN) | ida_nalt.REFINFO_NOBASE)
        mt.ri = r

    new_member_name = member_name
    member_ptr = ida_struct.get_member(struct, offset)
    if overwrite and member_ptr:
        if ida_struct.get_member_name(member_ptr.id) != member_name:
            logging.debug("Overwriting!")
            ret_val = ida_struct.set_member_name(struct, offset, member_name)
            i = 0
            while ret_val == ida_struct.STRUC_ERROR_MEMBER_NAME:
                new_member_name = "%s_%d" % (member_name, i)
                i += 1
                if i > 250:
                    logging.debug("failed change name")
                    return
                ret_val = ida_struct.set_member_name(struct, offset,
                                                     new_member_name)

    else:
        ret_val = ida_struct.add_struc_member(struct, new_member_name, offset,
                                              flag, mt, member_size)
        i = 0
        while ret_val == ida_struct.STRUC_ERROR_MEMBER_NAME:
            new_member_name = "%s_%d" % (member_name, i)
            i += 1
            if i > 250:
                return
            ret_val = ida_struct.add_struc_member(struct, new_member_name,
                                                  offset, flag, mt,
                                                  member_size)
        if ret_val != 0:
            logging.debug(f"ret_val: {ret_val}")
        member_ptr = ida_struct.get_member_by_name(struct, new_member_name)
    if member_type is not None and member_ptr is not None:
        ida_struct.set_member_tinfo(struct, member_ptr, 0, member_type,
                                    idaapi.TINFO_DEFINITE)
    return member_ptr