def enum_function_addrs(fva):
'''
yield the effective addresses of each instruction in the given function.
these addresses are not guaranteed to be in any order.
Args:
fva (int): the starting address of a function
Returns:
sequence[int]: the addresses of each instruction
'''
f = idaapi.get_func(fva)
if not f:
raise ValueError('not a function')
for block in idaapi.FlowChart(f):
ea = block.startEA
while ea <= block.endEA:
yield ea
ea = idc.NextHead(ea)
python类FlowChart()的实例源码
def find_all_ioctls():
"""
From the currently selected address attempts to traverse all blocks inside the current function to find all immediate values which
are used for a comparison/sub immediately before a jz. Returns a list of address, second operand pairs.
"""
ioctls = []
# Find the currently selected function and get a list of all of it's basic blocks
addr = idc.ScreenEA()
f = idaapi.get_func(addr)
fc = idaapi.FlowChart(f, flags=idaapi.FC_PREDS)
for block in fc:
# grab the last two instructions in the block
last_inst = idc.PrevHead(block.endEA)
penultimate_inst = idc.PrevHead(last_inst)
# If the penultimate instruction is cmp or sub against an immediate value immediatly preceding a 'jz'
# then it's a decent guess that it's an IOCTL code (if this is a disptach function)
if idc.GetMnem(penultimate_inst) in ['cmp', 'sub'] and idc.GetOpType(penultimate_inst, 1) == 5:
if idc.GetMnem(last_inst) == 'jz':
ioctl_tracker.add_ioctl(penultimate_inst)
for inst in ioctl_tracker.ioctl_locs:
value = get_operand_value(inst)
ioctls.append((inst, value))
return ioctls
def get_cfg(function_start, function_end):
f_name = GetFunctionName(function_start)
cfg = idaapi.FlowChart(idaapi.get_func(function_start))
return list(cfg)
def find_tight_loops(fva):
""" Code from Willi Ballenthin """
tight_loops = []
function = idaapi.get_func(fva)
for bb in idaapi.FlowChart(function):
# bb.endEA is the first addr not in the basic block
bb_end = idc.PrevHead(bb.endEA)
for x in idautils.XrefsFrom(bb_end):
if x.to == bb.startEA and bb.startEA < bb_end:
tight_loops.append((bb.startEA, bb_end))
if tight_loops:
g_logger.debug("Tight loops in 0x%x: %s", fva, ["0x%x - 0x%x" % (s, e) for (s, e) in tight_loops])
return tight_loops
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 get_basic_blocks(fva):
'''
return sequence of `BasicBlock` instances for given function.
'''
ret = []
func = idaapi.get_func(fva)
if func is None:
return ret
for bb in idaapi.FlowChart(func):
ret.append(BasicBlock(va=bb.startEA,
size=bb.endEA - bb.startEA))
return ret
def color_block(ea=None, color=0x55ff7f):
"""http://reverseengineering.stackexchange.com/questions/10662/change-block-node-color-with-idapython
and WanderingGlitch for the tip of refresh_idaview_anyway()"""
func_top = fn.top()
f = idaapi.get_func(ea)
g = idaapi.FlowChart(f, flags=idaapi.FC_PREDS)
bb_id = get_bb_id(g, ea)
p = idaapi.node_info_t()
p.bg_color = color
idaapi.set_node_info2(func_top, bb_id, p, idaapi.NIF_BG_COLOR | idaapi.NIF_FRAME_COLOR)
idaapi.refresh_idaview_anyway()
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def iterate(cls, func):
'''Returns each idaapi.BasicBlock for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
for bb in fc:
yield bb
return
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
# FIXME: document this
#def refs(func, member):
# xl, fn = idaapi.xreflist_t(), by(func)
# idaapi.build_stkvar_xrefs(xl, fn, member.ptr)
# x.ea, x.opnum, x.type
# ref_types = {
# 0 : 'Data_Unknown',
# 1 : 'Data_Offset',
# 2 : 'Data_Write',
# 3 : 'Data_Read',
# 4 : 'Data_Text',
# 5 : 'Data_Informational',
# 16 : 'Code_Far_Call',
# 17 : 'Code_Near_Call',
# 18 : 'Code_Far_Jump',
# 19 : 'Code_Near_Jump',
# 20 : 'Code_User',
# 21 : 'Ordinary_Flow'
# }
# return [(x.ea,x.opnum) for x in xl]
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def iterate(cls, func):
'''Returns each idaapi.BasicBlock for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
for bb in fc:
yield bb
return
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
# FIXME: document this
#def refs(func, member):
# xl, fn = idaapi.xreflist_t(), by(func)
# idaapi.build_stkvar_xrefs(xl, fn, member.ptr)
# x.ea, x.opnum, x.type
# ref_types = {
# 0 : 'Data_Unknown',
# 1 : 'Data_Offset',
# 2 : 'Data_Write',
# 3 : 'Data_Read',
# 4 : 'Data_Text',
# 5 : 'Data_Informational',
# 16 : 'Code_Far_Call',
# 17 : 'Code_Near_Call',
# 18 : 'Code_Far_Jump',
# 19 : 'Code_Near_Jump',
# 20 : 'Code_User',
# 21 : 'Ordinary_Flow'
# }
# return [(x.ea,x.opnum) for x in xl]
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def iterate(cls, func):
'''Returns each idaapi.BasicBlock for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
for bb in fc:
yield bb
return
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
# FIXME: document this
#def refs(func, member):
# xl, fn = idaapi.xreflist_t(), by(func)
# idaapi.build_stkvar_xrefs(xl, fn, member.ptr)
# x.ea, x.opnum, x.type
# ref_types = {
# 0 : 'Data_Unknown',
# 1 : 'Data_Offset',
# 2 : 'Data_Write',
# 3 : 'Data_Read',
# 4 : 'Data_Text',
# 5 : 'Data_Informational',
# 16 : 'Code_Far_Call',
# 17 : 'Code_Near_Call',
# 18 : 'Code_Far_Jump',
# 19 : 'Code_Near_Jump',
# 20 : 'Code_User',
# 21 : 'Ordinary_Flow'
# }
# return [(x.ea,x.opnum) for x in xl]
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
def bottom(func):
'''Return the exit-points of the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
exit_types = (fc_block_type_t.fcb_ret,fc_block_type_t.fcb_cndret,fc_block_type_t.fcb_noret,fc_block_type_t.fcb_enoret,fc_block_type_t.fcb_error)
return tuple(database.address.prev(n.endEA) for n in fc if n.type in exit_types)
def iterate(cls, func):
'''Returns each idaapi.BasicBlock for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
for bb in fc:
yield bb
return
def flow(func):
'''Return a flow chart object for the function ``func``.'''
fn = by(func)
fc = idaapi.FlowChart(f=fn, flags=idaapi.FC_PREDS)
return fc
# FIXME: document this
#def refs(func, member):
# xl, fn = idaapi.xreflist_t(), by(func)
# idaapi.build_stkvar_xrefs(xl, fn, member.ptr)
# x.ea, x.opnum, x.type
# ref_types = {
# 0 : 'Data_Unknown',
# 1 : 'Data_Offset',
# 2 : 'Data_Write',
# 3 : 'Data_Read',
# 4 : 'Data_Text',
# 5 : 'Data_Informational',
# 16 : 'Code_Far_Call',
# 17 : 'Code_Near_Call',
# 18 : 'Code_Far_Jump',
# 19 : 'Code_Near_Jump',
# 20 : 'Code_User',
# 21 : 'Ordinary_Flow'
# }
# return [(x.ea,x.opnum) for x in xl]
def find_bbls(function_ea):
'''
yields all basic blocks that belong to the
given function. The blocks are returned in
a 2-tuple like:
(start_address, end_address)
Both start and end address are RELATIVE offsets
from the image base.
'''
# get image base from IDA
image_base = idaapi.get_imagebase()
function_ea += image_base
# get flow chart from IDA
flow_chart = idaapi.FlowChart(
idaapi.get_func(function_ea),
flags=idaapi.FC_PREDS
)
# iterate through all basic blocks in
# the current routine
for block in flow_chart:
start_addr = block.startEA - image_base
end_addr = block.endEA - image_base
if start_addr != end_addr:
yield start_addr, end_addr
def __init__(self, fun_addr):
super(MyFlowGraph, self).__init__()
self.fun = idaapi.get_func(fun_addr)
self.startEA = self.fun.startEA
self.endEA = self.fun.endEA
for bb in idaapi.FlowChart(self.fun):
self.__setitem__(bb.id, MyBasicBlock(bb))
self._compute_links()
self.edge_map = self.make_graph()
self.shortest_path_map = self.dijkstra(self.edge_map)
self.size = sum([x.size() for x in self.values()])
self.viewer = MyFlowGraphViewer(self, "Extract(%s)" % idc.GetFunctionName(self.startEA))
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()
#XXX: good test function in 0x070016E7 (BIB.dll)