diff --git a/build.py b/build.py index 148bf37..3631d6c 100644 --- a/build.py +++ b/build.py @@ -2,7 +2,7 @@ #------------------------------------------------------------ # IDAPython - Python plugin for Interactive Disassembler Pro # -# Copyright (c) 2004-2009 Gergely Erdelyi +# Copyright (c) 2004-2009 Gergely Erdelyi # # All rights reserved. # @@ -70,6 +70,8 @@ BINDIST_MANIFEST = [ "examples/ex1_idautils.py", "examples/hotkey.py", "examples/structure.py", + "examples/ex_gdl_qflow_chart.py", + "examples/ex_strings.py", ] # List files for the source distribution (appended to binary list) @@ -220,10 +222,10 @@ class MSVCBuilder(BuilderBase): self.linker = "link" self.source_extension = ".cpp" self.object_extension = ".obj" - + def compiler_in_string(self, filename): return "/c %s" % filename - + def compiler_out_string(self, filename): return "/Fo%s" % filename @@ -244,7 +246,7 @@ def build_distribution(manifest, distrootdir, ea64, nukeold): # Also make a ZIP archive of the build zippath = distrootdir + ".zip" zip = zipfile.ZipFile(zippath, nukeold and "w" or "a", zipfile.ZIP_DEFLATED) - + # Copy files, one by one for f in manifest: if type(f) == types.TupleType: @@ -259,11 +261,11 @@ def build_distribution(manifest, distrootdir, ea64, nukeold): if srcdir == "": dstdir = distrootdir else: - dstdir = distrootdir + os.sep + srcdir + dstdir = distrootdir + os.sep + srcdir if not os.path.exists(dstdir): os.makedirs(dstdir) - + dstfilepath = dstdir + os.sep + srcfilename shutil.copyfile(srcfilepath, dstfilepath) zip.write(dstfilepath) @@ -348,7 +350,7 @@ def build_binary_package(ea64, nukeold): system = "Windows" platform_string = "win32" plugin_name = ea64 and "python.p64" or "python.plw" - + if system == "Linux": platform_string = "linux" plugin_name = ea64 and "python.plx64" or "python.plx" @@ -357,10 +359,10 @@ def build_binary_package(ea64, nukeold): platform_string = "macosx" plugin_name = ea64 and "python.pmc64" or "python.pmc" - BINDISTDIR = "idapython-%d.%d.%d_ida%d.%d_py%d.%d_%s" % (VERSION_MAJOR, - VERSION_MINOR, - VERSION_PATCH, - IDA_MAJOR_VERSION, + BINDISTDIR = "idapython-%d.%d.%d_ida%d.%d_py%d.%d_%s" % (VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH, + IDA_MAJOR_VERSION, IDA_MINOR_VERSION, PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION, @@ -378,9 +380,9 @@ def build_binary_package(ea64, nukeold): def build_source_package(): """ Build a directory and a ZIP file with all the sources """ - SRCDISTDIR = "idapython-%d.%d.%d" % (VERSION_MAJOR, - VERSION_MINOR, - VERSION_PATCH) + SRCDISTDIR = "idapython-%d.%d.%d" % (VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH) # Build the source distribution srcmanifest = [] srcmanifest.extend(BINDIST_MANIFEST) @@ -391,7 +393,7 @@ def build_source_package(): if __name__ == "__main__": # Do 64-bit build? - ea64 = '--ea64' in sys.argv + ea64 = '--ea64' in sys.argv build_binary_package(ea64=False, nukeold=True) if ea64: build_binary_package(ea64=True, nukeold=False) diff --git a/examples/ex_gdl_qflow_chart.py b/examples/ex_gdl_qflow_chart.py new file mode 100644 index 0000000..449c714 --- /dev/null +++ b/examples/ex_gdl_qflow_chart.py @@ -0,0 +1,35 @@ +import idaapi + +# ----------------------------------------------------------------------- +# Using raw IDAAPI +def raw_main(p=True): + global q + f = idaapi.get_func(here()) + if not f: + return + q = idaapi.qflow_chart_t("The title", f, 0, 0, idaapi.FC_PREDS) + for n in xrange(0, q.size()): + b = q[n] + if p: print "%x - %x [%d]:" % (b.startEA, b.endEA, n) + for ns in xrange(0, q.nsucc(n)): + if p: print " %d->%d" % (n, q.succ(n, ns)) + for ns in xrange(0, q.npred(n)): + if p: print " %d->%d" % (n, q.pred(n, ns)) + +# ----------------------------------------------------------------------- +# Using the class +def cls_main(p=True): + global f + f = idaapi.FlowChart(idaapi.get_func(here())) + for block in f: + if p: print "%x - %x [%d]:" % (block.startEA, block.endEA, block.id) + for succ_block in block.succs(): + if p: print " %x - %x [%d]:" % (succ_block.startEA, succ_block.endEA, succ_block.id) + for pred_block in block.preds(): + if p: print " %x - %x [%d]:" % (pred_block.startEA, pred_block.endEA, pred_block.id) + +q = None +f = None +raw_main(False) +cls_main(True) + diff --git a/examples/ex_strings.py b/examples/ex_strings.py new file mode 100644 index 0000000..db93b2e --- /dev/null +++ b/examples/ex_strings.py @@ -0,0 +1,6 @@ +import idautils + +s = Strings(False) +s.setup(strtypes=Strings.STR_UNICODE | Strings.STR_C) +for i in s: + print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i)) diff --git a/python/idautils.py b/python/idautils.py index 3c6e984..ca9aeb5 100644 --- a/python/idautils.py +++ b/python/idautils.py @@ -1,7 +1,7 @@ #------------------------------------------------------------ # IDAPython - Python plugin for Interactive Disassembler Pro # -# Copyright (c) 2004-2009 Gergely Erdelyi +# Copyright (c) 2004-2009 Gergely Erdelyi # # All rights reserved. # @@ -40,9 +40,9 @@ def CodeRefsTo(ea, flow): print ref """ if flow == 1: - return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to) + return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to) else: - return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to) + return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to) def CodeRefsFrom(ea, flow): @@ -61,9 +61,9 @@ def CodeRefsFrom(ea, flow): print ref """ if flow == 1: - return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from) + return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from) else: - return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from) + return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from) def DataRefsTo(ea): @@ -79,7 +79,7 @@ def DataRefsTo(ea): for ref in DataRefsTo(ScreenEA(), 1): print ref """ - return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to) + return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to) def DataRefsFrom(ea): @@ -95,7 +95,7 @@ def DataRefsFrom(ea): for ref in DataRefsFrom(ScreenEA(), 1): print ref """ - return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from) + return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from) def XrefTypeName(typecode): @@ -104,9 +104,9 @@ def XrefTypeName(typecode): @param typecode: cross-reference type code """ - ref_types = { + ref_types = { 0 : 'Data_Unknown', - 1 : 'Data_Offset', + 1 : 'Data_Offset', 2 : 'Data_Write', 3 : 'Data_Read', 4 : 'Data_Text', @@ -233,7 +233,7 @@ def Segments(): @return: List of segment start addresses. """ - for n in range(idaapi.get_segm_qty()): + for n in xrange(idaapi.get_segm_qty()): seg = idaapi.getnseg(n) if seg: yield seg.startEA @@ -298,7 +298,7 @@ def DecodeInstruction(ea): setattr(self, x, getattr(op, x)) r = dup(insn) r.Operands = [] - for n in range(0, idaapi.UA_MAXOP): + for n in xrange(0, idaapi.UA_MAXOP): t = idaapi.get_instruction_operand(insn, n) if t.type == idaapi.o_void: break @@ -359,4 +359,72 @@ class _cpu: #print "cpu.set(%s)"%name return idc.SetRegValue(value, name) +class Strings: + """ + Returns the string list. + + Example: + s = Strings() + + for i in s: + print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i)) + + """ + class StringItem: + """ + Class representing each string item. + The attributes are: + ea - string ea + type - string type (ASCSTR_xxxxx) + str() - returns the actual string + """ + def __init__(self, si): + self.ea = si.ea + self.type = si.type + self.length = si.length + def __str__(self): + return idc.GetString(self.ea, self.length, self.type) + + STR_C = 0x0001 # C-style ASCII string + STR_PASCAL = 0x0002 # Pascal-style ASCII string (length byte) + STR_LEN2 = 0x0004 # Pascal-style, length is 2 bytes + STR_UNICODE = 0x0008 # Unicode string + STR_LEN4 = 0x0010 # Pascal-style, length is 4 bytes + STR_ULEN2 = 0x0020 # Pascal-style Unicode, length is 2 bytes + STR_ULEN4 = 0x0040 # Pascal-style Unicode, length is 4 bytes + + def clear_cache(): + """Clears the strings list cache""" + self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache + + def __init__(self, default_setup=True): + if default_setup: + self.setup() + self._si = idaapi.string_info_t() + + def refresh(self, ea1=idaapi.cvar.inf.minEA, ea2=idaapi.cvar.inf.maxEA): + """Refreshes the strings list""" + idaapi.refresh_strlist(ea1, ea2) + self.size = idaapi.get_strlist_qty() + + def setup(self, strtypes=STR_C, minlen=5, only_7bit=True, ignore_instructions=False, ea1=idaapi.cvar.inf.minEA, ea2=idaapi.cvar.inf.maxEA, display_only_existing_strings=False): + t = idaapi.strwinsetup_t() + t.strtypes = strtypes + t.minlen = minlen + t.only_7bit = only_7bit + t.ea1 = ea1 + t.ea2 = ea2 + t.display_only_existing_strings = display_only_existing_strings + idaapi.set_strlist_options(t) + # automatically refreshes + self.refresh() + + def __getitem__(self, index): + """Returns string items""" + if index >= self.size: + raise StopIteration + if idaapi.get_strlist_item(index, self._si): + return Strings.StringItem(self._si) + return None + cpu = _cpu() diff --git a/python/idc.py b/python/idc.py index 070b8c8..117b256 100644 --- a/python/idc.py +++ b/python/idc.py @@ -3741,7 +3741,7 @@ def LoadFile(filepath, pos, ea, size): else: return 0 -def loadfile(filepath, pos, ea, size): LoadFile(filepath, pos, ea, size) +def loadfile(filepath, pos, ea, size): return LoadFile(filepath, pos, ea, size) def SaveFile(filepath, pos, ea, size): @@ -3764,7 +3764,7 @@ def SaveFile(filepath, pos, ea, size): else: return 0 -def savefile(filepath, pos, ea, size): SaveFile(filepath, pos, ea, size) +def savefile(filepath, pos, ea, size): return SaveFile(filepath, pos, ea, size) def fgetc(handle): @@ -7460,12 +7460,12 @@ def OpStroff(ea,n,strid): return OpStroffEx(ea,n,strid,0) def OpEnum(ea,n,enumid): return OpEnumEx(ea,n,enumid,0) def DelConst(constid, v, mask): return DelConstEx(constid, v, 0, mask) def GetConst(constid, v, mask): return GetConstEx(constid, v, 0, mask) -def AnalyseArea(sEA, eEA): AnalyzeArea(sEA,eEA) +def AnalyseArea(sEA, eEA): return AnalyzeArea(sEA,eEA) def MakeStruct(ea,name): return MakeStructEx(ea, -1, name) def Name(ea): return NameEx(BADADDR, ea) def GetTrueName(ea): return GetTrueNameEx(BADADDR, ea) -def MakeName(ea, name): MakeNameEx(ea,name,SN_CHECK) +def MakeName(ea, name): return MakeNameEx(ea,name,SN_CHECK) #def GetFrame(ea): return GetFunctionAttr(ea, FUNCATTR_FRAME) #def GetFrameLvarSize(ea): return GetFunctionAttr(ea, FUNCATTR_FRSIZE) diff --git a/swig/gdl.i b/swig/gdl.i index 766ae51..2c70283 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -18,8 +18,82 @@ %ignore node_set_t::intersect; %ignore node_set_t::node_set_t; %ignore node_set_t::sub; - +%ignore qflow_chart_t::blocks; %ignore flow_chart_t; %ignore setup_graph_subsystem; +%ignore qbasic_block_t::succ; +%ignore qbasic_block_t::pred; %include "gdl.hpp" + +%extend qflow_chart_t +{ + qbasic_block_t *__getitem__(int n) + { + return &(self->blocks[n]); + } +} + +%pythoncode %{ +# ----------------------------------------------------------------------- +class BasicBlock: + def __init__(self, id, bb, f): + self._f = f + self.id = id + """Basic block ID""" + self.startEA = bb.startEA + """startEA of basic block""" + self.endEA = bb.endEA + """endEA of basic block""" + self.type = self._f._q.calc_block_type(self.id) + """Block type (check fc_block_type_t enum)""" + + def preds(self): + """ + Iteratres the predecessors list + """ + q = self._f._q + for i in xrange(0, self._f._q.npred(self.id)): + yield self._f[q.pred(self.id, i)] + + def succs(self): + """ + Iteratres the successors list + """ + q = self._f._q + for i in xrange(0, q.nsucc(self.id)): + yield self._f[q.succ(self.id, i)] + +# ----------------------------------------------------------------------- +class FlowChart: + """ + Flowchart class used to determine basic blocks + """ + def __init__(self, f=None, bounds=None, flags=0): + """ + Constructor + @param f: A func_t type, use get_func(ea) to get a reference + @param bounds: A tuple of the form (start, end). Used if "f" is None + @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS + """ + if (not f) and (not bounds or type(bounds) != types.TupleType): + raise Exception("Please specifiy either a function or start/end pair") + if not bounds: + bounds = (BADADDR, BADADDR) + # create the flowchart + self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) + self.size = self._q.size() + def refresh(): + self._q.refresh() + self.size = self._q.size() + def __getitem__(self, index): + """ + Returns a basic block + @return: BasicBlock + """ + if index >= self.size: + raise StopIteration + return BasicBlock(index, self._q[index], self) + + +%}