From c04b70e0e5e7b6da2b8e781cdc795b26147948ef Mon Sep 17 00:00:00 2001 From: Arnaud Diederen Date: Wed, 9 Mar 2016 10:33:06 +0100 Subject: [PATCH] Brought in all fixes since Jan 8th, 2016. Specifically: BUGFIX: IDAPython's choose.choose() was broken. BUGFIX: calling idaapi.del_hotkey() twice with the same argument could crash IDA. BUGFIX: IDAPython's IDC emulation of idc.GetTevRegMem() was not working. BUGFIX: IDAPython: execute_ui_requests() could crash IDA. BUGFIX: IDAPython: IDP_Hooks instances could prevent the decompiler from working properly --- Scripts/ImportExportViewer.py | 22 ++++----- api_contents.txt | 43 +++++++++++++++++ examples/ex_askusingform.py | 3 -- examples/ex_choose2.py | 5 -- examples/ex_cli.py | 2 - examples/ex_custview.py | 4 +- examples/ex_expr.py | 2 - examples/ex_graph.py | 29 ++++++++++-- examples/vds4.py | 53 +++++---------------- examples/vds_xrefs.py | 39 ++++++---------- makefile | 1 + python/idc.py | 66 +++++++++++++++++++++----- pywraps/py_choose.hpp | 6 +-- pywraps/py_dbg.hpp | 21 +++++++++ pywraps/py_idaapi.hpp | 2 +- pywraps/py_idp.hpp | 78 ++++++++++++++++++++----------- pywraps/py_kernwin.hpp | 8 +++- swig/dbg.i | 2 + swig/idd.i | 8 ++++ swig/typeinf.i | 2 + tools/deploy.py | 9 ++-- tools/genhooks/recipe_idphooks.py | 23 ++++++++- 22 files changed, 278 insertions(+), 150 deletions(-) diff --git a/Scripts/ImportExportViewer.py b/Scripts/ImportExportViewer.py index 7c668ba..2a34871 100644 --- a/Scripts/ImportExportViewer.py +++ b/Scripts/ImportExportViewer.py @@ -3,14 +3,14 @@ # - enumerate imports # - enumerate entrypoints # - Use PluginForm class -# - Use PySide with PluginForm to create a Python UI +# - Use PyQt with PluginForm to create a Python UI # # (c) Hex-Rays # import idaapi import idautils from idaapi import PluginForm -from PySide import QtGui, QtCore +from PyQt5 import QtCore, QtWidgets # -------------------------------------------------------------------------- class ImpExpForm_t(PluginForm): @@ -51,24 +51,24 @@ class ImpExpForm_t(PluginForm): self.tree.clear() # Build imports - root = QtGui.QTreeWidgetItem(self.tree) + root = QtWidgets.QTreeWidgetItem(self.tree) root.setText(0, "Imports") for dll_name, imp_entries in self.BuildImports().items(): - imp_dll = QtGui.QTreeWidgetItem(root) + imp_dll = QtWidgets.QTreeWidgetItem(root) imp_dll.setText(0, dll_name) for imp_ea, imp_name, imp_ord in imp_entries: - item = QtGui.QTreeWidgetItem(imp_dll) + item = QtWidgets.QTreeWidgetItem(imp_dll) item.setText(0, "%s [0x%08x]" %(imp_name, imp_ea)) # Build exports - root = QtGui.QTreeWidgetItem(self.tree) + root = QtWidgets.QTreeWidgetItem(self.tree) root.setText(0, "Exports") for exp_i, exp_ord, exp_ea, exp_name in self.BuildExports(): - item = QtGui.QTreeWidgetItem(root) + item = QtWidgets.QTreeWidgetItem(root) item.setText(0, "%s [#%d] [0x%08x]" % (exp_name, exp_ord, exp_ea)) @@ -78,15 +78,15 @@ class ImpExpForm_t(PluginForm): """ # Get parent widget - self.parent = self.FormToPySideWidget(form) + self.parent = self.FormToPyQtWidget(form) # Create tree control - self.tree = QtGui.QTreeWidget() + self.tree = QtWidgets.QTreeWidget() self.tree.setHeaderLabels(("Names",)) self.tree.setColumnWidth(0, 100) # Create layout - layout = QtGui.QVBoxLayout() + layout = QtWidgets.QVBoxLayout() layout.addWidget(self.tree) self.PopulateTree() @@ -121,4 +121,4 @@ def main(): ImpExpForm.Show() # -------------------------------------------------------------------------- -main() \ No newline at end of file +main() diff --git a/api_contents.txt b/api_contents.txt index 2f000b4..6593fb9 100644 --- a/api_contents.txt +++ b/api_contents.txt @@ -2051,6 +2051,7 @@ 'delete_enumplace_t', 'delete_eval_ctx_t', 'delete_exception_info_t', + 'delete_excvec_t', 'delete_extra_cmts', 'delete_fixup_data_t', 'delete_fnumber_t', @@ -2461,6 +2462,44 @@ 'exception_info_t_handle', 'exception_info_t_name_get', 'exception_info_t_name_set', + 'excvec_t___getitem__', + 'excvec_t___len__', + 'excvec_t___setitem__', + 'excvec_t_at', + 'excvec_t_back', + 'excvec_t_back__SWIG_0', + 'excvec_t_back__SWIG_1', + 'excvec_t_begin', + 'excvec_t_begin__SWIG_0', + 'excvec_t_begin__SWIG_1', + 'excvec_t_capacity', + 'excvec_t_clear', + 'excvec_t_empty', + 'excvec_t_end', + 'excvec_t_end__SWIG_0', + 'excvec_t_end__SWIG_1', + 'excvec_t_erase', + 'excvec_t_erase__SWIG_0', + 'excvec_t_erase__SWIG_1', + 'excvec_t_extract', + 'excvec_t_front', + 'excvec_t_front__SWIG_0', + 'excvec_t_front__SWIG_1', + 'excvec_t_grow', + 'excvec_t_inject', + 'excvec_t_insert', + 'excvec_t_pop_back', + 'excvec_t_push_back', + 'excvec_t_push_back__SWIG_0', + 'excvec_t_push_back__SWIG_1', + 'excvec_t_qclear', + 'excvec_t_reserve', + 'excvec_t_resize', + 'excvec_t_resize__SWIG_0', + 'excvec_t_resize__SWIG_1', + 'excvec_t_size', + 'excvec_t_swap', + 'excvec_t_truncate', 'execute', 'execute_sync', 'execute_ui_requests', @@ -3279,6 +3318,7 @@ 'get_tev_info', 'get_tev_memory_info', 'get_tev_qty', + 'get_tev_reg_mem', 'get_tev_reg_mem_ea', 'get_tev_reg_mem_qty', 'get_tev_reg_val', @@ -4711,6 +4751,9 @@ 'new_exception_info_t', 'new_exception_info_t__SWIG_0', 'new_exception_info_t__SWIG_1', + 'new_excvec_t', + 'new_excvec_t__SWIG_0', + 'new_excvec_t__SWIG_1', 'new_fixup_data_t', 'new_fnumber_t', 'new_func_item_iterator_t', diff --git a/examples/ex_askusingform.py b/examples/ex_askusingform.py index 04f7b78..977e364 100644 --- a/examples/ex_askusingform.py +++ b/examples/ex_askusingform.py @@ -4,7 +4,6 @@ # from idaapi import Form -# # -------------------------------------------------------------------------- class TestEmbeddedChooserClass(Choose2): """ @@ -372,8 +371,6 @@ def test_dropdown_nomodal(): tdn_form, _ = tdn_form.Compile() tdn_form.Open() -# - # -------------------------------------------------------------------------- ida_main() diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py index e91eef1..d7915eb 100644 --- a/examples/ex_choose2.py +++ b/examples/ex_choose2.py @@ -1,9 +1,6 @@ import idaapi from idaapi import Choose2 -# - - class chooser_handler_t(idaapi.action_handler_t): def __init__(self, thing): idaapi.action_handler_t.__init__(self) @@ -129,5 +126,3 @@ if __name__ == '__main__': #test_choose2_embedded() test_choose2(False) - -# diff --git a/examples/ex_cli.py b/examples/ex_cli.py index b68d58c..183ac34 100644 --- a/examples/ex_cli.py +++ b/examples/ex_cli.py @@ -4,7 +4,6 @@ # from idaapi import NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE, COLSTR, cli_t -# class mycli_t(cli_t): flags = 0 sname = "pycli" @@ -59,7 +58,6 @@ class mycli_t(cli_t): """ print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) return None -# # ----------------------------------------------------------------------- diff --git a/examples/ex_custview.py b/examples/ex_custview.py index 36455fb..c38b03b 100644 --- a/examples/ex_custview.py +++ b/examples/ex_custview.py @@ -5,8 +5,8 @@ import idaapi import idc from idaapi import simplecustviewer_t -# +# ----------------------------------------------------------------------- class say_something_handler_t(idaapi.action_handler_t): def __init__(self, thing): idaapi.action_handler_t.__init__(self) @@ -179,5 +179,3 @@ def make_many(n): v.Show() L.append(v) return L - -# diff --git a/examples/ex_expr.py b/examples/ex_expr.py index 765d746..a40e449 100644 --- a/examples/ex_expr.py +++ b/examples/ex_expr.py @@ -4,7 +4,6 @@ # from idaapi import set_idc_func_ex -# def py_power(n, e): return n ** e @@ -13,4 +12,3 @@ if ok: print("Now the pow() will be present IDC!") else: print("Failed to register pow() IDC function") -# diff --git a/examples/ex_graph.py b/examples/ex_graph.py index 62f67a1..dafeb6b 100644 --- a/examples/ex_graph.py +++ b/examples/ex_graph.py @@ -17,30 +17,51 @@ class GraphCloser(action_handler_t): return AST_ENABLE_ALWAYS +class ColorChanger(action_handler_t): + def __init__(self, graph): + action_handler_t.__init__(self) + self.graph = graph + + def activate(self, ctx): + self.graph.color = self.graph.color ^ 0xffffff + self.graph.Refresh() + return 1 + + def update(self, ctx): + return AST_ENABLE_ALWAYS + + class MyGraph(GraphViewer): def __init__(self, funcname, result): self.title = "call graph of " + funcname GraphViewer.__init__(self, self.title) self.funcname = funcname self.result = result + self.color = 0xff00ff def OnRefresh(self): self.Clear() - id = self.AddNode(self.funcname) + id = self.AddNode((self.funcname, self.color)) for x in self.result.keys(): - callee = self.AddNode(x) + callee = self.AddNode((x, self.color)) self.AddEdge(id, callee) return True def OnGetText(self, node_id): - return str(self[node_id]) + return self[node_id] def Show(self): if not GraphViewer.Show(self): return False + # graph closer actname = "graph_closer:%s" % self.title - register_action(action_desc_t(actname, "Close %s" % self.title, GraphCloser(self))) + register_action(action_desc_t(actname, "Close: %s" % self.title, GraphCloser(self))) + attach_action_to_popup(self.GetTCustomControl(), None, actname) + + # color changer + actname = "color_changer:%s" % self.title + register_action(action_desc_t(actname, "Change colors: %s" % self.title, ColorChanger(self))) attach_action_to_popup(self.GetTCustomControl(), None, actname) return True diff --git a/examples/vds4.py b/examples/vds4.py index 5c8e02d..0cb666c 100644 --- a/examples/vds4.py +++ b/examples/vds4.py @@ -68,51 +68,22 @@ def run(): idaapi.user_numforms_free(numforms) # Display user-defined local variable information - # First defined the visitor class - class dump_lvar_info_t(idaapi.user_lvar_visitor_t): + lvinf = idaapi.lvar_uservec_t() + if idaapi.restore_user_lvar_settings(lvinf, entry_ea): + print "------- User defined local variable information\n" + for lv in lvinf.lvvec: + print "Lvar defined at %x" % (lv.ll.defea, ) - def __init__(self): - idaapi.user_lvar_visitor_t.__init__(self) - self.displayed_header = False - return + if len(str(lv.name)): + print " Name: %s" % (str(lv.name), ) - def get_info_qty_for_saving(self): - return 0 + if len(str(lv.type)): + #~ print_type_to_one_line(buf, sizeof(buf), idati, .c_str()); + print " Type: %s" % (str(lv.type), ) - def get_info_for_saving(self, lv): - return False + if len(str(lv.cmt)): + print " Comment: %s" % (str(lv.cmt), ) - def handle_retrieved_info(self, lv): - - try: - if not self.displayed_header: - self.displayed_header = True; - print "------- User defined local variable information" - - print "Lvar defined at %x" % (lv.ll.defea, ) - - if len(str(lv.name)): - print " Name: %s" % (str(lv.name), ) - - if len(str(lv.type)): - #~ print_type_to_one_line(buf, sizeof(buf), idati, .c_str()); - print " Type: %s" % (str(lv.type), ) - - if len(str(lv.cmt)): - print " Comment: %s" % (str(lv.cmt), ) - except: - traceback.print_exc() - return 0 - - def handle_retrieved_mapping(self, lm): - return 0 - - def get_info_mapping_for_saving(self): - return None - - # Now iterate over all user definitions - dli = dump_lvar_info_t(); - idaapi.restore_user_lvar_settings(entry_ea, dli) return diff --git a/examples/vds_xrefs.py b/examples/vds_xrefs.py index b78eb26..b949b27 100644 --- a/examples/vds_xrefs.py +++ b/examples/vds_xrefs.py @@ -15,17 +15,7 @@ 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' +from PyQt5 import QtCore, QtWidgets XREF_EA = 0 XREF_STRUC_MEMBER = 1 @@ -78,10 +68,7 @@ class XrefsForm(idaapi.PluginForm): def OnCreate(self, form): # Get parent widget - try: - self.parent = self.FormToPySideWidget(form) - except: - self.parent = self.FormToPyQtWidget(form) + self.parent = self.FormToPyQtWidget(form) self.populate_form() @@ -93,16 +80,16 @@ class XrefsForm(idaapi.PluginForm): def populate_form(self): # Create layout - layout = QtGui.QVBoxLayout() + layout = QtWidgets.QVBoxLayout() - layout.addWidget(QtGui.QLabel(self.__name)) - self.table = QtGui.QTableWidget() + layout.addWidget(QtWidgets.QLabel(self.__name)) + self.table = QtWidgets.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.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem("Address")) + self.table.setHorizontalHeaderItem(1, QtWidgets.QTableWidgetItem("Function")) + self.table.setHorizontalHeaderItem(2, QtWidgets.QTableWidgetItem("Line")) self.table.setColumnWidth(0, 80) self.table.setColumnWidth(1, 150) @@ -110,8 +97,8 @@ class XrefsForm(idaapi.PluginForm): self.table.cellDoubleClicked.connect(self.double_clicked) - #~ self.table.setSelectionMode(QtGui.QAbstractItemView.NoSelection) - self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows ) + #~ self.table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows ) self.parent.setLayout(layout) self.populate_table() @@ -244,13 +231,13 @@ class XrefsForm(idaapi.PluginForm): i = 0 for item in self.items: address, func, line = item - item = QtGui.QTableWidgetItem('0x%x' % (address, )) + item = QtWidgets.QTableWidgetItem('0x%x' % (address, )) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 0, item) - item = QtGui.QTableWidgetItem(func) + item = QtWidgets.QTableWidgetItem(func) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 1, item) - item = QtGui.QTableWidgetItem(line) + item = QtWidgets.QTableWidgetItem(line) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 2, item) diff --git a/makefile b/makefile index 1bc2ff3..8443621 100644 --- a/makefile +++ b/makefile @@ -94,6 +94,7 @@ ifeq ($(OUT_OF_TREE_BUILD),) DEPLOY_INIT_PY=$(SCRIPTDIR)/init.py DEPLOY_IDC_PY=$(SCRIPTDIR)/idc.py DEPLOY_IDAUTILS_PY=$(SCRIPTDIR)/idautils.py + DEPLOY_PYTHON_CFG=$(C)python.cfg TEST_IDC=test_idc else SCRIPTDIR=python diff --git a/python/idc.py b/python/idc.py index b34277c..e2be3d1 100644 --- a/python/idc.py +++ b/python/idc.py @@ -1715,11 +1715,7 @@ def GetInputMD5(): @return: MD5 string or None on error """ - ua = idaapi.uchar_array(16) - if idaapi.retrieve_input_file_md5(ua.cast()): - return "".join(["%02X" % ua[i] for i in xrange(16)]) - else: - return None + return idaapi.retrieve_input_file_md5() def GetFlags(ea): @@ -2032,7 +2028,7 @@ def GetReg(ea, reg): @return: the value of the segment register or -1 on error @note: The segment registers in 32bit program usually contain selectors, - so to get paragraph pointed by the segment register you need to + so to get paragraph pointed to by the segment register you need to call AskSelector() function. """ reg = idaapi.str2reg(reg); @@ -2598,6 +2594,8 @@ LFLG_64BIT = 0x04 # 64-bit program? LFLG_DBG_NOPATH = 0x08 # do not store input full path LFLG_SNAPSHOT = 0x10 # is memory snapshot? # in debugger process options +LFLG_8ALIGN4 = 0x40 # 4 byte alignment for 8byte scalars + # (__int64/double) inside structures INF_DEMNAMES = 14 # char; display demangled names as: DEMNAM_CMNT = 0 # comments DEMNAM_NAME = 1 # regular names @@ -2822,6 +2820,9 @@ INF_SIZEOF_LONG = 190 INF_SIZEOF_LLONG = 191 INF_CHANGE_COUNTER = 192 # database change counter; keeps track of byte and segment modifications INF_SIZEOF_LDBL = 196 # uchar; sizeof(long double) +INF_REFCMTS = 221 # uchar; number of comment lines to generate for refs + # to ASCII string or demangled name + # 0 - such comments won't be generated at all # Redefine these offsets for 64-bit version if __EA64__: @@ -2895,7 +2896,8 @@ if __EA64__: INF_SIZEOF_LONG = 274 INF_SIZEOF_LLONG = 275 INF_CHANGE_COUNTER = 276 - INF_SIZEOF_LBDL = 280 + INF_SIZEOF_LDBL = 280 + INF_REFCMTS = 305 _INFMAP = { INF_VERSION : (False, 'version'), # short; Version of database @@ -7050,6 +7052,38 @@ PT_HIGH = 0x0080 # assume high level prototypes PT_LOWER = 0x0100 # lower the function prototypes +def PrintLocalTypes(ordinals, flags): + """ + Print types in a format suitable for use in a header file + + @param ordinals: comma-separated list of type ordinals + @param flags: combination of PDF_... constants or 0 + + @return: string containing the type definitions + """ + class def_sink(idaapi.text_sink_t): + + def __init__(self): + idaapi.text_sink_t.__init__(self) + self.text = "" + + def _print(self, defstr): + self.text += defstr + return 0 + + sink = def_sink() + py_ordinals = map(lambda l : int(l), ordinals.split(",")) + idaapi.print_decls(sink, idaapi.cvar.idati, py_ordinals, flags) + + return sink.text + + +PDF_INCL_DEPS = 0x1 # include dependencies +PDF_DEF_FWD = 0x2 # allow forward declarations +PDF_DEF_BASE = 0x4 # include base types: __int8, __int16, etc.. +PDF_HEADER_CMT = 0x8 # prepend output with a descriptive comment + + def GetMaxLocalType(): """ Get number of local types + 1 @@ -8038,8 +8072,7 @@ def SetBptAttr(address, bptattr, value): if bptattr == BPTATTR_FLAGS: bpt.flags = value - idaapi.update_bpt(bpt) - return True + return idaapi.update_bpt(bpt) def SetBptCndEx(ea, cnd, is_lowcnd): """ @@ -8281,14 +8314,20 @@ def GetTevRegVal(tev, reg): def GetTevRegMemQty(tev): """ - Return the number of memory addresses recorded for the specified event + Return the number of blobs of memory recorded, for the specified event + + Note: this requires that the tracing options have been set to record pieces of memory for instruction events + @param tev: event number """ return idaapi.get_tev_reg_mem_qty(tev) def GetTevRegMem(tev, idx): """ - Return the memory pointed by 'index' for the specified event + Return the blob of memory pointed to by 'index', for the specified event + + Note: this requires that the tracing options have been set to record pieces of memory for instruction events + @param tev: event number @param idx: memory address index """ @@ -8296,7 +8335,10 @@ def GetTevRegMem(tev, idx): def GetTevRegMemEa(tev, idx): """ - Return the address pointed by 'index' for the specified event + Return the address of the blob of memory pointed to by 'index' for the specified event + + Note: this requires that the tracing options have been set to record pieces of memory for instruction events + @param tev: event number @param idx: memory address index """ diff --git a/pywraps/py_choose.hpp b/pywraps/py_choose.hpp index 8e4995f..369d77e 100644 --- a/pywraps/py_choose.hpp +++ b/pywraps/py_choose.hpp @@ -39,7 +39,7 @@ void idaapi choose_enter(void *self, uint32 n) //--------------------------------------------------------------------------- uint32 choose_choose( - void *self, + PyObject *self, int flags, int x0,int y0, int x1,int y1, @@ -48,14 +48,14 @@ uint32 choose_choose( int icon) { PYW_GIL_CHECK_LOCKED_SCOPE(); - newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); + newref_t pytitle(PyObject_GetAttrString(self, "title")); const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; int r = choose( flags, x0, y0, x1, y1, - self, + (void*) self, width, choose_sizer, choose_getl, diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp index b0592c5..750a8ea 100644 --- a/pywraps/py_dbg.hpp +++ b/pywraps/py_dbg.hpp @@ -338,5 +338,26 @@ static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) : answer; } +//------------------------------------------------------------------------- +static PyObject *py_get_tev_reg_mem(int tev, int reg) +{ + tev_info_t ti; + memreg_infos_t mis; + bool ok = get_tev_info(tev, &ti) + && get_insn_tev_reg_mem(tev, &mis) + && reg >= 0 && reg < mis.size(); + if ( ok ) + { + PyObject *py_str = PyString_FromStringAndSize( + (const char *) mis[reg].bytes.begin(), + mis[reg].bytes.size()); + return py_str; + } + else + { + Py_RETURN_NONE; + } +} + // #endif diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index 633e283..f3cf3fa 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -1757,7 +1757,7 @@ static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) if ( sv == NULL || idx >= sv->size() ) Py_RETURN_NONE; else - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); + return PyLong_FromUnsignedLongLong(size_t(&sv->at(idx))); } diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp index 3cd639b..e2cb0c2 100644 --- a/pywraps/py_idp.hpp +++ b/pywraps/py_idp.hpp @@ -695,35 +695,29 @@ struct libfunc_t; int idaapi IDP_Callback(void *ud, int notification_code, va_list va); class IDP_Hooks { -public: - virtual ~IDP_Hooks() - { - unhook(); - } - - bool hook() - { - return hook_to_notification_point(HT_IDP, IDP_Callback, this); - } - - bool unhook() - { - return unhook_from_notification_point(HT_IDP, IDP_Callback, this); - } - + friend int idaapi IDP_Callback(void *ud, int notification_code, va_list va); static int bool_to_cmdsize(bool in) { return in ? (1 + cmd.size) : 0; } static int bool_to_2or0(bool in) { return in ? 2 : 0; } static int cm_t_to_int(cm_t cm) { return int(cm); } + static bool _handle_string_output(PyObject *o, char *buf, size_t bufsize) + { + bool is_str = o != NULL && PyString_Check(o); + if ( is_str && buf != NULL ) + qstrncpy(buf, PyString_AS_STRING(o), bufsize); + Py_XDECREF(o); + return is_str; + } + static bool _handle_qstring_output(PyObject *o, qstring *buf) + { + bool is_str = o != NULL && PyString_Check(o); + if ( is_str && buf != NULL ) + buf->append(PyString_AS_STRING(o)); + Py_XDECREF(o); + return is_str; + } static int handle_custom_mnem_output(PyObject *o, char *buf, size_t bufsize) { - int rc = 0; - if ( o != NULL && PyString_Check(o) ) - { - qstrncpy(buf, PyString_AS_STRING(o), bufsize); - rc = 2; - } - Py_XDECREF(o); - return rc; + return _handle_string_output(o, buf, bufsize) ? 2 : 0; } static int handle_assemble_output(PyObject *o, ea_t /*ea*/, ea_t /*cs*/, ea_t /*ip*/, bool /*use32*/, const char */*line*/, uchar *bin) { @@ -743,7 +737,33 @@ public: Py_XDECREF(o); return rc; } + static int handle_get_reg_name_output(PyObject *o, int /*reg*/, size_t /*width*/, char *buf, size_t bufsize, int /*reghi*/) + { + int rc = 0; + if ( _handle_string_output(o, buf, bufsize) ) + rc = qstrlen(buf) + 2; + return rc; + } + static int handle_decorate_name3_output(PyObject *o, qstring *outbuf, const char * /*name*/, bool /*mangle*/, int /*cc*/) + { + return _handle_qstring_output(o, outbuf) ? 2 : 0; + } +public: + virtual ~IDP_Hooks() + { + unhook(); + } + + bool hook() + { + return hook_to_notification_point(HT_IDP, IDP_Callback, this); + } + + bool unhook() + { + return unhook_from_notification_point(HT_IDP, IDP_Callback, this); + } // hookgenIDP:methods virtual int init(const char * idp_modname) {qnotused(idp_modname); return 0;} virtual int term() {return 0;} @@ -792,7 +812,7 @@ virtual void kernel_config_loaded() {} virtual int might_change_sp(ea_t ea) {qnotused(ea); return 0;} virtual int is_alloca_probe(ea_t ea) {qnotused(ea); return 0;} virtual int out_3byte(ea_t dataea, uint32 value, bool analyze_only) {qnotused(dataea); qnotused(value); qnotused(analyze_only); return 0;} -virtual int get_reg_name(int reg, size_t width, char * buf, size_t bufsize, int reghi) {qnotused(reg); qnotused(width); qnotused(buf); qnotused(bufsize); qnotused(reghi); return 0;} +virtual PyObject * get_reg_name(int reg, size_t width, int reghi) {qnotused(reg); qnotused(width); qnotused(reghi); Py_RETURN_NONE;} virtual void savebase() {} virtual void gen_asm_or_lst(bool starting, FILE * fp, bool is_asm, int flags, gen_outline_t ** outline) {qnotused(starting); qnotused(fp); qnotused(is_asm); qnotused(flags); qnotused(outline); } virtual int out_src_file_lnnum() {return 0;} @@ -847,7 +867,7 @@ virtual int calc_cdecl_purged_bytes2() {return 0;} virtual int get_stkarg_offset2() {return 0;} virtual int til_for_file() {return 0;} virtual int equal_reglocs(argloc_t * a1, argloc_t * a2) {qnotused(a1); qnotused(a2); return 0;} -virtual int decorate_name3(qstring * outbuf, const char * name, bool mangle, int cc) {qnotused(outbuf); qnotused(name); qnotused(mangle); qnotused(cc); return 0;} +virtual PyObject * decorate_name3(const char * name, bool mangle, int cc) {qnotused(name); qnotused(mangle); qnotused(cc); Py_RETURN_NONE;} virtual int calc_retloc3(const tinfo_t * rettype, cm_t cc, argloc_t * retloc) {qnotused(rettype); qnotused(cc); qnotused(retloc); return 0;} virtual int calc_varglocs3(const func_type_data_t * ftd, regobjs_t * regs, relobj_t * stkargs, int nfixed) {qnotused(ftd); qnotused(regs); qnotused(stkargs); qnotused(nfixed); return 0;} virtual int calc_arglocs3(func_type_data_t * fti) {qnotused(fti); return 0;} @@ -1260,7 +1280,8 @@ case processor_t::get_reg_name: char * buf = va_arg(va, char *); size_t bufsize = va_arg(va, size_t); int reghi = va_arg(va, int); - ret = proxy->get_reg_name(reg, width, buf, bufsize, reghi); + PyObject * _tmp = proxy->get_reg_name(reg, width, reghi); + ret = IDP_Hooks::handle_get_reg_name_output(_tmp, reg, width, buf, bufsize, reghi); } break; @@ -1707,7 +1728,8 @@ case processor_t::decorate_name3: const char * name = va_arg(va, const char *); bool mangle = bool(va_arg(va, int)); cm_t cc = cm_t(va_arg(va, int)); - ret = proxy->decorate_name3(outbuf, name, mangle, IDP_Hooks::cm_t_to_int(cc)); + PyObject * _tmp = proxy->decorate_name3(name, mangle, IDP_Hooks::cm_t_to_int(cc)); + ret = IDP_Hooks::handle_decorate_name3_output(_tmp, outbuf, name, mangle, cc); } break; diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp index a17d282..7b6db62 100644 --- a/pywraps/py_kernwin.hpp +++ b/pywraps/py_kernwin.hpp @@ -427,11 +427,16 @@ bool py_del_hotkey(PyObject *pyctx) return false; py_idchotkey_ctx_t *ctx = (py_idchotkey_ctx_t *) PyCObject_AsVoidPtr(pyctx); - if ( !del_idc_hotkey(ctx->hotkey.c_str()) ) + if ( ctx == NULL || !del_idc_hotkey(ctx->hotkey.c_str()) ) return false; Py_DECREF(ctx->pyfunc); delete ctx; + // Here we must ensure that the python object is invalidated. + // This is to avoid the possibility of this function being called again + // with the same ctx, which would contain a pointer to a deleted object. + PyCObject_SetVoidPtr(pyctx, NULL); + return true; } @@ -826,6 +831,7 @@ static bool py_execute_ui_requests(PyObject *py_list) virtual idaapi ~py_ui_request_t() { + PYW_GIL_GET; py_callables.clear(); } }; diff --git a/swig/dbg.i b/swig/dbg.i index ae7db6e..2929e96 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -44,6 +44,8 @@ typedef struct %ignore internal_get_sreg_base; %rename (internal_get_sreg_base) py_internal_get_sreg_base; +%rename (get_tev_reg_mem) py_get_tev_reg_mem; + // We want ALL wrappers around what is declared in dbg.hpp // to release the GIL when calling into the IDA api: those // might be very long operations, that even require some diff --git a/swig/idd.i b/swig/idd.i index 50b1720..9436f10 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -10,6 +10,14 @@ %ignore gdecode_t; %apply unsigned char { char dtyp }; +%ignore qvector::operator==; +%ignore qvector::operator!=; +%ignore qvector::find; +%ignore qvector::has; +%ignore qvector::del; +%ignore qvector::add_unique; +%template(excvec_t) qvector; + %include "idd.hpp" %clear(char dtyp); diff --git a/swig/typeinf.i b/swig/typeinf.i index 9fabf8b..779cc69 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -90,6 +90,8 @@ %rename (get_named_type64) py_get_named_type64; %rename ("%s") tinfo_t::get_named_type; +%ignore udt_type_data_t::is_last_baseclass; + %rename (print_decls) py_print_decls; %ignore print_decls; diff --git a/tools/deploy.py b/tools/deploy.py index 624d01e..2acd26b 100644 --- a/tools/deploy.py +++ b/tools/deploy.py @@ -9,13 +9,10 @@ import re import os major, minor, micro, _, _ = sys.version_info +if major < 2 or minor < 7: + raise Exception("Expected Python version 2.7.x, but got %s.%s.%s (from %s)" % (major, minor, micro, sys.executable)) -try: - from argparse import ArgumentParser -except: - print "Failed to import module 'argparse'. Upgrade to Python 2.7, copy argparse.py to this directory or try 'apt-get install python-argparse'" - raise - +from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument("-t", "--template", required=True) parser.add_argument("-o", "--output", required=True) diff --git a/tools/genhooks/recipe_idphooks.py b/tools/genhooks/recipe_idphooks.py index a221ab0..c7bf03d 100644 --- a/tools/genhooks/recipe_idphooks.py +++ b/tools/genhooks/recipe_idphooks.py @@ -82,7 +82,26 @@ recipe = { "cc" : { "type" : "int", "convertor" : "IDP_Hooks::cm_t_to_int", - } + }, + "outbuf" : { "suppress_for_call" : True, }, + }, + "return" : { + "type" : "PyObject *", + "retexpr" : "Py_RETURN_NONE", + "convertor" : "IDP_Hooks::handle_decorate_name3_output", + "convertor_pass_args" : True, } - } + }, + "get_reg_name" : { + "params" : { + "buf" : { "suppress_for_call" : True, }, + "bufsize" : { "suppress_for_call" : True, }, + }, + "return" : { + "type" : "PyObject *", + "retexpr" : "Py_RETURN_NONE", + "convertor" : "IDP_Hooks::handle_get_reg_name_output", + "convertor_pass_args" : True, + } + }, }