def build_stack_variable(func_ea): stack_vars = dict() frame = idc.GetFrame(func_ea) if not frame: return stack_vars f_name = get_symbol_name(func_ea) #grab the offset of the stored frame pointer, so that #we can correlate offsets correctly in referent code # e.g., EBP+(-0x4) will match up to the -0x4 offset delta = idc.GetMemberOffset(frame, " s") if delta == -1: delta = 0 if f_name not in _FUNC_UNSAFE_LIST: offset = idc.GetFirstMember(frame) while -1 != _signed_from_unsigned(offset): member_name = idc.GetMemberName(frame, offset) if member_name is None: offset = idc.GetStrucNextOff(frame, offset) continue if (member_name == " r" or member_name == " s"): offset = idc.GetStrucNextOff(frame, offset) continue member_size = idc.GetMemberSize(frame, offset) if offset >= delta: offset = idc.GetStrucNextOff(frame, offset) continue member_flag = idc.GetMemberFlag(frame, offset) flag_str = _get_flags_from_bits(member_flag) member_offset = offset-delta stack_vars[member_offset] = {"name": member_name, "size": member_size, "flags": flag_str, "writes": list(), "referent": list(), "reads": list(), "safe": False } offset = idc.GetStrucNextOff(frame, offset) else: offset = idc.GetFirstMember(frame) frame_size = idc.GetFunctionAttr(func_ea, idc.FUNCATTR_FRSIZE) flag_str = "" member_offset = _signed_from_unsigned(offset) - delta stack_vars[member_offset] = {"name": f_name, "size": frame_size, "flags": flag_str, "writes": list(), "referent": list(), "reads": list(), "safe": False } return stack_vars
def get_function_sid(self, in_stack, local_size=1): if not in_stack: return 'create_struct_complex', idc.AddStrucEx(0, 'create_struct_complex', 0) ea = yaunit.get_next_function(lambda ea : yaunit.has_locals(ea, local_size)) frame = idaapi.get_frame(ea) self.assertNotEqual(frame, None) offset = idc.GetFirstMember(frame.id) while offset != idaapi.BADADDR: idc.DelStrucMember(frame.id, offset) offset = idc.GetFirstMember(frame.id) return ea, frame.id
def yatest_create_struct_in_stack_vars_with_renaming(self): """ test creation of struct from stack vars used to find a bug (structure is correctly applied on var if renamed) """ # create structure ident, sida = self.get_function_sid_without_del( True, local_size=complex_struc3_size, count_from_first_var=True) self.assertNotEqual(sida, -1) sidb = idc.AddStrucEx(0, 'create_struct_in_stack_vars_with_renaming', 0) self.assertNotEqual(sidb, -1) size = self.create_complex2(sidb, complex_struc3) self.assertEqual(complex_struc3_size, size) # set first var prototype offset = idc.GetFirstMember(sida) member_id = idc.GetMemberId(sida, offset) self.assertNotEqual(member_id, -1) self.assertTrue( idc.SetType(member_id, "create_struct_in_stack_vars_with_renaming* x;")) self.assertEqual("create_struct_in_stack_vars_with_renaming *", idc.GetType(idc.GetMemberId(sida, offset))) idc.SetMemberName(sida, offset, "var1") yaunit.save("create_struct_in_stack_vars_with_renaming", sida) yaunit.save("create_struct_in_stack_vars_with_renaming_offset", offset)
def get_object_id_of_union_member_id(hash_provider, member_id): try: return union_member_object_ids[member_id] except KeyError: idx = idc.GetFirstStrucIdx() while idx != idc.BADADDR: struc_id = idc.GetStrucId(idx) if idc.IsUnion(struc_id): offset = idc.GetFirstMember(struc_id) while offset != idc.BADADDR: smember_id = idc.GetMemberId(struc_id, offset) if smember_id == member_id: name = idc.GetMemberName(struc_id, offset) if name is not None: struc_name = idc.GetStrucName(struc_id) logger.debug("found member id 0x%016X in union %s/%s" % (member_id, struc_name, name)) return hash_provider.get_struc_member_id_for_name(struc_name, offset) # next member offset = idc.GetStrucNextOff(struc_id, offset) idx = idc.GetNextStrucIdx(idx) logger.error("Could not find member id 0x%016X in unions" % member_id) return None
def getFunctionArgumentCount(ea): ''' Bit of a hack, since IDA doesn't seem to have a good way to get this information. Gets the frame for a given function, and counts named members following the 'r' member. Note: IDA won't create a frame member for an unreferenced function arg... so you've been warned. ''' rFound = False argCount = 0 sid = idc.GetFrame(ea) midx = idc.GetFirstMember(sid) while midx != idc.BADADDR: name = idc.GetMemberName(sid, midx) if rFound and name is not None: argCount += 1 #print 'Found arg at 0x%x: "%s"' % (midx, name) elif name == ' r': #print 'Found r at 0x%x:' % midx rFound = True else: #print 'Found nonarg at 0x%x: "%s"' % (midx, name) pass midx = idc.GetStrucNextOff(sid, midx) return argCount
def get_args(f): local_variables = [] arguments = [] current = local_variables frame = idc.GetFrame(f) arg_string = "" if frame == None: return None start = idc.GetFirstMember(frame) end = idc.GetLastMember(frame) count = 0 max_count = 10000 args_str = "" while start <= end and count <= max_count: size = idc.GetMemberSize(frame, start) count = count + 1 if size == None: start = start + 1 continue name = idc.GetMemberName(frame, start) start += size if name in [" r", " s"]: # Skip return address and base pointer current = arguments continue arg_string += " " + name current.append(name) if len(arguments) == 0: arguments.append("void") return arguments
def get_function_sid_without_del(self, in_stack, local_size=1, count_from_first_var=False): if not in_stack: return 'create_struct_complex', idc.AddStrucEx(0, 'create_struct_complex', 0) ea = yaunit.get_next_function(lambda ea : yaunit.has_locals(ea, local_size, count_from_first_var)) frame = idaapi.get_frame(ea) self.assertNotEqual(frame, None) offset = idc.GetFirstMember(frame.id) return ea, frame.id
def hasStackVars(ea): """ :param ea: address of the function :return: whether the function has stack variables or not """ id = idc.GetFrame(ea) firstMember = idc.GetFirstMember(id) return firstMember != idaapi.BADADDR and firstMember != -1
def build_stack_args(f): stackArgs = dict() name = idc.Name(f) end = idc.GetFunctionAttr(f, idc.FUNCATTR_END) _locals = idc.GetFunctionAttr(f, idc.FUNCATTR_FRSIZE) _uses_bp = 0 != (idc.GetFunctionFlags(f) & idc.FUNC_FRAME) frame = idc.GetFrame(f) if frame is None: return stackArgs func_type = idc.GetType(f) if (func_type is not None) and ("(" in func_type): args = func_type[ func_type.index('(')+1: func_type.rindex(')') ] args_list = [ x.strip() for x in args.split(',')] if "..." in args_list: return stackArgs if name in RECOVER_DEBUG_FL: return stackArgs #grab the offset of the stored frame pointer, so that #we can correlate offsets correctly in referent code # e.g., EBP+(-0x4) will match up to the -0x4 offset delta = idc.GetMemberOffset(frame, " s") if -1 == delta: #indicates that it wasn't found. Unsure exactly what to do # in that case, punting for now delta = 0 offset = idc.GetFirstMember(frame) while -1 != _signed_from_unsigned(offset): memberName = idc.GetMemberName(frame, offset) if memberName is None: # gaps in stack usage are fine, but generate trash output # gaps also could indicate a buffer that IDA doesn't recognize offset = idc.GetStrucNextOff(frame, offset) continue if (memberName == " r" or memberName == " s"): #the return pointer and start pointer, who cares offset = idc.GetStrucNextOff(frame, offset) continue memberSize = idc.GetMemberSize(frame, offset) if offset >= delta: offset = idc.GetStrucNextOff(frame, offset) continue memberFlag = idc.GetMemberFlag(frame, offset) #TODO: handle the case where a struct is encountered (FF_STRU flag) flag_str = _get_flags_from_bits(memberFlag) stackArgs[offset-delta] = {"name":memberName, "size":memberSize, "flags":flag_str, "writes":list(), "referent":list(), "reads":list(), "safe": False} offset = idc.GetStrucNextOff(frame, offset) return stackArgs
def getStackVarDisasm(self): """ if the function uses stack variables with SP, their symbols should be defined :return: """ disasm = '' id = idc.GetFrame(self.func_ea) firstMember = idc.GetFirstMember(id) if hasStackVars(self.func_ea): # first, obtain the base by finding an instruction that uses one of the stack variables stackVars = getStackVars(self.func_ea) ea = self.func_ea base = -1 # TODO: maybe use get_min_spd_ea(func_ea) to get the base pointer? this stands for stack pointer delta! # search function instructions to find base (TODO: hacky, but i dunno how else to find base yet) while ea < self.func_ea + self.getSize(): d = Data.Data(ea) origDisasm = d.getOrigDisasm() # case where the stack frame is referenced for var, offset in stackVars: if var in origDisasm and '#' in origDisasm: # cases like LDR SP, [base+var_xx] if '[' in origDisasm: # grab the base if '+' in origDisasm: base = int( origDisasm[origDisasm.index('#') + 1:origDisasm.index('+')], 16) else: base = 0 # obtained base! no need to continue looping break # some cases like ADD SP, base+var_xx don't have '[' elif '+' in origDisasm: base = int( origDisasm[origDisasm.index('#') + 1:origDisasm.index('+')], 16) # obtained base! no need to continue looping break if base != -1: break ea += d.getSize() # if base couldn't be found still, it's likely no SP access is done with variables if base == -1: base = 0 # build up disasm based on stack vars using base-relative offsets for name, off in stackVars: relOff = base - off if relOff > 0: disasm += ".equ %s, -0x%X\n" % (name, abs(relOff)) else: disasm += ".equ %s, 0x%X\n" % (name, abs(relOff)) return disasm
def add_member_descr(structure, sid): """ Insert comments descripting every member of a structure whose id is sid Arguments: structure -- structure object holding data sid -- structure id """ if len(structure.members) == 0 or \ idc.GetMemberQty(sid) == 0: return members_map = {} for member in structure.members: members_map[member.name] = member m_offset = -1 for i in xrange(0, idc.GetMemberQty(sid)): # for each member of imported structure if i == 0: m_offset = idc.GetFirstMember(sid) else: m_offset = idc.GetStrucNextOff(sid, m_offset) if m_offset == -1 or m_offset == idaapi.BADADDR: break m_name = idc.GetMemberName(sid, m_offset) # None if not m_name: continue if m_name not in members_map: # A same member may have different name between msdn databases # and ida import structure table, ida may add some prefixes. if m_name[1:] in members_map: # start with '_' m_name = m_name[1:] elif re.match(r'^tag(.)*', m_name) and \ m_name[3:] in members_map: # start with 'tag' m_name = m_name[3:] elif re.match(r'^_tag(.)*', m_name) and \ m_name[4:] in members_map: # start with '_tag' m_name = m_name[4:] else: continue idc.SetMemberComment(sid, m_offset, format_comment(members_map[m_name].description), False)
def has_locals(ea, lvar_size=1, count_from_first_var=False): frame = idaapi.get_frame(ea) if frame is None or frame.memqty <= 1: return False if count_from_first_var: sida = frame.id offset = idc.GetFirstMember(sida) return idc.GetFrameLvarSize(ea) > lvar_size+offset else: return idc.GetFrameLvarSize(ea) > lvar_size
def getStackVars(ea, base=-1): # type: (int, int) -> list[(str, int)] """ Gets the stack variables associted with the function at ea If no base is specified, the offsets don't include the base calculation in them :param ea: the address of the function :param base: the stack base, must obtain to compute the offsets relative to it :return: a list of tuples, the stack variable name and its offset """ stackVars = [] id = idc.GetFrame(ea) firstMember = idc.GetFirstMember(id) # if the function has stack variables if firstMember != idaapi.BADADDR and firstMember != -1: # build up disasm based on stack vars lastMember = idc.GetLastMember(id) i = firstMember # Stack can be offset, first member might not be found at index 0, and all offsets must be adjusted by this foundFirstElement = False stackOffset = 0 while i <= lastMember: name = idc.GetMemberName(id, i) off = idc.GetMemberOffset( id, name ) # this is the offset in the struct... which isn't always consistent! size = idc.GetMemberSize(id, i) # append if varname is found (sometimes, None is returned because the variables are not in the next index) if name: # first variable found! this is the stack of the stack variables! if not foundFirstElement: stackOffset = i foundFirstElement = True if base == -1: # absolute offsets appended stackVars.append((name, off - stackOffset)) else: # base-relative offsets appended stackVars.append((name, base - off - stackOffset)) # sometimes, for some reason, the offset for stack variables does not follow linearly if size: i += size else: # reach next var, which might not be one size unit after the last... while not idc.GetMemberSize(id, i) and i <= lastMember: i += 1 return stackVars
def StructMembers(sid): """ Get a list of structure members information. @param sid: ID of the structure. @return: List of tuples (offset, name, size) @note: If 'sid' does not refer to a valid structure, an exception will be raised. """ off = idc.GetFirstMember(sid) if off == idaapi.BADNODE: raise Exception("No structure with ID: 0x%x" % sid) members = idc.GetMemberQty(sid) for idx in range(0, members): yield (off, idc.GetMemberName(sid, off), idc.GetMemberSize(sid, off)) off = idc.GetStrucNextOff(sid, off)
def StructMembers(sid): """ Get a list of structure members information (or stack vars if given a frame). @param sid: ID of the structure. @return: List of tuples (offset, name, size) @note: If 'sid' does not refer to a valid structure, an exception will be raised. @note: This will not return 'holes' in structures/stack frames; it only returns defined structure members. """ m = idc.GetFirstMember(sid) if m == -1: raise Exception("No structure with ID: 0x%x" % sid) while (m != idaapi.BADADDR): name = idc.GetMemberName(sid, m) if name: yield (m, name, idc.GetMemberSize(sid, m)) m = idc.GetStrucNextOff(sid, m)
def yatest_create_struct_in_stack_vars(self): """ test creation of struct from stack vars used to find a bug when creating struct for stack vars and naming vars """ # create structure ident, sida = self.get_function_sid_without_del(True, local_size=complex_struc3_size, count_from_first_var=True) self.assertNotEqual(sida, -1) sidb = idc.AddStrucEx(0, 'create_struct_in_stack_vars', 0) self.assertNotEqual(sidb, -1) size = self.create_complex2(sidb, complex_struc3) self.assertEqual(complex_struc3_size, size) # set first var prototype offset = idc.GetFirstMember(sida) member_id = idc.GetMemberId(sida, offset) self.assertNotEqual(member_id, -1) self.assertTrue(idc.SetType(member_id, "create_struct_in_stack_vars* x;")) self.assertEqual("create_struct_in_stack_vars *", idc.GetType(idc.GetMemberId(sida, offset))) yaunit.save("create_struct_in_stack_vars", sida) yaunit.save("create_struct_in_stack_vars_offset", offset)
def get_struc_id_from_member_if(member_id): try: return member_struc_ids[member_id] except KeyError: idx = idc.GetFirstStrucIdx() while idx != idc.BADADDR: struc_id = idc.GetStrucId(idx) if idc.IsUnion(struc_id): offset = idc.GetFirstMember(struc_id) while offset != idc.BADADDR: smember_id = idc.GetMemberId(struc_id, offset) if smember_id == member_id: member_struc_ids[member_id] = struc_id return struc_id offset = idc.GetStrucNextOff(struc_id, offset) idx = idc.GetNextStrucIdx(idx) logger.error("Could not find struc id from member id 0x%08X (name=%s)" % (member_id, idaapi.get_struc_name(member_id))) return None
def argCount(self): end = idc.GetFunctionAttr(self.addr, idc.FUNCATTR_END) start = idc.GetFunctionAttr(self.addr, idc.FUNCATTR_START) frame = idc.GetFrame(start) localv = idc.GetFunctionAttr(self.addr, idc.FUNCATTR_FRSIZE) frameSize = idc.GetFrameSize(start) #idc.GetStrucSize(frame) reg_off = 0 local_count = 0 arg_count = 0 sid = idc.GetFrame(self.addr) if sid: firstM = idc.GetFirstMember(sid) lastM = idc.GetLastMember(sid) arg_count = 0 if lastM - firstM > 0x1000: return for i in xrange(firstM, lastM): mName = idc.GetMemberName(sid, i) mSize = idc.GetMemberSize(sid, i) mFlag = idc.GetMemberFlag(sid, i) off = idc.GetMemberOffset(sid, mName) #print "%s: %d, %x, off=%x" % (mName, mSize, mFlag, off) if mName == " r": reg_off = off # XXX: just store the data, dont loop twice. for i in xrange(firstM, lastM): mName = idc.GetMemberName(sid, i) mSize = idc.GetMemberSize(sid, i) mFlag = idc.GetMemberFlag(sid, i) off = idc.GetMemberOffset(sid, mName) if off <= reg_off: local_count += 1 elif off > reg_off and reg_off != 0: arg_count += 1 if arg_count > 0: return arg_count / 4 elif arg_count == 0: return 0 # offset to return try: ret = idc.GetMemberOffset(frame, " r") except: if frameSize > localv: return (frameSize - localv) / 4 # error getting function frame (or none exists) return -1 if (ret < 0): if frameSize > localv: return (frameSize - localv) / 4 return -1 firstArg = ret + 4 args = frameSize - firstArg numArgs = args / 4 return numArgs
def _get_members_offset(self): off = idc.GetFirstMember(self.sid) for i in range(self.nb_member): yield off off = idc.GetStrucNextOff(self.sid, off)
def _get_members_offset(self): off = idc.GetFirstMember(self.sid) while off != idc.BADADDR: yield off off = idc.GetStrucNextOff(self.sid, off)