From 7eb6d04c6ed26dfff43d32794b2708419ed1446d Mon Sep 17 00:00:00 2001 From: "elias.bachaalany@gmail.com" Date: Fri, 5 Jul 2013 23:12:04 +0000 Subject: [PATCH] Contributed by EiNSTeiN_: - Fixed compilation error on Linux - Minor bugfix in hexrays.i - Added two more samples: vds3.py and vds_xrefs.py --- examples/vds3.py | 162 +++++++++++++++++++++ examples/vds_xrefs.py | 319 ++++++++++++++++++++++++++++++++++++++++++ swig/hexrays.i | 10 +- swig/pro.i | 6 +- 4 files changed, 491 insertions(+), 6 deletions(-) create mode 100644 examples/vds3.py create mode 100644 examples/vds_xrefs.py diff --git a/examples/vds3.py b/examples/vds3.py new file mode 100644 index 0000000..545c09b --- /dev/null +++ b/examples/vds3.py @@ -0,0 +1,162 @@ +""" Invert the then and else blocks of a cif_t. + +Author: EiNSTeiN_ + +This is a rewrite in Python of the vds3 example that comes with hexrays sdk. + + +The main difference with the original C code is that when we create the inverted +condition object, the newly created cexpr_t instance is given to the hexrays and +must not be freed by swig. To achieve this, we have to change the 'thisown' flag +when appropriate. See http://www.swig.org/Doc1.3/Python.html#Python_nn35 + +""" + +import idautils +import idaapi +import idc + +import traceback + +NETNODE_NAME = '$ hexrays-inverted-if' + +class hexrays_callback_info(object): + + def __init__(self): + self.vu = None + + self.node = idaapi.netnode() + if not self.node.create(NETNODE_NAME): + # node exists + self.load() + else: + self.stored = [] + + return + + def load(self): + + self.stored = [] + + try: + data = self.node.getblob(0, 'I') + if data: + self.stored = eval(data) + print 'Invert-if: Loaded %s' % (repr(self.stored), ) + except: + print 'Failed to load invert-if locations' + traceback.print_exc() + return + + return + + def save(self): + + try: + self.node.setblob(repr(self.stored), 0, 'I') + except: + print 'Failed to save invert-if locations' + traceback.print_exc() + return + + return + + def invert_if(self, cfunc, insn): + + if insn.opname != 'if': + return False + + cif = insn.details + + if not cif.ithen or not cif.ielse: + return False + + idaapi.qswap(cif.ithen, cif.ielse) + cond = idaapi.cexpr_t(cif.expr) + notcond = idaapi.lnot(cond) + cond.thisown = 0 # the new wrapper 'notcond' now holds the reference to the cexpr_t + + cif.expr.swap(notcond) + + return True + + def add_location(self, ea): + if ea in self.stored: + self.stored.remove(ea) + else: + self.stored.append(ea) + self.save() + return + + def invert_if_event(self, vu): + + vu.get_current_item(idaapi.USE_KEYBOARD) + item = vu.item + + cfunc = vu.cfunc.__deref__() + + if item.citype != idaapi.VDI_EXPR: + return False + + if self.invert_if(cfunc, item.it.to_specific_type): + vu.refresh_ctext() + + self.add_location(item.it.ea) + + return True + + def restore(self, cfunc): + + #~ print 'restoring invert-if for %x' % (cfunc.entry_ea, ) + + str(cfunc) # generate treeitems. + + restored = False + + for item in cfunc.treeitems: + item = item.to_specific_type + if item.opname == 'if' and item.ea in self.stored: + if self.invert_if(cfunc, item): + restored = True + #~ print 'restore invert-if location %x' % (item.ea, ) + else: + print 'invert-if location %x: NOT RESTORED' % (item.ea, ) + + return restored + + def menu_callback(self): + try: + self.invert_if_event(self.vu) + except: + traceback.print_exc() + return 0 + + def event_callback(self, event, *args): + + try: + if event == idaapi.hxe_keyboard: + vu, keycode, shift = args + + if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("I") and shift == 0: + if self.invert_if_event(vu): + return 1 + + elif event == idaapi.hxe_right_click: + self.vu = args[0] + idaapi.add_custom_viewer_popup_item(self.vu.ct, "Invert then/else", "I", self.menu_callback) + + elif event == idaapi.hxe_maturity: + cfunc, maturity = args + + if maturity == idaapi.CMAT_FINAL: + self.restore(cfunc) + except: + traceback.print_exc() + + return 0 + +if idaapi.init_hexrays_plugin(): + i = hexrays_callback_info() + idaapi.install_hexrays_callback(i.event_callback) +else: + print 'invert-if: hexrays is not available.' diff --git a/examples/vds_xrefs.py b/examples/vds_xrefs.py new file mode 100644 index 0000000..cf06e9d --- /dev/null +++ b/examples/vds_xrefs.py @@ -0,0 +1,319 @@ +""" Xref plugin for Hexrays Decompiler + +Author: EiNSTeiN_ + +Show decompiler-style Xref when the X key is pressed in the Decompiler window. + +- It supports any global name: functions, strings, integers, etc. +- It supports structure member. + +""" + +import idautils +import idaapi +import idc + +import traceback + +try: + from PyQt4 import QtCore, QtGui + print 'Using PyQt' +except: + print 'PyQt not available' + + try: + from PySide import QtGui, QtCore + print 'Using PySide' + except: + print 'PySide not available' + +XREF_EA = 0 +XREF_STRUC_MEMBER = 1 + +class XrefsForm(idaapi.PluginForm): + + def __init__(self, target): + + idaapi.PluginForm.__init__(self) + + self.target = target + + if type(self.target) == idaapi.cfunc_t: + + self.__type = XREF_EA + self.__ea = self.target.entry_ea + self.__name = 'Xrefs of %x' % (self.__ea, ) + + elif type(self.target) == idaapi.cexpr_t and self.target.opname == 'obj': + + self.__type = XREF_EA + self.__ea = self.target.obj_ea + self.__name = 'Xrefs of %x' % (self.__ea, ) + + elif type(self.target) == idaapi.cexpr_t and self.target.opname in ('memptr', 'memref'): + + self.__type = XREF_STRUC_MEMBER + name = self.get_struc_name() + self.__name = 'Xrefs of %s' % (name, ) + + else: + raise ValueError('cannot show xrefs for this kind of target') + + return + + def get_struc_name(self): + + x = self.target.operands['x'] + m = self.target.operands['m'] + + xtype = typestring(x.type.u_str()) + xtype.remove_ptr_or_array() + typename = str(xtype) + + sid = idc.GetStrucIdByName(typename) + member = idc.GetMemberName(sid, m) + + return '%s::%s' % (typename, member) + + def OnCreate(self, form): + + # Get parent widget + try: + self.parent = self.FormToPySideWidget(form) + except: + self.parent = self.FormToPyQtWidget(form) + + self.populate_form() + + return + + def Show(self): + idaapi.PluginForm.Show(self, self.__name) + return + + def populate_form(self): + # Create layout + layout = QtGui.QVBoxLayout() + + layout.addWidget(QtGui.QLabel(self.__name)) + self.table = QtGui.QTableWidget() + layout.addWidget(self.table) + + self.table.setColumnCount(3) + self.table.setHorizontalHeaderItem(0, QtGui.QTableWidgetItem("Address")) + self.table.setHorizontalHeaderItem(1, QtGui.QTableWidgetItem("Function")) + self.table.setHorizontalHeaderItem(2, QtGui.QTableWidgetItem("Line")) + + self.table.setColumnWidth(0, 80) + self.table.setColumnWidth(1, 150) + self.table.setColumnWidth(2, 450) + + self.table.cellDoubleClicked.connect(self.double_clicked) + + #~ self.table.setSelectionMode(QtGui.QAbstractItemView.NoSelection) + self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows ) + self.parent.setLayout(layout) + + self.populate_table() + + return + + def double_clicked(self, row, column): + + ea = self.functions[row] + idaapi.open_pseudocode(ea, True) + + return + + def get_decompiled_line(self, cfunc, ea): + + print repr(ea) + if ea not in cfunc.eamap: + print 'strange, %x is not in %x eamap' % (ea, cfunc.entry_ea) + return + + insnvec = cfunc.eamap[ea] + + lines = [] + for stmt in insnvec: + + qs = idaapi.qstring() + qp = idaapi.qstring_printer_t(cfunc.__deref__(), qs, False) + + stmt._print(0, qp) + s = str(qs).split('\n')[0] + + #~ s = idaapi.tag_remove(s) + lines.append(s) + + return '\n'.join(lines) + + def get_items_for_ea(self, ea): + + frm = [x.frm for x in idautils.XrefsTo(self.__ea)] + + items = [] + for ea in frm: + try: + cfunc = idaapi.decompile(ea) + cfunc.refcnt += 1 + + self.functions.append(cfunc.entry_ea) + self.items.append((ea, idc.GetFunctionName(cfunc.entry_ea), self.get_decompiled_line(cfunc, ea))) + + except Exception as e: + print 'could not decompile: %s' % (str(e), ) + raise + + return + + def get_items_for_type(self): + + x = self.target.operands['x'] + m = self.target.operands['m'] + + xtype = typestring(x.type.u_str()) + xtype.remove_ptr_or_array() + typename = str(xtype) + + addresses = [] + for ea in idautils.Functions(): + + try: + cfunc = idaapi.decompile(ea) + cfunc.refcnt += 1 + except: + print 'Decompilation of %x failed' % (ea, ) + continue + + str(cfunc) + + for citem in cfunc.treeitems: + citem = citem.to_specific_type + if not (type(citem) == idaapi.cexpr_t and citem.opname in ('memptr', 'memref')): + continue + + _x = citem.operands['x'] + _m = citem.operands['m'] + _xtype = typestring(_x.type.u_str()) + _xtype.remove_ptr_or_array() + _typename = str(_xtype) + + #~ print 'in', hex(cfunc.entry_ea), _typename, _m + + if not (_typename == typename and _m == m): + continue + + parent = citem + while parent: + if type(parent.to_specific_type) == idaapi.cinsn_t: + break + parent = cfunc.body.find_parent_of(parent) + + if not parent: + print 'cannot find parent statement (?!)' + continue + + if parent.ea in addresses: + continue + + if parent.ea == idaapi.BADADDR: + print 'parent.ea is BADADDR' + continue + + addresses.append(parent.ea) + + self.functions.append(cfunc.entry_ea) + self.items.append((parent.ea, idc.GetFunctionName(cfunc.entry_ea), self.get_decompiled_line(cfunc, int(parent.ea)))) + + + return [] + + def populate_table(self): + + self.functions = [] + self.items = [] + + if self.__type == XREF_EA: + self.get_items_for_ea(self.__ea) + else: + self.get_items_for_type() + + self.table.setRowCount(len(self.items)) + + i = 0 + for item in self.items: + address, func, line = item + item = QtGui.QTableWidgetItem('0x%x' % (address, )) + item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) + self.table.setItem(i, 0, item) + item = QtGui.QTableWidgetItem(func) + item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) + self.table.setItem(i, 1, item) + item = QtGui.QTableWidgetItem(line) + item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) + self.table.setItem(i, 2, item) + + i += 1 + + self.table.resizeRowsToContents() + + return + + def OnClose(self, form): + pass + +class hexrays_callback_info(object): + + def __init__(self): + self.vu = None + return + + def show_xrefs(self, vu): + + vu.get_current_item(idaapi.USE_KEYBOARD) + item = vu.item + + sel = None + if item.citype == idaapi.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'): + # if an expression is selected. verify that it's either a cot_obj, cot_memref or cot_memptr + sel = item.it.to_specific_type + + elif item.citype == idaapi.VDI_FUNC: + # if the function itself is selected, show xrefs to it. + sel = item.f + else: + return False + + form = XrefsForm(sel) + form.Show() + return True + + def menu_callback(self): + self.show_xrefs(self.vu) + return 0 + + def event_callback(self, event, *args): + + try: + if event == idaapi.hxe_keyboard: + vu, keycode, shift = args + + if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("X") and shift == 0: + if self.show_xrefs(vu): + return 1 + + elif event == idaapi.hxe_right_click: + self.vu = args[0] + idaapi.add_custom_viewer_popup_item(self.vu.ct, "Xrefs", "X", self.menu_callback) + + except: + traceback.print_exc() + + return 0 + +if idaapi.init_hexrays_plugin(): + i = hexrays_callback_info() + idaapi.install_hexrays_callback(i.event_callback) +else: + print 'invert-if: hexrays is not available.' diff --git a/swig/hexrays.i b/swig/hexrays.i index 4fb7605..742a001 100644 --- a/swig/hexrays.i +++ b/swig/hexrays.i @@ -754,15 +754,15 @@ citem_t.to_specific_type = property(citem_to_specific_type) """ array used for translating cinsn_t->op type to their names. """ cinsn_t.op_to_typename = {} -for k in dir(globals()): +for k in dir(_idaapi): if k.startswith('cit_'): - cinsn_t.op_to_typename[getattr(globals(), k)] = k[4:] + cinsn_t.op_to_typename[getattr(_idaapi, k)] = k[4:] """ array used for translating cexpr_t->op type to their names. """ cexpr_t.op_to_typename = {} -for k in dir(globals()): +for k in dir(_idaapi): if k.startswith('cot_'): - cexpr_t.op_to_typename[getattr(globals(), k)] = k[4:] + cexpr_t.op_to_typename[getattr(_idaapi, k)] = k[4:] def property_op_to_typename(self): return self.op_to_typename[self.op] @@ -1066,7 +1066,7 @@ def _map_as_dict(maptype, name, keytype, valuetype): for fctname in ['begin', 'end', 'first', 'second', 'next', \ 'find', 'insert', 'erase', 'clear', 'size']: - fct = globals()[name + '_' + fctname] + fct = getattr(_idaapi, name + '_' + fctname) setattr(maptype, '__' + fctname, fct) maptype.__len__ = maptype.size diff --git a/swig/pro.i b/swig/pro.i index d61003f..7061265 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -82,7 +82,7 @@ $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); %ignore setflag; %ignore read2bytes; %ignore rotate_left; -%ignore qswap; +//%ignore qswap; %ignore swap32; %ignore swap16; %ignore swap_value; @@ -108,6 +108,10 @@ $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); %ignore qstrtok; %ignore qstrlwr; %ignore qstrupr; + +void qvector::grow(const unsigned int &x=0); +%ignore qvector::grow; + %include "pro.h" //---------------------------------------------------------------------