def _find_force_end(loc): """ Locate the first "ret" instruction down from the input location :param loc: Location a function is needed at :return: ItemEnd of the return location. """ loc = idc.NextHead(loc) while not idc.isAlign(idc.GetFlags(loc)) and not idaapi.get_func(loc): if "ret" in idc.GetMnem(loc): return idc.ItemEnd(loc) loc = idc.NextHead(loc) return idc.ItemEnd(idc.PrevHead(loc))
def _get_blocks_codes_per_func_iter(): """ Iterative function to generate all blocks and opcodes :return: N/A """ all_blocks = {} all_codes = {} all_opcodes = [] for func in idautils.Functions(): # blocks_in_func contains # <idaapi.BasicBlock object at 0x0545F3F0>, ... blocks_in_func = idaapi.FlowChart(idaapi.get_func(func)) blocks = [] for block in blocks_in_func: # IDA BUG! block.startEA == block.endEA?? # Should be in the range "block.startEA <= block < block.endEA" if block.startEA != block.endEA: blocks.append((block.startEA, block.endEA)) for head in idautils.Heads(block.startEA, block.endEA): ibytes = idc.GetManyBytes(head, idc.ItemEnd(head) - head) spd = idc.GetSpd(head) all_codes[head] = insn.Instruction(head, ibytes, spd) # IDA BUG! all_codes[head].bytes == 0?? # The size of the code should be positive if all_codes[head].bytes != 0: all_opcodes.append( (all_codes[head].addr, all_codes[head].addr + len(all_codes[head].bytes))) all_blocks[func] = blocks yield (all_blocks, all_opcodes)
def __init__(self, addr, endaddr=None): """endADDR: first addr not part of the element""" if endaddr is None: endaddr = idc.ItemEnd(addr) super(IDASizedElt, self).__init__(addr) self.endADDR = endaddr self.size = endaddr - addr
def get_code_and_blocks(ea): """Extracts the control flow graph for the function at the given address. Returns a dictionary with the instructions (ea->insn.Instruction) and a list of the basic blocs (bbl.BasicBlock).""" code = {} blocks = {} ida_blocks = set(idaapi.FlowChart(idaapi.get_func(ea))) for bb in ida_blocks: # XXX: it seems that it's not a bug but inter-function jumps! if bb.startEA == bb.endEA: # skip that .. it's IDA's bug #print "skipping block %x : %x in func %x"%(bb.startEA, bb.endEA, ea) continue blocks[bb.startEA] = bbl.BasicBlock(bb.startEA, bb.endEA, {}) for head in idautils.Heads(bb.startEA, bb.endEA): ibytes = idc.GetManyBytes(head, idc.ItemEnd(head) - head) spd = idc.GetSpd(head) code[head] = insn.Instruction(head, ibytes, spd) blocks[bb.startEA].instrs.append(code[head]) next_head = idc.NextHead(head, bb.endEA) if idaapi.isFlow(idc.GetFlags(next_head)): code[head].succ.add(next_head) for suc_bb in (s for s in bb.succs() if s.startEA != s.endEA): #assume head is the last instruction of the block code[head].succ.add(suc_bb.startEA) for bb in (b for b in ida_blocks if b.startEA != b.endEA): for suc_bb in (s for s in bb.succs() if s.startEA != s.endEA): # a jump with zero offset (like, jz 0) gives two succs to the same bb if blocks[suc_bb.startEA] not in blocks[bb.startEA].successors: blocks[bb.startEA].successors.append(blocks[suc_bb.startEA]) blocks[bb.startEA].successors.sort(key=lambda x: x.begin, reverse=True) #FIXME: find a better way .. for block in blocks.itervalues(): if block.instrs[0].addr == ea: #print "found the entry!:", block.instrs block.instrs[0].f_entry = True block.type |= bbl.BasicBlock.ENTRY break else: print "BUG: could not find function entry in instrs!!" #print "blocks:", blocks return code, blocks.values()
def _convert_address_to_function(func): """Convert an address that IDA has classified incorrectly into a proper function.""" # If everything goes wrong, we'll try to restore this function. orig = idc.FirstFuncFchunk(func) # If the address is not code, let's undefine whatever it is. if not idc.isCode(idc.GetFlags(func)): if not is_mapped(func): # Well, that's awkward. return False item = idc.ItemHead(func) itemend = idc.ItemEnd(func) if item != idc.BADADDR: _log(1, 'Undefining item {:#x} - {:#x}', item, itemend) idc.MakeUnkn(item, idc.DOUNK_EXPAND) idc.MakeCode(func) # Give IDA a chance to analyze the new code or else we won't be able to create a # function. idc.Wait() idc.AnalyseArea(item, itemend) else: # Just try removing the chunk from its current function. IDA can add it to another function # automatically, so make sure it's removed from all functions by doing it in loop until it # fails. for i in range(1024): if not idc.RemoveFchunk(func, func): break # Now try making a function. if idc.MakeFunction(func) != 0: return True # This is a stubborn chunk. Try recording the list of chunks, deleting the original function, # creating the new function, then re-creating the original function. if orig != idc.BADADDR: chunks = list(idautils.Chunks(orig)) if idc.DelFunction(orig) != 0: # Ok, now let's create the new function, and recreate the original. if idc.MakeFunction(func) != 0: if idc.MakeFunction(orig) != 0: # Ok, so we created the functions! Now, if any of the original chunks are not # contained in a function, we'll abort and undo. if all(idaapi.get_func(start) for start, end in chunks): return True # Try to undo the damage. for start, _ in chunks: idc.DelFunction(start) # Everything we've tried so far has failed. If there was originally a function, try to restore # it. if orig != idc.BADADDR: _log(0, 'Trying to restore original function {:#x}', orig) idc.MakeFunction(orig) return False
def item_off(ea): '''ea_t -> (int, int)''' head = idc.ItemHead(ea) end = idc.ItemEnd(head) ti = get_or_guess_tinfo(head) try: size = array_elem_size(ti) except NotArrayError: size = ti.get_size() diff = ea - head idx = diff / size rem = diff % size return (idx, rem)