def dump_function_delta(self):
"""
Dump the computed function delta.
"""
lmsg("Functions added:")
lmsg(hex_list(self.functions_added))
lmsg("Functions removed:")
lmsg(hex_list(self.functions_removed))
lmsg("Functions modified:")
lmsg(hex_list(self.functions_modified))
#--------------------------------------------------------------------------
# Async Metadata Helpers
#--------------------------------------------------------------------------
python类Functions()的实例源码
def find_dispatch_by_struct_index():
"""Attempts to locate the dispatch function based off it being loaded in a structure
at offset 70h, based off of https://github.com/kbandla/ImmunityDebugger/blob/master/1.73/Libs/driverlib.py """
out = set()
for function_ea in idautils.Functions():
flags = GetFunctionFlags(function_ea)
# skip library functions
if flags & FUNC_LIB:
continue
func = idaapi.get_func(function_ea)
addr = func.startEA
while addr < func.endEA:
if GetMnem(addr) == 'mov':
if '+70h' in GetOpnd(addr, 0) and idc.GetOpType(addr, 1) == 5:
out.add(GetOpnd(addr, 1))
addr = idc.NextHead(addr)
return out
def get_w32syscalls():
syscalls = set()
# def get_syscall_start():
# for m, n in idautils.Names():
# if n == '_W32pServiceTable':
# return m
# ea = get_syscall_start()
ea = idaapi.str2ea('_W32pServiceTable')
f = idaapi.get_full_long(ea)
functions = set(idautils.Functions())
while f in functions:
fname = GetFunctionName(f)
syscalls.add(fname)
ea += 4
f = idaapi.get_full_long(ea)
print 'win32k system call' , len(syscalls)
return syscalls
def _rename_functions(self):
'''Rename functions.'''
print "IPL: Started to rename functions..."
failed = 0
total = 0
for function in idautils.Functions():
total += 1
pdb_mangled_name = self.PDBLookup.lookup(function, True)
if not pdb_mangled_name:
failed += 1
print "IPL: Failed to find symbol for function: 0x{:08x}".format(function)
continue
_, mangled_function_name = pdb_mangled_name.split('!')
# https://www.hex-rays.com/products/ida/support/idadoc/203.shtml
idc.MakeNameEx(function, mangled_function_name,
idc.SN_AUTO | idc.SN_NOCHECK)
print "IPL: Total {} functions, {} failed to rename.".format(total, failed)
def output_symbols(out):
"""Dump symbols."""
try:
from idaapi import get_func_name2 as get_func_name
# Since get_func_name is deprecated (at least from IDA 6.9)
except ImportError:
from idaapi import get_func_name
# Older versions of IDA don't have get_func_name2
# so we just use the older name get_func_name
def func_name_propagate_thunk(ea):
current_name = get_func_name(ea)
if current_name[0].isalpha():
return current_name
func = idaapi.get_func(ea)
temp_ptr = idaapi.ea_pointer()
ea_new = idaapi.BADADDR
if func.flags & idaapi.FUNC_THUNK == idaapi.FUNC_THUNK:
ea_new = idaapi.calc_thunk_func_target(func, temp_ptr.cast())
if ea_new != idaapi.BADADDR:
ea = ea_new
propagated_name = get_func_name(ea) or '' # Ensure it is not `None`
if len(current_name) > len(propagated_name) > 0:
return propagated_name
else:
return current_name
# Fallback to non-propagated name for weird times that IDA gives
# a 0 length name, or finds a longer import name
for ea in idautils.Segments():
fs = idautils.Functions(idc.SegStart(ea), idc.SegEnd(ea))
for f in fs:
out.write('("%s" 0x%x 0x%x)\n' % (
func_name_propagate_thunk(f),
idc.GetFunctionAttr(f, idc.FUNCATTR_START),
idc.GetFunctionAttr(f, idc.FUNCATTR_END)))
def prototypes():
types = set()
for ea in idautils.Functions():
proto = idaapi.print_type(ea, True)
if proto:
types.append(proto + ';')
return list(types)
def bulk_prefix():
"""
Prefix the Functions window selection with a user defined string.
"""
# NOTE / COMPAT:
# prompt the user for a prefix to apply to the selected functions
if using_ida7api:
tag = idaapi.ask_str(PREFIX_DEFAULT, 0, "Function Tag")
else:
tag = idaapi.askstr(0, PREFIX_DEFAULT, "Function Tag")
# the user closed the window... ignore
if tag == None:
return
# the user put a blank string and hit 'okay'... notify & ignore
elif tag == '':
idaapi.warning("[ERROR] Tag cannot be empty [ERROR]")
return
#
# loop through all the functions selected in the 'Functions window' and
# apply the user defined prefix tag to each one.
#
for func_name in get_selected_funcs():
# ignore functions that already have the specified prefix applied
if func_name.startswith(tag):
continue
# apply the user defined prefix to the function (rename it)
new_name = '%s%s%s' % (str(tag), PREFIX_SEPARATOR, func_name)
func_addr = idaapi.get_name_ea(idaapi.BADADDR, func_name)
idaapi.set_name(func_addr, new_name, idaapi.SN_NOWARN)
# refresh the IDA views
refresh_views()
def clear_prefix():
"""
Clear user defined prefixes from the selected functions in the Functions window.
"""
#
# loop through all the functions selected in the 'Functions window' and
# clear any user defined prefixes applied to them.
#
for func_name in get_selected_funcs():
#
# locate the last (rfind) prefix separator in the function name as
# we will want to keep everything that comes after it
#
i = func_name.rfind(PREFIX_SEPARATOR)
# if there is no prefix (separator), there is nothing to trim
if i == -1:
continue
# trim the prefix off the original function name and discard it
new_name = func_name[i+1:]
func_addr = idaapi.get_name_ea(idaapi.BADADDR, func_name)
idaapi.set_name(func_addr, new_name, idaapi.SN_NOWARN)
# refresh the IDA views
refresh_views()
#------------------------------------------------------------------------------
# IDA Util
#------------------------------------------------------------------------------
def get_all_funcs():
"""
Enumerate all function names defined in the IDB.
"""
return set(idaapi.get_func_name(ea) for ea in idautils.Functions())
def match_funcs(qt_funcs):
"""
Convert function names scraped from Qt to their *actual* representation.
The function names we scrape from the Functions window Qt table actually
use the underscore character ('_') as a substitute for a variety of
different characters.
For example, a function named foo%bar in the IDB will appears as foo_bar
in the Functions window table.
This function takes a list of names as they appear in the Functions window
table such as the following:
['foo_bar']
And applies a best effort lookup to return a list of the 'true' function
names as they are stored in the IDB.
['foo%bar']
TODO: rewrite this to be more efficient for larger idbs
TODO: takes first matching function, may want to change it to make the requirements more strict
"""
res = set()
ida_funcs = get_all_funcs()
for f in qt_funcs:
for f2 in ida_funcs:
if len(f) == len(f2):
i = 0
while i < len(f) and (f[i] == f2[i] or f[i] == '_'):
i += 1
if i == len(f):
res.add(f2)
break
return list(res)
def get_user_functions():
user_functions = []
for fva in idautils.Functions():
f_attr = idc.GetFunctionAttr(fva, idc.FUNCATTR_FLAGS)
if not f_attr & idc.FUNC_LIB and not f_attr & idc.FUNC_THUNK:
user_functions.append(fva)
return user_functions
def get_functions():
'''
enumerate the functions in the currently loaded module.
Yields:
int: address of the function.
'''
startea = idc.BeginEA()
for fva in idautils.Functions(idc.SegStart(startea), idc.SegEnd(startea)):
yield fva
def functions_iter():
functions = set()
exports = get_export_list()
for func_ea in idautils.Functions():
if func_ea in functions:
continue # functions with chunks appear once for each of them..
functions.add(func_ea)
code, blocks = get_code_and_blocks(func_ea)
crefs_to = get_func_code_refs_to(func_ea)
crefs_from = get_func_code_refs_from(func_ea, code.iterkeys())
f = func.Function(func_ea, code, blocks, crefs_to, crefs_from)
f.ftype = idc.GetType(func_ea)
f.name = idc.GetFunctionName(func_ea)
if func_ea in exports:
f.exported = True
yield f
typed_imports = get_typed_imports()
for imp_ea, ftype in typed_imports:
crefs_to = get_func_code_refs_to(imp_ea)
f = func.Function(imp_ea, None, None, crefs_to, None)
f.ftype = ftype
f.level = -1 # special level for imported functions
yield f
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 sample_source():
global full_hash
full_hash = ""
c = 0
for addr in idautils.Functions(idc.MinEA(),idc.MaxEA()):
fname = idc.GetFunctionName(addr)
full_hash += normalize_fname(fname)+":"+calc_hash(addr)+":"+shexst(addr)+"|"
c = c+1
if c > 1000:
print "Too many subs. Plz run:"
print "SRC SAMPLE : open('lame_ipc.txt','wb').write(full_hash)"
print "DST SAMPLE : src_data = open('lame_ipc.txt','rb').read(full_hash)"
else:
print 'src_data = "' + full_hash + '"'
return
def sample_dest():
global src_data
if src_data is None:
print "run the src_data = ... first"
return
src_hashes = {}
for i in src_data.split("|"):
z = i.split(":")
if len(z) < 2:
continue
if src_hashes.has_key(z[1]):
src_hashes[z[1]] = "baadf00d"
else:
src_hashes[z[1]] = z[0]
dst_hashes = {}
for addr in idautils.Functions(idc.MinEA(),idc.MaxEA()):
fname = idc.GetFunctionName(addr)
z = calc_hash(addr)
if dst_hashes.has_key(z):
dst_hashes[z] = "baadf00d"
else:
dst_hashes[z] = addr
c = 0
for tmp in dst_hashes:
if dst_hashes[tmp] == "baadf00d":
continue
if src_hashes.has_key(tmp):
if src_hashes[tmp] != "baadf00d":
idc.MakeNameEx(dst_hashes[tmp],"SHARED_"+src_hashes[tmp], SN_NOWARN)
c = c+1
print "%d subs have been renamed" % (c)
return
def get_functions():
return (IdaLocation(f) for f in idautils.Functions())
def get_list_of_functions(self):
'''Get all functions list.'''
seg_ea = idc.BeginEA()
functions_list = {}
for func_ea in idautils.Functions(idc.SegStart(seg_ea), idc.SegEnd(seg_ea)):
function_name = self.maybe_demangle(idc.GetFunctionName(func_ea))
functions_list[function_name] = func_ea
return functions_list
def __init__(self):
super(FunctionsPlus, self).__init__()
if idc.GetLongPrm(idc.INF_PROCNAME).lower() != 'metapc':
print "Functions+ warning: not tested in this configuration"
self.cols = Cols()
def Show(self):
'''Creates the form is not created or focuses it if it was.'''
return PluginForm.Show(self, "Functions+",
options=PluginForm.FORM_PERSIST)
def update_mapping(self):
pass
self.fun_mapping = {idc.GetFunctionName(x): (idaapi.get_func(x).startEA, idaapi.get_func(x).endEA-1) for x in
idautils.Functions()}
self.seg_mapping = {idc.SegName(x): (idc.SegStart(x), idc.SegEnd(x)) for x in idautils.Segments()}
def process_program(self):
funs = list(idautils.Functions())
nb = len(funs)
for i, fun in zip(xrange(nb), funs):
self.process_routine(fun, rtn_i=i+1, total_rtn=nb)
if self.STOP:
return
def set_start_stop(self, ftype):
assert_ida_available()
import idc
import idaapi
import idautils
fun_mapping = {idc.GetFunctionName(x): (idaapi.get_func(x).startEA, idaapi.get_func(x).endEA-1)
for x in idautils.Functions()}
start = idc.BeginEA()
stop = 0
if ftype == PE:
start, stop = fun_mapping["start"]
else:
if not idc.isCode(idc.GetFlags(start)):
if idc.MakeCode(start) == 0:
print "Fail to decode instr !"
idaapi.autoWait()
if idc.GetFunctionName(start) == "":
if idc.MakeFunction(start) == 0:
print "Fail to create function !"
idaapi.autoWait()
fun_mapping = {idc.GetFunctionName(x): (idaapi.get_func(x).startEA, idaapi.get_func(x).endEA-1)
for x in idautils.Functions()}
if "main" in fun_mapping:
start, stop = fun_mapping["main"]
elif "start" in fun_mapping:
if "__libc_start_main" in fun_mapping:
instrs = list(idautils.FuncItems(fun_mapping["start"][0]))
instrs.reverse()
for inst in instrs:
arg1 = idc.GetOperandValue(inst, 0)
if idc.GetMnem(inst) == "push":
start, stop = arg1, fun_mapping["start"][1]
break
else:
start, stop = fun_mapping["start"]
self.config.start, self.config.stop = start, stop
def find_dispatch_by_cfg():
"""
Finds the functions in the binary which are not directly called anywhere and counts how many other functions they call,
returing all functions which call > 0 other functions but are not called themselves. As a dispatch function is not normally directly
called but will normally many other functions this is a fairly good way to guess which function it is.
"""
out = []
called = set()
caller = dict()
# Loop through all the functions in the binary
for function_ea in idautils.Functions():
flags = GetFunctionFlags(function_ea)
# skip library functions
if flags & FUNC_LIB:
continue
f_name = GetFunctionName(function_ea)
# For each of the incoming references
for ref_ea in CodeRefsTo(function_ea, 0):
called.add(f_name)
# Get the name of the referring function
caller_name = GetFunctionName(ref_ea)
if caller_name not in caller.keys():
caller[caller_name] = 1
else:
caller[caller_name] += 1
while True:
if len(caller.keys()) == 0:
break
potential = max(caller, key=caller.get)
if potential not in called:
out.append(potential)
del caller[potential]
return out
def get_ntsyscalls():
syscalls = set()
ea = idaapi.str2ea('_KiServiceTable')
f = idaapi.get_full_long(ea)
functions = set(idautils.Functions())
while f in functions:
fname = GetFunctionName(f)
syscalls.add(fname)
ea += 4
f = idaapi.get_full_long(ea)
print 'ntos system call' , len(syscalls)
return syscalls
def get_all_funcs():
return set(idaapi.get_func_name(ea) for ea in idautils.Functions())
def get_selected_funcs():
tform = idaapi.find_tform("Functions window")
if not tform:
idc.Warning("Unable to find 'Functions window'")
return
widget = idaapi.PluginForm.FormToPySideWidget(tform)
table = widget.findChild(QtWidgets.QTableView)
selected_funcs = [str(s.data()) for s in table.selectionModel().selectedRows()]
return match_funcs(selected_funcs)
def get_selected_funcs():
"""
Return the list of function names selected in the Functions window.
"""
# NOTE / COMPAT:
if using_ida7api:
import sip
twidget = idaapi.find_widget("Functions window")
widget = sip.wrapinstance(long(twidget), QtWidgets.QWidget) # NOTE: LOL
else:
tform = idaapi.find_tform("Functions window")
if using_pyqt5:
widget = idaapi.PluginForm.FormToPyQtWidget(tform)
else:
widget = idaapi.PluginForm.FormToPySideWidget(tform)
# TODO: test this
if not widget:
idaapi.warning("Unable to find 'Functions window'")
return
#
# locate the table widget within the Functions window that actually holds
# all the visible function metadata
#
table = widget.findChild(QtWidgets.QTableView)
#
# scrape the selected function names from the Functions window table
#
selected_funcs = [str(s.data()) for s in table.selectionModel().selectedRows()]
#
# re-map the scraped names as they appear in the function table, to their true
# names as they are saved in the IDB. See the match_funcs(...) function
# comment for more details
#
return match_funcs(selected_funcs)
def metadata_progress(completed, total):
"""
Handler for metadata collection callback, updates progress dialog.
"""
idaapi.replace_wait_box("Collected metadata for %u/%u Functions" % (completed, total))
#--------------------------------------------------------------------------
# Event Hooks
#--------------------------------------------------------------------------