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)
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)
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
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)
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