By way of offering a compare and contrast between IDC and IDAPython, the following sections present the same example cases seen previously in the discussion of IDC. Wherever possible we endeavor to make maximum use of Python-specific features to demonstrate some of the efficiencies that can be gained by scripting in Python.
One of the strengths of IDAPython is the way that it uses Python’s powerful datatypes to simplify access to collections of database objects. In Example 15-8, we reimplement the function enumeration script of Example 15-1 in Python. Recall that the purpose of this script is to iterate over every function in a database and print basic information about each function, including the start and end addresses of the function, the size of the function’s arguments, and the size of the function’s local variable space. All output is sent to the output window.
Example 15-8. Function enumeration using Python
funcs = Functions() for f in funcs: name = Name(f) end = GetFunctionAttr(f, FUNCATTR_END) locals = GetFunctionAttr(f, FUNCATTR_FRSIZE) frame = GetFrame(f) # retrieve a handle to the function's stack frame if frame is None: continue ret = GetMemberOffset(frame, " r") # " r" is the name of the return address if ret == −1: continue firstArg = ret + 4 args = GetStrucSize(frame) - firstArg Message("Function: %s, starts at %x, ends at %x " % (name, f, end)) Message(" Local variable area is %d bytes " % locals) Message(" Arguments occupy %d bytes (%d args) " % (args, args / 4))
For this particular script, the use of Python gains us little in the way of efficiency other than the use of the Functions
list generator, which facilitates the for
loop at .
Example 15-9 demonstrates how the instruction-counting script of Example 15-2 might be written in Python, taking advantage of the list generators available in the idautils
module.
Example 15-9. Instruction enumeration in Python
from idaapi import * func = get_func(here()) # here() is synonymous with ScreenEA() if not func is None: fname = Name(func.startEA) count = 0 for i in FuncItems(func.startEA): count = count + 1 Warning("%s contains %d instructions " % (fname,count)) else: Warning("No function found at location %x" % here())
Differences from the IDC version include the use of an SDK function (accessed via idaapi
) to retrieve a reference to a function object (specifically a func_t
) and the use of the FuncItems
generator (from idautils
) to provide easy iteration over all of the instructions within the function. Because we can’t use Python’s len
function on a generator, we are still obligated to step through the generator list in order to count each instruction one at a time.
The idautils
module contains several generator functions that build cross-reference lists in a somewhat more intuitive way than we saw in IDC. Example 15-10 rewrites the function call enumeration script that we saw previously in Example 15-3.
Example 15-10. Enumerating function calls using Python
from idaapi import * func = get_func(here()) if not func is None: fname = Name(func.startEA) items = FuncItems(func.startEA) for i in items: for xref in XrefsFrom(i, 0): if xref.type == fl_CN or xref.type == fl_CF: Message("%s calls %s from 0x%x " % (fname, Name(xref.to), i)) else: Warning("No function found at location %x" % here())
New in this script is the use of the XrefsFrom
generator (from idautils
) to step through all cross-references from the current instruction. XrefsFrom
returns a reference to an xrefblk_t
object that contains detailed information about the current cross-reference.
Example 15-11 is the Python version of the .idt generator script from Example 15-5.
Example 15-11. A Python script to generate IDT files
file = AskFile(1, "*.idt", "Select IDT save file") with open(file, 'w') as fd: fd.write("ALIGNMENT 4 ") fd.write("0 Name=%s " % GetInputFile()) for i in range(GetEntryPointQty()): ord = GetEntryOrdinal(i) if ord == 0: continue addr = GetEntryPoint(ord) if ord == addr: continue #entry point has no ordinal fd.write("%d Name=%s" % (ord, Name(addr))) purged = GetFunctionAttr(addr, FUNCATTR_ARGSIZE) if purged > 0: fd.write(" Pascal=%d" % purged) fd.write(" ")
The two scripts look remarkably similar because IDAPython has no generator function for entry-point lists, so we are left to use the same set of functions that were used in Example 15-5. One difference worth noting is that IDAPython deprecates IDC’s file-handling functions in favor of Python’s built-in file-handling functions.
13.59.197.213