From ac5d88a83bc04ecf8cce54140b5b426e388fefc4 Mon Sep 17 00:00:00 2001 From: "elias.bachaalany" Date: Wed, 10 Nov 2010 13:58:08 +0000 Subject: [PATCH] IDAPython 1.4.3: - IDA 6.0 support - Python CLI now prints expression evaluation result (no need to use print()) - Changed Alt-8 to Ctrl-F3 (because it conflicts with window switching key Alt+n) - Added get_highlighted_identifier() - Added PluginForm class to allow UI development with either PyQt4 or PySide - Added idautils.Entries() to enumerate entrypoints - idc / AddConst() was broken - Minor fixes --- BUILDING.txt | 33 ++- CHANGES.txt | 10 +- README.txt | 26 ++- build.py | 8 +- examples/ex_gdl_qflow_chart.py | 24 +- python.cpp | 70 ++++-- python/idautils.py | 30 ++- python/idc.py | 2 +- python/init.py | 2 +- swig/bytes.i | 3 + swig/gdl.i | 27 +-- swig/graph.i | 28 ++- swig/idaapi.i | 5 +- swig/kernwin.i | 386 ++++++++++++++++++++++++++++----- swig/pro.i | 2 + 15 files changed, 520 insertions(+), 136 deletions(-) diff --git a/BUILDING.txt b/BUILDING.txt index 256fb1e..8641b29 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -9,13 +9,14 @@ REQUIREMENTS [Tested versions are in brackets] - - IDA and IDA SDK [5.6] + + - IDA and IDA SDK [> 5.6] http://www.hex-rays.com/idapro/ - - Python [2.5.1, 2.6.1] + - Python [2.5.1, 2.6.1, 2.7] http://www.python.org/ - - Simplified Wrapper Interface Generator (SWIG) [1.3.36] + - Simplified Wrapper Interface Generator (SWIG) [2.0] http://www.swig.org/ - Unix utilities (GNU patch on Windows): @@ -35,18 +36,18 @@ BUILDING Make sure all the needed tools (compiler, swig) are on the PATH. -1, Unpack the IDAPython source and IDA Pro SDK into the following +1. Unpack the IDAPython source and IDA Pro SDK into the following directory structure: - swigsdk-versions/5.6/ - version 5.6 of the IDA Pro SDK + swigsdk-versions/x.y/ - A supported version of the IDA Pro SDK idapython/ - IDAPython source code -2, On Mac OS X copy libida.dylib from the IDA install directory to - swigsdk-versions/5.6/lib/gcc32.mac/ +2. On Mac OS X copy libida.dylib from the IDA install directory to + swigsdk-versions/x.y/lib/gcc32.mac/ and libida64.dylib to - swigsdk-versions/5.6/lib/gcc64.mac/ + swigsdk-versions/x.y/lib/gcc64.mac/ -3, Build the plugin +3. Build the plugin python build.py @@ -55,6 +56,18 @@ Make sure all the needed tools (compiler, swig) are on the PATH. Run 'build.py --help' for more information. -4, Install the components as described in README.txt +4. Install the components as described in README.txt See build.py for build details and possible tweaks. + +On 64 bits distributions, you may need to compile Python to generate a 32bit +version of the interpreter: + +1. tar xvf Python-2.6.6.tar.bz2 +2. cd Python-2.6.6 +3. CC="gcc -m32" CXX="c++ -m32" ./configure --prefix=/path/to/py32 --enable-shared +4. make +5. make install +6. cd /path/to/py32/lib +7. ln -s libpython2.6.so.1.0 libpython2.6.so.1 +8. PYTHONHOME=/path/to/py32 LD_LIBRARY_PATH=/path/to/py32/lib idaq diff --git a/CHANGES.txt b/CHANGES.txt index 2b6cd0f..b6fdd9e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,14 @@ Please see http://code.google.com/p/idapython/source/list for a detailed list of changes. +Changes from version 1.4.2 to 1.4.3 +------------------------------------ +- IDA 6.0 support +- Python CLI now prints expression evaluation result (no need to use print()) +- Changed Alt-8 to Ctrl-F3 (because it conflicts with window switching key Alt+n) +- Added get_highlighted_identifier() +- Added PluginForm class to allow UI development with either PyQt4 or PySide +- Added idautils.Entries() to enum entrypoints + Changes from version 1.4.1 to 1.4.2 ------------------------------------ - Added command completion @@ -127,4 +136,3 @@ Changes from version 0.5.0 to 0.6.0 - Bunch of misc small cleanups and fixes - For more details see CHANGES-SWIG.txt and CHANGES-Plugin.txt - diff --git a/README.txt b/README.txt index 55af21a..7acf4c2 100644 --- a/README.txt +++ b/README.txt @@ -16,7 +16,7 @@ AVAILABILITY ------------ Latest stable versions of IDAPython are available from - http://www.d-dome.net/idapython/ + http://code.google.com/p/idapython/downloads/list Development builds are available from http://code.google.com/p/idapython/ @@ -26,7 +26,7 @@ RESOURCES --------- The full function cross-reference is readable online at - http://www.d-dome.net/idapython/reference/ + http://www.hex-rays.com/idapro/idapython_docs/ Bugs and enhancement requests should be submitted to http://code.google.com/p/idapython/issues/list @@ -38,25 +38,29 @@ Mailing list for the project is hosted by Google Groups at INSTALLATION FROM BINARIES -------------------------- -1, Install Python 2.5 or 2.6 from http://www.python.org/ -2, Copy the python and python64 directories to the IDA install directory +1. Install Python 2.5 or 2.6 from http://www.python.org/ +2. Copy the python and python64 directories to the IDA install directory 3. Copy the plugins to the %IDADIR%\plugins\ USAGE ----- -The plugin has three hotkeys: - - - Run script (Alt-9) - - Execute Python statement(s) (Alt-8) - - Run previously executed script again (Alt-7) + - Run script: File / Script file (Alt-F7) + - Execute Python statement(s) (Ctrl-F3) + - Run previously executed script again: View / Recent Scripts (Alt+F9) Batch mode execution: Start IDA with the following command line options: -A -OIDAPython:yourscript.py file_to_work_on +or +-Syourscript.py +or +-S"yourscript.py arg1 arg2 arg3" + +(Please see http://www.hexblog.com/?p=128) If you want fully unattended execution mode, make sure your script exits with a qexit() call. @@ -74,11 +78,11 @@ Where N can be: User init file: You can place your custom settings to a file called 'idapythonrc.py' -that should be placed to +that should be placed to ${HOME}/.idapro/ -or +or %AppData%\Hex-Rays\IDA Pro diff --git a/build.py b/build.py index fdc66e1..9ff09e6 100644 --- a/build.py +++ b/build.py @@ -23,8 +23,8 @@ from distutils import sysconfig # Start of user configurable options VERBOSE = True -IDA_MAJOR_VERSION = 5 -IDA_MINOR_VERSION = 7 +IDA_MAJOR_VERSION = 6 +IDA_MINOR_VERSION = 0 if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] @@ -36,7 +36,7 @@ else: # IDAPython version VERSION_MAJOR = 1 VERSION_MINOR = 4 -VERSION_PATCH = 2 +VERSION_PATCH = 3 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) @@ -89,6 +89,8 @@ BINDIST_MANIFEST = [ "examples/ex_dbg.py", "examples/ex_custview.py", "examples/ex_prefix_plugin.py", + "examples/ex_pyside.py", + "examples/ex_pyqt.py", "examples/ex_imports.py" ] diff --git a/examples/ex_gdl_qflow_chart.py b/examples/ex_gdl_qflow_chart.py index 449c714..2421e61 100644 --- a/examples/ex_gdl_qflow_chart.py +++ b/examples/ex_gdl_qflow_chart.py @@ -3,30 +3,38 @@ 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) + 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)) + 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)) + 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) + 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) + 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) + if p: + print " %x - %x [%d]:" % (pred_block.startEA, pred_block.endEA, pred_block.id) q = None f = None diff --git a/python.cpp b/python.cpp index 499f223..f4cae77 100644 --- a/python.cpp +++ b/python.cpp @@ -52,7 +52,7 @@ static const char S_IDC_ARGS_VARNAME[] = "ARGV"; static const char S_MAIN[] = "__main__"; static const char S_IDC_RUNPYTHON_STATEMENT[] = "RunPythonStatement"; -static const char S_HOTKEY_RUNSTATEMENT[] = "Alt-8"; +static const char S_HOTKEY_RUNSTATEMENT[] = "Ctrl-F3"; static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; #ifdef PLUGINFIX @@ -320,7 +320,7 @@ static void handle_python_error(char *errbuf, size_t errbufsize) //------------------------------------------------------------------------ // Helper function to get globals for the __main__ module // Note: The references are borrowed. No need to free them. -PyObject *GetMainGlobals() +static PyObject *GetMainGlobals() { PyObject *module = PyImport_AddModule(S_MAIN); if ( module == NULL ) @@ -328,6 +328,42 @@ PyObject *GetMainGlobals() return PyModule_GetDict(module); } +//------------------------------------------------------------------------ +static void PythonEvalOrExec(const char *str, const char *filename = "") +{ + // Compile as an expression + PyCompilerFlags cf = {0}; + PyObject *py_code = Py_CompileStringFlags(str, filename, Py_eval_input, &cf); + if ( py_code == NULL || PyErr_Occurred() ) + { + // Not an expression? + PyErr_Clear(); + + // Run as a string + PyRun_SimpleString(str); + return; + } + + PyObject *py_globals = GetMainGlobals(); + PyObject *py_result = PyEval_EvalCode( + (PyCodeObject *) py_code, + py_globals, + py_globals); + + Py_DECREF(py_code); + + if ( py_result == NULL || PyErr_Occurred() ) + { + PyErr_Print(); + return; + } + qstring result_str; + if ( py_result != Py_None && PyW_ObjectToString(py_result, &result_str) ) + msg("%s\n", result_str.c_str()); + + Py_DECREF(py_result); +} + //------------------------------------------------------------------------ // Simple Python statement runner function for IDC static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; @@ -654,11 +690,14 @@ bool idaapi IDAPython_extlang_run( ok = false; break; } + PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func); PyObject *pres = PyEval_EvalCodeEx( code, - globals, NULL, &pargs[0], nargs, + globals, NULL, + &pargs[0], nargs, NULL, 0, NULL, 0, NULL); + ok = return_python_result(result, pres, errbuf, errbufsize); } while ( false ); @@ -666,6 +705,7 @@ bool idaapi IDAPython_extlang_run( if ( imported_module ) Py_XDECREF(module); + return ok; } @@ -1002,29 +1042,27 @@ void enable_extlang_python(bool enable) // Execute a line in the Python CLI bool idaapi IDAPython_cli_execute_line(const char *line) { - const char *first_line = strrchr(line, '\n'); - if ( first_line == NULL ) - first_line = line; + // do not process empty lines + if ( line[0] == '\0' ) + return true; + + const char *last_line = strrchr(line, '\n'); + if ( last_line == NULL ) + last_line = line; else - first_line += 1; + last_line += 1; // skip empty lines - if ( first_line[0] != '\0' ) + if ( last_line[0] != '\0' ) { - // take a copy of the line so we r-trim it - char *tline = qstrdup(first_line); - trim(tline); - // line ends with ":" or begins with a space character? - bool more = tline[qstrlen(tline)-1] == ':' || isspace(first_line[0]); - qfree(tline); - + bool more = last_line[qstrlen(last_line)-1] == ':' || isspace(last_line[0]); if ( more ) return false; } begin_execution(); - PyRun_SimpleString(line); + PythonEvalOrExec(line); end_execution(); return true; diff --git a/python/idautils.py b/python/idautils.py index 9d97e67..310e9f1 100644 --- a/python/idautils.py +++ b/python/idautils.py @@ -16,6 +16,7 @@ import idc import types import os + def refs(ea, funcfirst, funcnext): """ Generic reference collector - INTERNAL USE ONLY. @@ -241,6 +242,7 @@ def Chunks(start): yield (chunk.startEA, chunk.endEA) status = func_iter.next() + def Modules(): """ Returns a list of module objects with name,size,base and the rebase_to attributes @@ -251,17 +253,19 @@ def Modules(): yield idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to) result = idaapi.get_next_module(mod) + def Names(): """ Returns a list of names - @return: tuple(ea, name) + @return: List of tuples (ea, name) """ for i in xrange(idaapi.get_nlist_size()): ea = idaapi.get_nlist_ea(i) name = idaapi.get_nlist_name(i) yield (ea, name) + def Segments(): """ Get list of segments (sections) in the binary image @@ -274,6 +278,20 @@ def Segments(): yield seg.startEA +def Entries(): + """ + Returns a list of entry points + + @return: List of tuples (index, ordinal, ea, name) + """ + n = idaapi.get_entry_qty() + for i in xrange(0, n): + ordinal = idaapi.get_entry_ordinal(i) + ea = idaapi.get_entry(ordinal) + name = idaapi.get_entry_name(ordinal) + yield (i, ordinal, ea, name) + + def FuncItems(start): """ Get a list of function items @@ -475,11 +493,6 @@ class Strings(object): return None -# ----------------------------------------------------------------------- -def GetRegisterList(): - """Returns the register list""" - return idaapi.ph_get_regnames() - # ----------------------------------------------------------------------- def GetIdbDir(): """ @@ -489,6 +502,11 @@ def GetIdbDir(): """ return os.path.dirname(idaapi.cvar.database_idb) + os.sep +# ----------------------------------------------------------------------- +def GetRegisterList(): + """Returns the register list""" + return idaapi.ph_get_regnames() + # ----------------------------------------------------------------------- def GetInstructionList(): """Returns the instruction list of the current processor module""" diff --git a/python/idc.py b/python/idc.py index 013914d..58b6546 100644 --- a/python/idc.py +++ b/python/idc.py @@ -7530,7 +7530,7 @@ def WriteTxt(filepath, ea1, ea2): def WriteExe(filepath): return GenerateFile(OFILE_EXE, filepath, 0, BADADDR, 0) -def AddConst(enum_id,name,value): return AddConstEx(enum_id,name,value,-1) +def AddConst(enum_id,name,value): return AddConstEx(enum_id,name,value, idaapi.BADADDR) def AddStruc(index,name): return AddStrucEx(index,name,0) def AddUnion(index,name): return AddStrucEx(index,name,1) def OpStroff(ea,n,strid): return OpStroffEx(ea,n,strid,0) diff --git a/python/init.py b/python/init.py index 53d7873..650d5b8 100644 --- a/python/init.py +++ b/python/init.py @@ -2,7 +2,7 @@ # ----------------------------------------------------------------------- # IDAPython - Python plugin for Interactive Disassembler Pro # -# Copyright (c) 2004-2010 Gergely Erdelyi +# Copyright (c) The IDAPython Team # # All rights reserved. # diff --git a/swig/bytes.i b/swig/bytes.i index f3936a3..a52e76f 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -48,6 +48,9 @@ %ignore shuffle_tribytes; %ignore set_enum_id; %ignore validate_tofs; +%ignore set_flags_nomark; +%ignore set_flbits_nomark; +%ignore clr_flbits_nomark; %ignore ida_vpagesize; %ignore ida_vpages; %ignore ida_npagesize; diff --git a/swig/gdl.i b/swig/gdl.i index 775bad4..9c0e10f 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -1,25 +1,10 @@ -%ignore cancellable_graph_t::check_cancel; -%ignore gdl_graph_t::gen_gdl; -%ignore gdl_graph_t::gen_gdl; -%ignore gdl_graph_t::path; -%ignore gdl_graph_t::path_exists; -%ignore gdl_graph_t::gen_dot; +%ignore cancellable_graph_t; +%ignore gdl_graph_t; -%ignore intmap_t::dstr; -%ignore intmap_t::print; -%ignore intseq_t::add_block; -%ignore intseq_t::add_unique; -%ignore intseq_t::del; -%ignore intseq_t::dstr; -%ignore intseq_t::print; -%ignore intseq_t::remove_block; -%ignore intset_t::dstr; -%ignore intset_t::print; -%ignore node_set_t::add; -%ignore node_set_t::extract; -%ignore node_set_t::intersect; -%ignore node_set_t::node_set_t; -%ignore node_set_t::sub; +%ignore intmap_t; +%ignore intset_t; +%ignore intseq_t; +%ignore node_set_t; %ignore qflow_chart_t::blocks; %ignore flow_chart_t; %ignore default_graph_format; diff --git a/swig/graph.i b/swig/graph.i index a349702..3faeefc 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -1,4 +1,27 @@ -#ifdef __NT__ +%ignore mutable_graph_t; +%ignore graph_visitor_t; +%ignore abstract_graph_t; + +%{ +#ifdef __GNUC__ +// for some reason GCC insists on putting the vtable into the object file, +// even though we only use mutable_graph_t by pointer +// so we define these methods here to make the linker happy + +edge_info_t *idaapi mutable_graph_t::get_edge(edge_t e) { INTERR(); }; +mutable_graph_t *idaapi mutable_graph_t::clone(void) const { INTERR(); }; +bool idaapi mutable_graph_t::redo_layout(void) { INTERR(); }; +void idaapi mutable_graph_t::resize(int n) { INTERR(); }; +int idaapi mutable_graph_t::add_node(const rect_t *r) { INTERR(); }; +ssize_t idaapi mutable_graph_t::del_node(int n) { INTERR(); }; +bool idaapi mutable_graph_t::add_edge(int i, int j, const edge_info_t *ei) { INTERR(); }; +bool idaapi mutable_graph_t::del_edge(int i, int j) { INTERR(); }; +bool idaapi mutable_graph_t::replace_edge(int i, int j, int x, int y) { INTERR(); }; +bool idaapi mutable_graph_t::refresh(void) { INTERR(); }; +bool idaapi mutable_graph_t::set_nrect(int n, const rect_t &r) { INTERR(); }; +#endif +%} + %{ // class py_graph_t @@ -726,9 +749,7 @@ void pyg_select_node(PyObject *self, int nid) } // %} -#endif // __NT__ -#ifdef __NT__ %inline %{ // void pyg_refresh(PyObject *self); @@ -739,7 +760,6 @@ void pyg_select_node(PyObject *self, int nid); bool pyg_show(PyObject *self); // %} -#endif // __NT__ %pythoncode %{ # diff --git a/swig/idaapi.i b/swig/idaapi.i index 668add1..28ad909 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -76,9 +76,7 @@ #include "err.h" #include "fpro.h" #include -#ifdef __NT__ - #include "graph.hpp" -#endif +#include "graph.hpp" // @@ -114,6 +112,7 @@ static const char S_ON_DBL_CLICK[] = "OnDblClick"; static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; static const char S_ON_KEYDOWN[] = "OnKeydown"; static const char S_ON_COMPLETE_LINE[] = "OnCompleteLine"; +static const char S_ON_CREATE[] = "OnCreate"; static const char S_ON_POPUP[] = "OnPopup"; static const char S_ON_HINT[] = "OnHint"; static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; diff --git a/swig/kernwin.i b/swig/kernwin.i index 3e2133d..e19672f 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -16,12 +16,26 @@ %ignore askfile_cv; %ignore askyn_cv; %ignore askyn_v; +%ignore add_custom_viewer_popup_item; +%ignore create_custom_viewer; +%ignore destroy_custom_viewer; +%ignore destroy_custom_viewerdestroy_custom_viewer; +%ignore get_custom_viewer_place; +%ignore set_custom_viewer_popup_menu; +%ignore set_custom_viewer_handler; +%ignore set_custom_viewer_range; +%ignore is_idaview; +%ignore refresh_custom_viewer; +%ignore set_custom_viewer_handlers; // Ignore these string functions. There are trivial replacements in Python. %ignore addblanks; %ignore trim; %ignore skipSpaces; %ignore stristr; +%ignore get_highlighted_identifier; +%rename (get_highlighted_identifier) py_get_highlighted_identifier; + // CLI %ignore cli_t; %ignore install_command_interpreter; @@ -119,49 +133,6 @@ bool idaapi py_menu_item_callback(void *userdata) return ret; } -//------------------------------------------------------------------------ -/* -# -def set_dock_pos(src, dest, orient, left = 0, top = 0, right = 0, bottom = 0) - """ - Sets the dock orientation of a window relatively to another window. - - @param src: Source docking control - @param dest: Destination docking control - @param orient: One of DOR_XXXX constants - @param left, top, right, bottom: These parameter if DOR_FLOATING is used, or if you want to specify the width of docked windows - @return: Boolean - - Example: - set_dock_pos('Structures', 'Enums', DOR_RIGHT) <- docks the Structures window to the right of Enums window - """ - pass -# -*/ - -//------------------------------------------------------------------------ -/* -int py_execute_sync(PyObject *py_callable, int reqf) -{ - if ( !PyCallable_Check(py_callable) ) - return -1; - - struct py_exec_request_t: exec_request_t - { - PyObject *py_callable; - virtual int idaapi execute(void) - { - PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); - int r = py_result == NULL || !PyInt_Check(py_result) ? -1 : PyInt_AsLong(py_result); - Py_XDECREF(py_result); - return r; - } - py_exec_request_t(PyObject *pyc): py_callable(pyc) { } - }; - py_exec_request_t req(py_callable); - return execute_sync(req, reqf); -} -*/ @@ -753,11 +724,125 @@ PyObject *choose2_find(const char *title) return NULL; return c2->get_self(); } + + +class plgform_t +{ +private: + PyObject *py_obj; + TForm *form; + + static int idaapi s_callback(void *ud, int notification_code, va_list va) + { + plgform_t *_this = (plgform_t *)ud; + if ( notification_code == ui_tform_visible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + PyObject *py_result = PyObject_CallMethod( + _this->py_obj, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL)); + + PyW_ShowErr(S_ON_CREATE); + Py_XDECREF(py_result); + } + } + else if ( notification_code == ui_tform_invisible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + PyObject *py_result = PyObject_CallMethod( + _this->py_obj, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL)); + + PyW_ShowErr(S_ON_CLOSE); + Py_XDECREF(py_result); + + _this->unhook(); + } + } + return 0; + } + + void unhook() + { + unhook_from_notification_point(HT_UI, s_callback, this); + form = NULL; + + // Call DECREF at last, since it may trigger __del__ + Py_XDECREF(py_obj); + } + +public: + plgform_t(): py_obj(NULL), form(NULL) + { + } + + bool show( + PyObject *obj, + const char *caption, + int options) + { + // Already displayed? + TForm *f = find_tform(caption); + if ( f != NULL ) + { + // Our form? + if ( f == form ) + { + // Switch to it + switchto_tform(form, true); + return true; + } + // Fail to create + return false; + } + // Create a form + form = create_tform(caption, NULL); + if ( form == NULL ) + return false; + + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) + { + form = NULL; + return false; + } + + py_obj = obj; + Py_INCREF(obj); + + if ( is_idaq() ) + options |= FORM_QWIDGET; + + this->form = form; + open_tform(form, options); + return true; + } + + void close(int options = 0) + { + if ( form != NULL ) + close_tform(form, options); + } + + static PyObject *__new__() + { + return PyCObject_FromVoidPtr(new plgform_t(), __del__); + } + + static void __del__(void *obj) + { + delete (plgform_t *)obj; + } +}; // %} -#ifdef __NT__ %{ // //-------------------------------------------------------------------------- @@ -778,7 +863,7 @@ struct py_cli_cbs_t qstring *line, int *p_x, int *p_sellen, - uint16 *vk_key, + int *vk_key, int shift); }; @@ -796,7 +881,7 @@ private: static const py_cli_cbs_t py_cli_cbs[MAX_PY_CLI]; //-------------------------------------------------------------------------- #define IMPL_PY_CLI_CB(CBN) \ - static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, uint16 *vk_key, int shift) \ + static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, int *vk_key, int shift) \ { \ return py_clis[CBN]->on_keydown(line, p_x, p_sellen, vk_key, shift); \ } \ @@ -843,7 +928,7 @@ private: qstring *line, int *p_x, int *p_sellen, - uint16 *vk_key, + int *vk_key, int shift) { PyObject *result = PyObject_CallMethod( @@ -1875,9 +1960,7 @@ public: // %} -#endif -#ifdef __NT__ %inline %{ // static int py_install_command_interpreter(PyObject *py_obj) @@ -2085,7 +2168,6 @@ bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) #undef DECL_THIS // %} -#endif // __NT__ %inline %{ uint32 idaapi choose_sizer(void *self) @@ -2179,7 +2261,28 @@ uint32 choose_choose(void *self, // //------------------------------------------------------------------------ +//------------------------------------------------------------------------ +/* +# +def get_highlighted_identifier(flags = 0): + """ + Returns the currently highlighted identifier + @param flags: reserved (pass 0) + @return: None or the highlighted identifier + """ + pass +# +*/ +static PyObject *py_get_highlighted_identifier(int flags = 0) +{ + char buf[MAXSTR]; + bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); + if ( !ok ) + Py_RETURN_NONE; + else + return PyString_FromString(buf); +} //------------------------------------------------------------------------ /* @@ -2374,8 +2477,62 @@ def py_execute_sync(callable, reqf) """ pass # +//------------------------------------------------------------------------ +static int py_execute_sync(PyObject *py_callable, int reqf) +{ + if ( !PyCallable_Check(py_callable) ) + return -1; + + struct py_exec_request_t: exec_request_t + { + PyObject *py_callable; + virtual int idaapi execute(void) + { + PyGILState_STATE state = PyGILState_Ensure(); + PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); + int r = py_result == NULL || !PyInt_Check(py_result) ? -1 : PyInt_AsLong(py_result); + Py_XDECREF(py_result); + PyGILState_Release(state); + return r; + } + py_exec_request_t(PyObject *pyc): py_callable(pyc) + { + } + }; + py_exec_request_t req(py_callable); + return execute_sync(req, reqf); +} +*/ + +//------------------------------------------------------------------------ +/* +# +def set_dock_pos(src, dest, orient, left = 0, top = 0, right = 0, bottom = 0): + """ + Sets the dock orientation of a window relatively to another window. + + @param src: Source docking control + @param dest: Destination docking control + @param orient: One of DOR_XXXX constants + @param left, top, right, bottom: These parameter if DOR_FLOATING is used, or if you want to specify the width of docked windows + @return: Boolean + + Example: + set_dock_pos('Structures', 'Enums', DOR_RIGHT) <- docks the Structures window to the right of Enums window + """ + pass +# +*/ + +//------------------------------------------------------------------------ +/* +# +def is_idaq(): + """ + Returns True or False depending if IDAPython is hosted by IDAQ + """ +# */ -//int py_execute_sync(PyObject *py_callable, int reqf); @@ -2385,6 +2542,32 @@ void choose2_refresh(PyObject *self); void choose2_close(PyObject *self); int choose2_show(PyObject *self); void choose2_activate(PyObject *self); + + +#define DECL_PLGFORM plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +static PyObject *plgform_new() +{ + return plgform_t::__new__(); +} + +static bool plgform_show( + PyObject *py_link, + PyObject *py_obj, + const char *caption, + int options = FORM_MDI|FORM_TAB|FORM_MENU|FORM_RESTORE) +{ + DECL_PLGFORM; + return plgform->show(py_obj, caption, options); +} + +static void plgform_close( + PyObject *py_link, + int options) +{ + DECL_PLGFORM; + plgform->close(options); +} +#undef DECL_PLGFORM // %} @@ -2617,6 +2800,107 @@ class Choose2(object): # """Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t""" # return [0x0, CHITEM_BOLD] # + + +class PluginForm(object): + """ + PluginForm class. + + This form can be used to host additional controls. Please check the PyQt example. + """ + + FORM_MDI = 0x01 + """start by default as MDI""" + FORM_TAB = 0x02 + """attached by default to a tab""" + FORM_RESTORE = 0x04 + """restore state from desktop config""" + FORM_ONTOP = 0x08 + """form should be "ontop""" + FORM_MENU = 0x10 + """form must be listed in the windows menu (automatically set for all plugins)""" + FORM_CENTERED = 0x20 + """form will be centered on the screen""" + FORM_PERSIST = 0x40 + """form will persist until explicitly closed with Close()""" + + + def __init__(self): + """ + """ + self.__clink__ = _idaapi.plgform_new() + + + + def Show(self, caption, options = 0): + """ + Creates the form if not was not created or brings to front if it was already created + + @param caption: The form caption + @param options: One of PluginForm.FORM_ constants + """ + options |= PluginForm.FORM_MDI|PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + return _idaapi.plgform_show(self.__clink__, self, caption, options) + + + @staticmethod + def FormToPyQtWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PyQt + + @param ctx: Context. Reference to a module that already imported SIP and QtGui modules + """ + return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) + + + @staticmethod + def FormToPySideWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PySide + + @param ctx: Context. Reference to a module that already imported QtGui module + """ + return ctx.QtGui.QWidget.FromCObject(form) + + + def OnCreate(self, form): + """ + This event is called when the plugin form is created. + The programmer should populate the form when this event is triggered. + + @return: None + """ + pass + + + def OnClose(self, form): + """ + Called when the plugin form is closed + + @return: None + """ + pass + + + def Close(self, options): + """ + Closes the form. + + @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) + + @return: None + """ + return _idaapi.plgform_close(self.__clink__) + + FORM_SAVE = 0x1 + """save state in desktop config""" + + FORM_NO_CONTEXT = 0x2 + """don't change the current context (useful for toolbars)""" + + FORM_DONT_SAVE_SIZE = 0x4 + """don't save size of the window""" + # %} diff --git a/swig/pro.i b/swig/pro.i index 22a6edf..55a3610 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -37,6 +37,8 @@ %ignore wchar2char; %ignore u2cstr; %ignore c2ustr; +%ignore base64_encode; +%ignore base64_decode; %ignore utf8_unicode; %ignore win_utf2idb; %ignore char2oem;