From db58b31711487cb129a4785556612d678f668cb8 Mon Sep 17 00:00:00 2001 From: "elias.bachaalany@gmail.com" Date: Wed, 3 Jul 2013 01:40:54 +0000 Subject: [PATCH] - Experimental: integrated Hex-Rays Decompiler bindings that were contributed by EiNSTeiN: https://github.com/EiNSTeiN-/hexrays-python - Added '--with-hexrays' switch to the build script so it wrap Hex-Rays Decompiler API - Added one Hex-Rays decompiler sample: vds1.py --- BUILDING.txt | 3 + CHANGES.txt | 9 + build.py | 79 +- examples/vds1.py | 27 + idapython.vcxproj | 4 +- idapython.vcxproj.filters | 6 + python.cpp | 3201 +++++++++++++++++++------------------ pywraps/py_idaapi.hpp | 3 +- pywraps/py_idaapi.py | 94 +- swig/hexrays.i | 1111 +++++++++++++ swig/idaapi.i | 103 +- swig/pro.i | 235 +-- swig/typeconv.i | 274 ++-- 13 files changed, 3221 insertions(+), 1928 deletions(-) create mode 100644 examples/vds1.py create mode 100644 swig/hexrays.i diff --git a/BUILDING.txt b/BUILDING.txt index f65c4d6c..ddd57e90 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -42,6 +42,9 @@ Make sure all the needed tools (compiler, swig) are on the PATH. swigsdk-versions/x.y/ - A supported version of the IDA SDK idapython/ - IDAPython source code +Note: To build with Hex-Rays decompiler support, please copy hexrays.hpp from + the decompiler SDK folder into IDA's include folder (in the SDK). + 2. On Mac OS X copy libida.dylib from the IDA install directory to swigsdk-versions/x.y/lib/x86_mac_gcc_32/ and libida64.dylib to diff --git a/CHANGES.txt b/CHANGES.txt index 74381e7f..90e355b2 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.5.6 to 1.5.7 +------------------------------------ +- Added '--with-hexrays' switch to the build script so it wrap Hex-Rays Decompiler API +- Experimental: integrated Hex-Rays Decompiler bindings that were contributed by EiNSTeiN: + https://github.com/EiNSTeiN-/hexrays-python +- Added one Hex-Rays decompiler sample: vds1.py +- Fixed small mismatch between SWIG define and CL defines (/DNO_OBSOLETE_FUNCS) +- Use print_type2() instead of the deprecated function print_type() + Changes from version 1.5.5 to 1.5.6 ------------------------------------ - IDA Pro 6.4 support diff --git a/build.py b/build.py index e855b173..06e62b0e 100644 --- a/build.py +++ b/build.py @@ -45,6 +45,10 @@ # Find Python headers PYTHON_INCLUDE_DIRECTORY = sysconfig.get_config_var('INCLUDEPY') +S_EA64 = 'ea64' +S_WITH_HEXRAYS = 'with-hexrays' +S_NO_OPT = 'no-opt' + # Swig command-line parameters SWIG_OPTIONS = '-modern -python -c++ -w451 -shadow -D__GNUC__ -DNO_OBSOLETE_FUNCS' @@ -61,6 +65,7 @@ # Common includes for all compilations COMMON_INCLUDES = [ ".", "swig" ] +# ----------------------------------------------------------------------- # List files for the binary distribution BINDIST_MANIFEST = [ "README.txt", @@ -104,6 +109,7 @@ "examples/ex_imports.py" ] +# ----------------------------------------------------------------------- # List files for the source distribution (appended to binary list) SRCDIST_MANIFEST = [ "BUILDING.txt", @@ -150,14 +156,31 @@ "swig/xref.i", "swig/graph.i", "swig/fpro.i", + "swig/hexrays.i", "tools/gendocs.py", ] +# ----------------------------------------------------------------------- +def parse_options(args): + """Parse arguments and returned a dictionary of options""" + + no_opt = '--' + S_NO_OPT in sys.argv + ea64 = '--' + S_EA64 in sys.argv + with_hexrays = '--' + S_WITH_HEXRAYS in sys.argv + + return { + S_EA64: ea64, + S_WITH_HEXRAYS: with_hexrays, + S_NO_OPT: no_opt + } + +# ----------------------------------------------------------------------- class BuilderBase: """ Base class for builders """ def __init__(self): pass + def compile(self, source, objectname=None, includes=[], macros=[]): """ Compile the source file @@ -187,6 +210,7 @@ def compile(self, source, objectname=None, includes=[], macros=[]): print cmdstring return os.system(cmdstring) + def link(self, objects, outfile, libpaths=[], libraries=[], extra_parameters=None): """ Link the binary from objects and libraries """ cmdstring = "%s %s %s" % (self.linker, @@ -217,6 +241,7 @@ def _build_command_string(self, macros, argument_delimiter): return macrostring +# ----------------------------------------------------------------------- class GCCBuilder(BuilderBase): """ Generic GCC compiler class """ def __init__(self): @@ -241,6 +266,7 @@ def linker_out_string(self, filename): return "-o %s" % filename +# ----------------------------------------------------------------------- class MSVCBuilder(BuilderBase): """ Generic Visual C compiler class """ def __init__(self): @@ -266,7 +292,7 @@ def compiler_out_string(self, filename): def linker_out_string(self, filename): return "/out:%s" % filename - +# ----------------------------------------------------------------------- def build_distribution(manifest, distrootdir, ea64, nukeold): """ Create a distibution to a directory and a ZIP file """ # (Re)create the output directory @@ -307,7 +333,17 @@ def build_distribution(manifest, distrootdir, ea64, nukeold): zip.close() -def build_plugin(platform, idasdkdir, plugin_name, ea64): +# ----------------------------------------------------------------------- +def build_plugin( + platform, + idasdkdir, + plugin_name, + options): + + # Get the arguments + ea64 = options[S_EA64] + with_hexrays = options[S_WITH_HEXRAYS] + global SWIG_OPTIONS """ Build the plugin from the SWIG wrapper and plugin main source """ # Path to the IDA SDK headers @@ -334,7 +370,8 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): ida_lib = "ida.lib" SWIG_OPTIONS += " -D__NT__ " extra_link_parameters = "" - builder.compiler_parameters += " -Ox" + if not options[S_NO_OPT]: + builder.compiler_parameters += " -Ox" # Platform-specific settings for the Mac OS X build elif platform == "macosx": builder = GCCBuilder() @@ -353,6 +390,11 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): if ea64: platform_macros.append("__EA64__") + # Build with Hex-Rays decompiler + if with_hexrays: + platform_macros.append("WITH_HEXRAYS") + SWIG_OPTIONS += ' -DWITH_HEXRAYS ' + platform_macros.append("NDEBUG") if not '--no-early-load' in sys.argv: @@ -388,6 +430,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): extra_link_parameters) assert res == 0, "Failed to link the plugin binary" +# ----------------------------------------------------------------------- def detect_platform(ea64): # Detect the platform system = platform.system() @@ -410,7 +453,9 @@ def detect_platform(ea64): return (system, platform_string, plugin_name) -def build_binary_package(ea64, nukeold): +# ----------------------------------------------------------------------- +def build_binary_package(options, nukeold): + ea64 = options[S_EA64] system, platform_string, plugin_name = detect_platform(ea64) BINDISTDIR = "idapython-%d.%d.%d_ida%d.%d_py%d.%d_%s" % (VERSION_MAJOR, VERSION_MINOR, @@ -421,7 +466,7 @@ def build_binary_package(ea64, nukeold): PYTHON_MINOR_VERSION, platform_string) # Build the plugin - build_plugin(platform_string, IDA_SDK, plugin_name, ea64) + build_plugin(platform_string, IDA_SDK, plugin_name, options) # Build the binary distribution binmanifest = [] @@ -436,6 +481,7 @@ def build_binary_package(ea64, nukeold): build_distribution(binmanifest, BINDISTDIR, ea64, nukeold) +# ----------------------------------------------------------------------- def build_source_package(): """ Build a directory and a ZIP file with all the sources """ SRCDISTDIR = "idapython-%d.%d.%d" % (VERSION_MAJOR, @@ -448,6 +494,7 @@ def build_source_package(): srcmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py"]) build_distribution(srcmanifest, SRCDISTDIR, ea64=False, nukeold=True) +# ----------------------------------------------------------------------- def gen_docs(z = False): print "Generating documentation....." old_dir = os.getcwd() @@ -494,6 +541,7 @@ def gen_docs(z = False): os.chdir(old_dir) return +# ----------------------------------------------------------------------- def usage(): print """IDAPython build script. @@ -504,23 +552,36 @@ def usage(): Used with '--doc' switch. It will compress the generated documentation --ea64: Builds also the 64bit version of the plugin + --with-hexrays: + Build with the Hex-Rays Decompiler wrappings --no-early-load: The plugin will be compiled as normal plugin This switch disables processor, plugin and loader scripts """ +# ----------------------------------------------------------------------- def main(): if '--help' in sys.argv: return usage() elif '--doc' in sys.argv: return gen_docs(z = '--zip' in sys.argv) - # Do 64-bit build? - ea64 = '--ea64' in sys.argv - build_binary_package(ea64=False, nukeold=True) + # Parse options + options = parse_options(sys.argv) + ea64 = options[S_EA64] + + # Always build the non __EA64__ version + options[S_EA64] = False + build_binary_package(options, nukeold=True) + + # Rebuild package with __EA64__ if needed if ea64: - build_binary_package(ea64=True, nukeold=False) + options[S_EA64] = True + build_binary_package(options, nukeold=False) + + # Always build the source package build_source_package() +# ----------------------------------------------------------------------- if __name__ == "__main__": main() diff --git a/examples/vds1.py b/examples/vds1.py new file mode 100644 index 00000000..c259c287 --- /dev/null +++ b/examples/vds1.py @@ -0,0 +1,27 @@ +import idaapi + +def main(): + if not idaapi.init_hexrays_plugin(): + return False + + print "Hex-rays version %s has been detected" % idaapi.get_hexrays_version() + + f = idaapi.get_func(idaapi.get_screen_ea()); + if f is None: + print "Please position the cursor within a function" + return True + + cfunc = idaapi.decompile(f); + if cfunc is None: + print "Failed to decompile!" + return True + + sv = cfunc.get_pseudocode(); + for i in xrange(0, sv.size()): + line = idaapi.tag_remove(str(sv[i])); + print line + + return True + +if main(): + idaapi.term_hexrays_plugin(); diff --git a/idapython.vcxproj b/idapython.vcxproj index 06f6c71b..854ee02d 100644 --- a/idapython.vcxproj +++ b/idapython.vcxproj @@ -97,7 +97,7 @@ Disabled .\pywraps;..\..\include;\python27\include;%(AdditionalIncludeDirectories) - NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=3;PLUGINFIX;%(PreprocessorDefinitions) + WITH_HEXRAYS;NO_OBSOLETE_FUNCS;_DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;USE_STANDARD_FILE_FUNCTIONS;VER_MAJOR=1;VER_MINOR=5;VER_PATCH=3;PLUGINFIX;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug @@ -302,6 +302,7 @@ + @@ -346,6 +347,7 @@ + diff --git a/idapython.vcxproj.filters b/idapython.vcxproj.filters index f917cf9c..881971af 100644 --- a/idapython.vcxproj.filters +++ b/idapython.vcxproj.filters @@ -293,6 +293,12 @@ TEXT + + swig_i + + + py + diff --git a/python.cpp b/python.cpp index ae53f12e..eae5160c 100644 --- a/python.cpp +++ b/python.cpp @@ -1,1591 +1,1610 @@ -//--------------------------------------------------------------------- -// IDAPython - Python plugin for Interactive Disassembler -// -// Copyright (c) The IDAPython Team -// -// All rights reserved. -// -// For detailed copyright information see the file COPYING in -// the root of the distribution archive. -//--------------------------------------------------------------------- -// python.cpp - Main plugin code -//--------------------------------------------------------------------- -#include - -//------------------------------------------------------------------------- -// This define fixes the redefinition of ssize_t -#ifdef HAVE_SSIZE_T -#define _SSIZE_T_DEFINED 1 -#endif - -#ifdef __LINUX__ -#include -#endif -#ifdef __MAC__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include "pywraps.hpp" - -//------------------------------------------------------------------------- -// Defines and constants - -// Python-style version tuple comes from the makefile -// Only the serial and status is set here -#define VER_SERIAL 0 -#define VER_STATUS "final" -#define IDAPYTHON_RUNSTATEMENT 0 -#define IDAPYTHON_ENABLE_EXTLANG 3 -#define IDAPYTHON_DISABLE_EXTLANG 4 -#define PYTHON_DIR_NAME "python" -#define S_IDAPYTHON "IDAPython" -#define S_INIT_PY "init.py" -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_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; - -#ifdef PLUGINFIX - #define PLUGIN_FLAGS PLUGIN_FIX -#else - #define PLUGIN_FLAGS 0 -#endif - -//------------------------------------------------------------------------- -// Types - -// -enum script_run_when -{ - run_on_db_open = 0, // run script after opening database (default) - run_on_ui_ready = 1, // run script when UI is ready - run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) -}; - -//------------------------------------------------------------------------- -// Global variables -static bool g_initialized = false; -static int g_run_when = -1; -static char g_run_script[QMAXPATH]; -static char g_idapython_dir[QMAXPATH]; - -//------------------------------------------------------------------------- -// Prototypes and forward declarations - -// Alias to SWIG_Init -//lint -esym(526,init_idaapi) not defined -extern "C" void init_idaapi(void); - -// Plugin run() callback -void idaapi run(int arg); - -//------------------------------------------------------------------------- -// This is a simple tracing code for debugging purposes. -// It might evolve into a tracing facility for user scripts. - -//#define ENABLE_PYTHON_PROFILING -#ifdef ENABLE_PYTHON_PROFILING -#include "compile.h" -#include "frameobject.h" - -int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - PyObject *str; - - /* Catch line change events. */ - /* Print the filename and line number */ - if ( what == PyTrace_LINE ) - { - str = PyObject_Str(frame->f_code->co_filename); - if ( str ) - { - msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); - Py_DECREF(str); - } - } - return 0; -} -#endif - -//------------------------------------------------------------------------- -// Helper routines to make Python script execution breakable from IDA -static int ninsns = 0; // number of times trace function was called -static bool box_displayed; // has the wait box been displayed? -static time_t start_time; // the start time of the execution -static int script_timeout = 2; -static bool g_ui_ready = false; -static bool g_alert_auto_scripts = true; -static bool g_remove_cwd_sys_path = false; -static bool g_use_local_python = false; - -static void end_execution(void); -static void begin_execution(void); - -//------------------------------------------------------------------------ -// This callback is called on various interpreter events -static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - if ( wasBreak() ) - { - // User pressed Cancel in the waitbox; send KeyboardInterrupt exception - PyErr_SetInterrupt(); - } - else if ( !box_displayed && ++ninsns > 10 ) - { - // We check the timer once every 10 calls - ninsns = 0; - - // Timeout disabled or elapsed? - if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) - { - box_displayed = true; - show_wait_box("Running Python script"); - } - } -#ifdef ENABLE_PYTHON_PROFILING - return tracefunc(obj, frame, what, arg); -#else - qnotused(obj); - qnotused(frame); - qnotused(what); - qnotused(arg); - return 0; -#endif -} - -//------------------------------------------------------------------------ -static void reset_execution_time() -{ - start_time = time(NULL); - ninsns = 0; -} - -//------------------------------------------------------------------------ -// Prepare for Python execution -static void begin_execution() -{ - if ( !g_ui_ready || script_timeout == 0 ) - return; - - end_execution(); - reset_execution_time(); - PyEval_SetTrace(break_check, NULL); -} - -//--------------------------------------------------------------------------- -static void hide_script_waitbox() -{ - if ( box_displayed ) - { - hide_wait_box(); - box_displayed = false; - } -} - -//------------------------------------------------------------------------ -// Called after Python execution finishes -static void end_execution() -{ - hide_script_waitbox(); -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#else - PyEval_SetTrace(NULL, NULL); -#endif -} - -//------------------------------------------------------------------------- -//lint -esym(714,disable_script_timeout) Symbol not referenced -void disable_script_timeout() -{ - // Clear timeout - script_timeout = 0; - - // Uninstall the trace function and hide the waitbox (if it was shown) - end_execution(); -} - -//------------------------------------------------------------------------- -//lint -esym(714,set_script_timeout) Symbol not referenced -int set_script_timeout(int timeout) -{ - // Update the timeout - qswap(timeout, script_timeout); - - // Reset the execution time and hide the waitbox (so it is shown again after timeout elapses) - reset_execution_time(); - hide_script_waitbox(); - - return timeout; -} - -//------------------------------------------------------------------------ -// Return a formatted error or just print it to the console -static void handle_python_error( - char *errbuf, - size_t errbufsize, - bool clear_error = true) -{ - if ( errbufsize > 0 ) - errbuf[0] = '\0'; - - // No exception? - if ( !PyErr_Occurred() ) - return; - - qstring s; - if ( PyW_GetError(&s, clear_error) ) - qstrncpy(errbuf, s.c_str(), errbufsize); -} - -//------------------------------------------------------------------------ -// Helper function to get globals for the __main__ module -// Note: The references are borrowed. No need to free them. -static PyObject *GetMainGlobals() -{ - PyObject *module = PyImport_AddModule(S_MAIN); - return module == NULL ? NULL : 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(); - PYW_GIL_ENSURE; - PyObject *py_result = PyEval_EvalCode( - (PyCodeObject *) py_code, - py_globals, - py_globals); - PYW_GIL_RELEASE; - 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); -} - -//------------------------------------------------------------------------ -// Executes a simple string -static bool idaapi IDAPython_extlang_run_statements( - const char *str, - char *errbuf, - size_t errbufsize) -{ - PyObject *globals = GetMainGlobals(); - bool ok; - if ( globals == NULL ) - { - ok = false; - } - else - { - errbuf[0] = '\0'; - PyErr_Clear(); - begin_execution(); - PYW_GIL_ENSURE; - PyObject *result = PyRun_String( - str, - Py_file_input, - globals, - globals); - PYW_GIL_RELEASE; - Py_XDECREF(result); - end_execution(); - - ok = result != NULL && !PyErr_Occurred(); - - if ( !ok ) - handle_python_error(errbuf, errbufsize); - } - if ( !ok && errbuf[0] == '\0' ) - qstrncpy(errbuf, "internal error", errbufsize); - return ok; -} - -//------------------------------------------------------------------------ -// Simple Python statement runner function for IDC -static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; -static error_t idaapi idc_runpythonstatement( - idc_value_t *argv, - idc_value_t *res) -{ - char errbuf[MAXSTR]; - bool ok = IDAPython_extlang_run_statements(argv[0].c_str(), errbuf, sizeof(errbuf)); - - if ( ok ) - res->set_long(0); - else - res->set_string(errbuf); - - return eOk; -} - -//-------------------------------------------------------------------------- -const char *idaapi set_python_options( - const char *keyword, - int value_type, - const void *value) -{ - do - { - if ( value_type == IDPOPT_NUM ) - { - if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 ) - { - script_timeout = int(*(uval_t *)value); - break; - } - else if ( qstrcmp(keyword, "ALERT_AUTO_SCRIPTS") == 0 ) - { - g_alert_auto_scripts = *(uval_t *)value != 0; - break; - } - else if ( qstrcmp(keyword, "REMOVE_CWD_SYS_PATH") == 0 ) - { - g_remove_cwd_sys_path = *(uval_t *)value != 0; - break; - } - else if ( qstrcmp(keyword, "USE_LOCAL_PYTHON") == 0 ) - { - g_use_local_python = *(uval_t *)value != 0; - break; - } - } - return IDPOPT_BADKEY; - } while (false); - return IDPOPT_OK; -} - -//------------------------------------------------------------------------- -// Check for the presence of a file in IDADIR/python and complain on error -bool CheckScriptFiles() -{ - static const char *const script_files[] = - { - S_IDC_MODNAME ".py", - S_INIT_PY, - "idaapi.py", - "idautils.py" - }; - for ( size_t i=0; i 0 ) - errbuf[0] = '\0'; - - if ( py_result == NULL ) - { - handle_python_error(errbuf, errbufsize); - return false; - } - - int cvt = CIP_OK; - if ( idc_result != NULL ) - { - idc_result->clear(); - cvt = pyvar_to_idcvar(py_result, idc_result); - if ( cvt < CIP_OK ) - qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); - } - - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_result); - - return cvt >= CIP_OK; -} - -//------------------------------------------------------------------------- -// Compile callback for Python external language evaluator -bool idaapi IDAPython_extlang_compile( - const char *name, - ea_t /*current_ea*/, - const char *expr, - char *errbuf, - size_t errbufsize) -{ - PyObject *globals = GetMainGlobals(); - - PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); - if ( code == NULL ) - { - handle_python_error(errbuf, errbufsize); - return false; - } - - // Set the desired function name - Py_XDECREF(code->co_name); - code->co_name = PyString_FromString(name); - - // Create a function out of code - PyObject *func = PyFunction_New((PyObject *)code, globals); - - if ( func == NULL ) - { -ERR: - handle_python_error(errbuf, errbufsize); - Py_XDECREF(code); - return false; - } - - int err = PyDict_SetItemString(globals, name, func); - Py_XDECREF(func); - if ( err ) - goto ERR; - - return true; -} - -//------------------------------------------------------------------------- -// Run callback for Python external language evaluator -bool idaapi IDAPython_extlang_run( - const char *name, - int nargs, - const idc_value_t args[], - idc_value_t *result, - char *errbuf, - size_t errbufsize) -{ - // Try to extract module name (if any) from the funcname - char modname[MAXSTR] = {0}; - char funcname[MAXSTR] = {0}; - bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); - - ppyobject_vec_t pargs; - boolvec_t decref; - bool ok = true; - PyObject *module = NULL; - do - { - // Convert arguments to python - ok = pyw_convert_idc_args(args, nargs, pargs, &decref, errbuf, errbufsize); - if ( !ok ) - break; - - if ( imported_module ) - { - PYW_GIL_ENSURE; - module = PyImport_ImportModule(modname); - PYW_GIL_RELEASE; - } - else - { - module = PyImport_AddModule(S_MAIN); - QASSERT(30156, module != NULL); - } - - PyObject *globals = PyModule_GetDict(module); - QASSERT(30157, globals != NULL); - - PyObject *func = PyDict_GetItemString(globals, funcname); - if ( func == NULL ) - { - qsnprintf(errbuf, errbufsize, "undefined function %s", name); - ok = false; - break; - } - - PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func); - PYW_GIL_ENSURE; - PyObject *pres = PyEval_EvalCodeEx( - code, - globals, NULL, - &pargs[0], nargs, - NULL, 0, NULL, 0, NULL); - PYW_GIL_RELEASE; - ok = return_python_result(result, pres, errbuf, errbufsize); - } while ( false ); - - pyw_free_idc_args(pargs, &decref); - - if ( imported_module ) - Py_XDECREF(module); - - return ok; -} - -//------------------------------------------------------------------------- -// Compile callback for Python external language evaluator -bool idaapi IDAPython_extlang_compile_file( - const char *filename, - char *errbuf, - size_t errbufsize) -{ - begin_execution(); - bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); - end_execution(); - return ok; -} - -//------------------------------------------------------------------------- -// Create an object instance -bool idaapi IDAPython_extlang_create_object( - const char *name, // in: object class name - int nargs, // in: number of input arguments - const idc_value_t args[], // in: input arguments - idc_value_t *result, // out: created object or exception - char *errbuf, // out: error message if evaluation fails - size_t errbufsize) // in: size of the error buffer -{ - PyObject *py_mod(NULL), *py_cls(NULL); - ppyobject_vec_t pargs; - - bool ok = false; - do - { - // Parse the object name (to get the module and class name) - char modname[MAXSTR] = {0}; - char clsname[MAXSTR] = {0}; - parse_py_modname(name, modname, clsname, MAXSTR); - - // Get a reference to the module - py_mod = PyW_TryImportModule(modname); - if ( py_mod == NULL ) - { - qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); - break; - } - - // Get the class reference - py_cls = PyW_TryGetAttrString(py_mod, clsname); - if ( py_cls == NULL ) - { - qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); - break; - } - - // Error during conversion? - ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); - if ( !ok ) - break; - - // Call the constructor - PYW_GIL_ENSURE; - PyObject *py_res = PyObject_CallObject(py_cls, pargs.empty() ? NULL : pargs[0]); - PYW_GIL_RELEASE; - ok = return_python_result(result, py_res, errbuf, errbufsize); - } while ( false ); - - Py_XDECREF(py_mod); - Py_XDECREF(py_cls); - - // Free the arguments tuple - pyw_free_idc_args(pargs); - return ok; -} - -//------------------------------------------------------------------------- -// Returns the attribute value of a given object from the global scope -bool idaapi IDAPython_extlang_get_attr( - const idc_value_t *obj, // in: object (may be NULL) - const char *attr, // in: attribute name - idc_value_t *result) -{ - PyObject *py_mod(NULL), *py_obj(NULL); - bool is_opaque_obj = false; - int cvt = CIP_FAILED; - do - { - // Get a reference to the module - py_mod = PyW_TryImportModule(S_MAIN); - if ( py_mod == NULL ) - break; - - // Object specified: - // - (1) string contain attribute name in the main module - // - (2) opaque object (we use it as is) - if ( obj != NULL ) - { - // (1) Get attribute from main module - if ( obj->vtype == VT_STR2 ) - py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); - // (2) see if opaque object - else - { - // Convert object (expecting opaque object) - cvt = idcvar_to_pyvar(*obj, &py_obj); - // Only opaque objects are accepted - if ( cvt != CIP_OK_NODECREF ) - { - Py_XDECREF(py_obj); - py_obj = NULL; - cvt = CIP_FAILED; - break; - } - is_opaque_obj = true; - } - // Get the attribute reference - if ( py_obj == NULL ) - break; - } - // No object specified: - else - { - // ...then work with main module - py_obj = py_mod; - } - // Special case: if attribute not passed then retrieve the class - // name associated associated with the passed object - if ( attr == NULL || attr[0] == '\0' ) - { - cvt = CIP_FAILED; - // Get the class - PyObject *cls = PyObject_GetAttrString(py_obj, "__class__"); - if ( cls == NULL ) - break; - - // Get its name - PyObject *name = PyObject_GetAttrString(cls, "__name__"); - Py_DECREF(cls); - if ( name == NULL ) - break; - - // Convert name object to string object - PyObject *string = PyObject_Str(name); - Py_DECREF(name); - if ( string == NULL ) - break; - - // Convert name python string to a C string - const char *clsname = PyString_AsString(name); - if ( clsname == NULL ) - break; - - result->set_string(clsname); - cvt = CIP_OK; //lint !e838 - break; - } - - PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr); - // No attribute? - if ( py_attr == NULL ) - { - cvt = CIP_FAILED; - break; - } - // Don't store result - if ( result == NULL ) - { - cvt = CIP_OK; - // Decrement attribute (because of GetAttrString) - Py_DECREF(py_attr); - } - else - { - cvt = pyvar_to_idcvar(py_attr, result); - // Conversion succeeded and opaque object was passed: - // Since the object will be passed to IDC, it is likely that IDC value will be - // destroyed and also destroying the opaque object with it. That is an undesired effect. - // We increment the reference of the object so that even if the IDC value dies - // the opaque object remains. So by not decrement reference after GetAttrString() call - // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. - if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) - { - // Decrement the reference (that was incremented by GetAttrString()) - Py_DECREF(py_attr); - } - } - } while ( false ); - - // Free main module reference - Py_XDECREF(py_mod); - - // Wasn't working with main module? - if ( obj != NULL && !is_opaque_obj ) - Py_XDECREF(py_obj); - - return cvt >= CIP_OK; -} - -//------------------------------------------------------------------------- -// Returns the attribute value of a given object from the global scope -//lint -e{818} -bool idaapi IDAPython_extlang_set_attr( - idc_value_t *obj, // in: object name (may be NULL) - const char *attr, // in: attribute name - idc_value_t *value) -{ - PyObject *py_mod(NULL), *py_obj(NULL); - bool ok = false, is_opaque_obj = false; - do - { - // Get a reference to the module - py_mod = PyW_TryImportModule(S_MAIN); - if ( py_mod == NULL ) - break; - - if ( obj != NULL ) - { - // Get the attribute reference (from just a name) - if ( obj->vtype == VT_STR2 ) - py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); - else - { - int cvt = idcvar_to_pyvar(*obj, &py_obj); - // Only opaque objects are accepted - if ( cvt != CIP_OK_NODECREF ) - { - Py_XDECREF(py_obj); - py_obj = NULL; - } - else - is_opaque_obj = true; - } - // No object to set_attr on? - if ( py_obj == NULL ) - break; - } - else - { - // set_attr on the main module - py_obj = py_mod; - } - // Convert the value - PyObject *py_var(NULL); - int cvt = idcvar_to_pyvar(*value, &py_var); - if ( cvt >= CIP_OK ) - { - ok = PyObject_SetAttrString(py_obj, attr, py_var) != -1; - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_var); - } - } while ( false ); - - Py_XDECREF(py_mod); - - if ( obj != NULL && !is_opaque_obj ) - Py_XDECREF(py_obj); - - return ok; -} - -//------------------------------------------------------------------------- -// Calculator callback for Python external language evaluator -//lint -e{818} -bool idaapi IDAPython_extlang_calcexpr( - ea_t /*current_ea*/, - const char *expr, - idc_value_t *rv, - char *errbuf, - size_t errbufsize) -{ - PyObject *globals = GetMainGlobals(); - if ( globals == NULL ) - return false; - - begin_execution(); - PYW_GIL_ENSURE; - PyObject *result = PyRun_String(expr, Py_eval_input, globals, globals); - PYW_GIL_RELEASE; - end_execution(); - - return return_python_result(rv, result, errbuf, errbufsize); -} - -//------------------------------------------------------------------------- -bool idaapi IDAPython_extlang_call_method( - const idc_value_t *idc_obj, - const char *method_name, - int nargs, - const idc_value_t args[], - idc_value_t *result, - char *errbuf, - size_t errbufsize) -{ - // Check for unsupported usage of call_method. - // Mainly a method call requires an object and a method. - if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) - { - qstrncpy(errbuf, "call_method does not support this operation", errbufsize); - return false; - } - // Behave like run() - else if ( idc_obj == NULL && method_name != NULL ) - return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); - - PyObject *py_obj(NULL), *py_method(NULL); - // Holds conversion status of input object - int obj_cvt; - ppyobject_vec_t pargs; - bool ok = false; - do - { - // Convert input object - obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); - if ( obj_cvt < CIP_OK ) - { - qstrncpy(errbuf, "Failed to convert input object to Python value", errbufsize); - break; - } - - py_method = PyW_TryGetAttrString(py_obj, method_name); - if ( py_method == NULL || !PyCallable_Check(py_method) ) - { - qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); - break; - } - - // Convert arguments to python objects - ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); - if ( !ok ) - break; - - PYW_GIL_ENSURE; - PyObject *py_res = PyObject_CallObject(py_method, pargs.empty() ? NULL : pargs[0]); - PYW_GIL_RELEASE; - ok = return_python_result(result, py_res, errbuf, errbufsize); - } while ( false ); - - // Free converted args - pyw_free_idc_args(pargs); - - // Release reference of object if needed - if ( obj_cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_obj); - - Py_XDECREF(py_method); - return ok; -} - -//------------------------------------------------------------------------- -const extlang_t extlang_python = -{ - sizeof(extlang_t), - 0, - "Python", - IDAPython_extlang_compile, - IDAPython_extlang_run, - IDAPython_extlang_calcexpr, - IDAPython_extlang_compile_file, - "py", - IDAPython_extlang_create_object, - IDAPython_extlang_get_attr, - IDAPython_extlang_set_attr, - IDAPython_extlang_call_method, - IDAPython_extlang_run_statements, -}; - -//------------------------------------------------------------------------- -void enable_extlang_python(bool enable) -{ - if ( enable ) - select_extlang(&extlang_python); - else - select_extlang(NULL); -} - -//------------------------------------------------------------------------- -// Execute a line in the Python CLI -bool idaapi IDAPython_cli_execute_line(const char *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 - last_line += 1; - - // Skip empty lines - if ( last_line[0] != '\0' ) - { - // Line ends with ":" or begins with a space character? - bool more = last_line[qstrlen(last_line)-1] == ':' || qisspace(last_line[0]); - if ( more ) - return false; - } - - // - // Pseudo commands - // - qstring s; - do - { - // Help command? - if ( line[0] == '?' ) - s.sprnt("help(%s)", line+1); - // Shell command? - else if ( line[0] == '!' ) - s.sprnt("idaapi.IDAPython_ExecSystem(r'%s')", line+1); - else - break; - // Patch the command line pointer - line = s.c_str(); - } while (false); - - begin_execution(); - PythonEvalOrExec(line); - end_execution(); - - return true; -} - -//------------------------------------------------------------------------- -bool idaapi IDAPYthon_cli_complete_line( - qstring *completion, - const char *prefix, - int n, - const char *line, - int x) -{ - PyObject *py_complete = get_idaapi_attr(S_IDAAPI_COMPLETION); - if ( py_complete == NULL ) - return false; - - PYW_GIL_ENSURE; - PyObject *py_ret = PyObject_CallFunction(py_complete, "sisi", prefix, n, line, x); //lint !e1776 - PYW_GIL_RELEASE; - - Py_DECREF(py_complete); - - // Swallow the error - PyW_GetError(completion); - - bool ok = py_ret != NULL && PyString_Check(py_ret) != 0; - - if ( ok ) - *completion = PyString_AS_STRING(py_ret); - - Py_XDECREF(py_ret); - - return ok; -} - -//------------------------------------------------------------------------- -static const cli_t cli_python = -{ - sizeof(cli_t), - 0, - "Python", - "Python - IDAPython plugin", - "Enter any Python expression", - IDAPython_cli_execute_line, - IDAPYthon_cli_complete_line, - NULL -}; - -//------------------------------------------------------------------------- -// Control the Python CLI status -void enable_python_cli(bool enable) -{ - if ( enable ) - install_command_interpreter(&cli_python); - else - remove_command_interpreter(&cli_python); -} - -//------------------------------------------------------------------------- -// Prints the IDAPython copyright banner -void py_print_banner() -{ - PYW_GIL_ENSURE; - PyRun_SimpleString("print_banner()"); - PYW_GIL_RELEASE; -} - -//------------------------------------------------------------------------ -// Parse plugin options -void parse_plugin_options() -{ - // Get options from IDA - const char *options = get_plugin_options(S_IDAPYTHON); - - // No options? - if ( options == NULL ) - return; - - // User specified 'when' parameter? - const char *p = strchr(options, ';'); - if ( p == NULL ) - { - g_run_when = run_on_db_open; - p = options; - } - else - { - g_run_when = atoi(options); - ++p; - } - qstrncpy(g_run_script, p, sizeof(g_run_script)); -} - -//------------------------------------------------------------------------ -// Converts the global IDC variable "ARGV" into a Python variable. -// The arguments will then be accessible via 'idc' module / 'ARGV' variable. -void convert_idc_args() -{ - PyObject *py_args = PyList_New(0); - - idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); - if ( idc_args != NULL ) - { - idc_value_t attr; - char attr_name[20] = {"0"}; - for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) - { - PyList_Insert(py_args, i, PyString_FromString(attr.c_str())); - qsnprintf(attr_name, sizeof(attr_name), "%d", i); - } - } - - // Get reference to the IDC module (it is imported by init.py) - PyObject *py_mod = PyW_TryImportModule(S_IDC_MODNAME); - if ( py_mod != NULL ) - PyObject_SetAttrString(py_mod, S_IDC_ARGS_VARNAME, py_args); - - Py_DECREF(py_args); - Py_XDECREF(py_mod); -} - -//------------------------------------------------------------------------ -// We install the menu later because the text version crashes if -// add_menu_item is called too early -static int idaapi menu_installer_cb(void *, int code, va_list) -{ - switch ( code ) - { - case ui_ready_to_run: - g_ui_ready = true; - py_print_banner(); - - if ( g_run_when == run_on_ui_ready ) - RunScript(g_run_script); - break; - - case ui_database_inited: - { - convert_idc_args(); - if ( g_run_when == run_on_db_open ) - RunScript(g_run_script); - break; - } - default: - break; - } - return 0; -} - -//------------------------------------------------------------------------- -// remove current directory (empty entry) from the sys.path -static void sanitize_path() -{ - char buf[QMAXPATH]; - qstrncpy(buf, Py_GetPath(), sizeof(buf)); - char *ctx; - qstring newpath; - for ( char *d0 = qstrtok(buf, DELIMITER, &ctx); - d0 != NULL; - d0 = qstrtok(NULL, DELIMITER, &ctx) ) - { - if ( d0[0] == '\0' ) - // skip empty entry - continue; - - if ( !newpath.empty() ) - newpath.append(DELIMITER); - newpath.append(d0); - } - PySys_SetPath(newpath.begin()); -} - - -//------------------------------------------------------------------------- -// we have to do it ourselves because Python 2.7 calls exit() if importing site fails -static bool initsite(void) -{ - PyObject *m; - m = PyImport_ImportModule("site"); - if (m == NULL) - { - PyErr_Print(); - Py_Finalize(); - return false; - } - else { - Py_DECREF(m); - } - return true; -} - -//------------------------------------------------------------------------- -// Initialize the Python environment -bool IDAPython_Init(void) -{ - // Already initialized? - if ( g_initialized ) - return true; - - // Form the absolute path to IDA\python folder - qstrncpy(g_idapython_dir, idadir(PYTHON_DIR_NAME), sizeof(g_idapython_dir)); - - // Check for the presence of essential files - if ( !CheckScriptFiles() ) - return false; - - char tmp[QMAXPATH]; -#ifdef __LINUX__ - // Export symbols from libpython to resolve imported module deps - // use the standard soname: libpython2.7.so.1.0 -#define PYLIB "libpython" QSTRINGIZE(PY_MAJOR_VERSION) "." QSTRINGIZE(PY_MINOR_VERSION) ".so.1.0" - if ( !dlopen(PYLIB, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) - { - warning("IDAPython dlopen(" PYLIB ") error: %s", dlerror()); - return false; - } -#endif - -#ifdef __MAC__ - // We should set python home to the module's path, otherwise it can pick up stray modules from $PATH - NSModule pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); - // Use dylib functions to find out where the framework was loaded from - const char *buf = (char *)NSLibraryNameForModule(pythonModule); - if ( buf != NULL ) - { - // The path will be something like: - // /System/Library/Frameworks/Python.framework/Versions/2.5/Python - // We need to strip the last part - // use static buffer because Py_SetPythonHome() just stores a pointer - static char pyhomepath[MAXSTR]; - qstrncpy(pyhomepath, buf, MAXSTR); - char * lastslash = strrchr(pyhomepath, '/'); - if ( lastslash != NULL ) - { - *lastslash = 0; - Py_SetPythonHome(pyhomepath); - } - } -#endif - - // Read configuration value - read_user_config_file("python.cfg", set_python_options, NULL); - if ( g_alert_auto_scripts ) - { - if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) - && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" - "Do you want to continue loading IDAPython?", tmp) <= 0 ) - { - return false; - } - } - - if ( g_use_local_python ) - Py_SetPythonHome(g_idapython_dir); - - // don't import "site" right now - Py_NoSiteFlag = 1; - - // Start the interpreter - Py_Initialize(); - - if ( !Py_IsInitialized() ) - { - warning("IDAPython: Py_Initialize() failed"); - return false; - } - - // remove current directory - sanitize_path(); - - // import "site" - if ( !g_use_local_python && !initsite() ) - { - warning("IDAPython: importing \"site\" failed"); - return false; - } - - // Enable multi-threading support - if ( !PyEval_ThreadsInitialized() ) - PyEval_InitThreads(); - - // Init the SWIG wrapper - init_idaapi(); - - // Set IDAPYTHON_VERSION in Python - qsnprintf(tmp, sizeof(tmp), - "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" - "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", - VER_MAJOR, - VER_MINOR, - VER_PATCH, - VER_STATUS, - VER_SERIAL, - g_remove_cwd_sys_path ? "True" : "False"); - PyRun_SimpleString(tmp); - - // Install extlang. Needs to be done before running init.py - // in case it's calling idaapi.enable_extlang_python(1) - install_extlang(&extlang_python); - - // Execute init.py (for Python side initialization) - qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL); - if ( !PyRunFile(tmp) ) - { - // Try to fetch a one line error string. We must do it before printing - // the traceback information. Make sure that the exception is not cleared - handle_python_error(tmp, sizeof(tmp), false); - - // Print the exception traceback - PyRun_SimpleString("import traceback;traceback.print_exc();"); - - warning("IDAPython: error executing " S_INIT_PY ":\n" - "%s\n" - "\n" - "Refer to the message window to see the full error log.", tmp); - return false; - } - - // Init pywraps and notify_when - if ( !init_pywraps() || !pywraps_nw_init() ) - { - warning("IDAPython: init_pywraps() failed!"); - return false; - } - -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#endif - - // Batch-mode operation: - parse_plugin_options(); - - // Register a RunPythonStatement() function for IDC - set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, idc_runpythonstatement, idc_runpythonstatement_args, 0); - - // A script specified on the command line is run - if ( g_run_when == run_on_init ) - RunScript(g_run_script); - -#ifdef PLUGINFIX - hook_to_notification_point(HT_UI, menu_installer_cb, NULL); -#else - install_python_menus(); - py_print_banner(); -#endif - - // Enable the CLI by default - enable_python_cli(true); - - g_initialized = true; - pywraps_nw_notify(NW_INITIDA_SLOT); - return true; -} - -//------------------------------------------------------------------------- -// Cleaning up Python -void IDAPython_Term(void) -{ -#ifdef PLUGINFIX - unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); -#endif - /* Remove the menu items before termination */ - del_menu_item("File/Python command..."); - - // Notify about IDA closing - pywraps_nw_notify(NW_TERMIDA_SLOT); - - // De-init notify_when - pywraps_nw_term(); - - // Remove the CLI - enable_python_cli(false); - - // Remove the extlang - remove_extlang(&extlang_python); - - // De-init pywraps - deinit_pywraps(); - - // Uninstall IDC function - set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL, 0); - - // Shut the interpreter down - Py_Finalize(); - - g_initialized = false; -} - -//------------------------------------------------------------------------- -// Plugin init routine -int idaapi init(void) -{ - if ( IDAPython_Init() ) - return PLUGIN_KEEP; - else - return PLUGIN_SKIP; -} - -//------------------------------------------------------------------------- -// Plugin term routine -void idaapi term(void) -{ - IDAPython_Term(); -} - -//------------------------------------------------------------------------- -// Plugin hotkey entry point -void idaapi run(int arg) -{ - try - { - switch ( arg ) - { - case IDAPYTHON_RUNSTATEMENT: - IDAPython_RunStatement(); - break; - case IDAPYTHON_ENABLE_EXTLANG: - enable_extlang_python(true); - break; - case IDAPYTHON_DISABLE_EXTLANG: - enable_extlang_python(false); - break; - default: - warning("IDAPython: unknown plugin argument %d", arg); - break; - } - } - catch(...) - { - warning("Exception in Python interpreter. Reloading..."); - IDAPython_Term(); - IDAPython_Init(); - } -} - -//------------------------------------------------------------------------- -// PLUGIN DESCRIPTION BLOCK -//------------------------------------------------------------------------- -plugin_t PLUGIN = -{ - IDP_INTERFACE_VERSION, - PLUGIN_FLAGS | PLUGIN_HIDE, // plugin flags - init, // initialize - term, // terminate. this pointer may be NULL. - run, // invoke plugin - S_IDAPYTHON, // long comment about the plugin - // it could appear in the status line - // or as a hint - // multiline help about the plugin - "IDA Python Plugin\n", - // the preferred short name of the plugin - S_IDAPYTHON, - // the preferred hotkey to run the plugin - NULL -}; +//--------------------------------------------------------------------- +// IDAPython - Python plugin for Interactive Disassembler +// +// Copyright (c) The IDAPython Team +// +// All rights reserved. +// +// For detailed copyright information see the file COPYING in +// the root of the distribution archive. +//--------------------------------------------------------------------- +// python.cpp - Main plugin code +//--------------------------------------------------------------------- +#include + +//------------------------------------------------------------------------- +// This define fixes the redefinition of ssize_t +#ifdef HAVE_SSIZE_T +#define _SSIZE_T_DEFINED 1 +#endif + +#ifdef __LINUX__ +#include +#endif +#ifdef __MAC__ +#include +#endif +#include +#include +#include +#include +#include +#include + +#ifdef WITH_HEXRAYS + #include + hexdsp_t *hexdsp = NULL; +#endif + +#include "pywraps.hpp" + +//------------------------------------------------------------------------- +// Defines and constants + +// Python-style version tuple comes from the makefile +// Only the serial and status is set here +#define VER_SERIAL 0 +#define VER_STATUS "final" +#define IDAPYTHON_RUNSTATEMENT 0 +#define IDAPYTHON_ENABLE_EXTLANG 3 +#define IDAPYTHON_DISABLE_EXTLANG 4 +#define PYTHON_DIR_NAME "python" +#define S_IDAPYTHON "IDAPython" +#define S_INIT_PY "init.py" +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_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; + +#ifdef PLUGINFIX + #define PLUGIN_FLAGS PLUGIN_FIX +#else + #define PLUGIN_FLAGS 0 +#endif + +//------------------------------------------------------------------------- +// Types + +// +enum script_run_when +{ + run_on_db_open = 0, // run script after opening database (default) + run_on_ui_ready = 1, // run script when UI is ready + run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) +}; + +//------------------------------------------------------------------------- +// Global variables +static bool g_initialized = false; +static int g_run_when = -1; +static char g_run_script[QMAXPATH]; +static char g_idapython_dir[QMAXPATH]; + +//------------------------------------------------------------------------- +// Prototypes and forward declarations + +// Alias to SWIG_Init +//lint -esym(526,init_idaapi) not defined +extern "C" void init_idaapi(void); + +// Plugin run() callback +void idaapi run(int arg); + +//------------------------------------------------------------------------- +// This is a simple tracing code for debugging purposes. +// It might evolve into a tracing facility for user scripts. + +//#define ENABLE_PYTHON_PROFILING +#ifdef ENABLE_PYTHON_PROFILING +#include "compile.h" +#include "frameobject.h" + +int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + PyObject *str; + + /* Catch line change events. */ + /* Print the filename and line number */ + if ( what == PyTrace_LINE ) + { + str = PyObject_Str(frame->f_code->co_filename); + if ( str ) + { + msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); + Py_DECREF(str); + } + } + return 0; +} +#endif + +//------------------------------------------------------------------------- +// Helper routines to make Python script execution breakable from IDA +static int ninsns = 0; // number of times trace function was called +static bool box_displayed; // has the wait box been displayed? +static time_t start_time; // the start time of the execution +static int script_timeout = 2; +static bool g_ui_ready = false; +static bool g_alert_auto_scripts = true; +static bool g_remove_cwd_sys_path = false; +static bool g_use_local_python = false; + +static void end_execution(void); +static void begin_execution(void); + +//------------------------------------------------------------------------ +// This callback is called on various interpreter events +static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + if ( wasBreak() ) + { + // User pressed Cancel in the waitbox; send KeyboardInterrupt exception + PyErr_SetInterrupt(); + } + else if ( !box_displayed && ++ninsns > 10 ) + { + // We check the timer once every 10 calls + ninsns = 0; + + // Timeout disabled or elapsed? + if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) + { + box_displayed = true; + show_wait_box("Running Python script"); + } + } +#ifdef ENABLE_PYTHON_PROFILING + return tracefunc(obj, frame, what, arg); +#else + qnotused(obj); + qnotused(frame); + qnotused(what); + qnotused(arg); + return 0; +#endif +} + +//------------------------------------------------------------------------ +static void reset_execution_time() +{ + start_time = time(NULL); + ninsns = 0; +} + +//------------------------------------------------------------------------ +// Prepare for Python execution +static void begin_execution() +{ + if ( !g_ui_ready || script_timeout == 0 ) + return; + + end_execution(); + reset_execution_time(); + PyEval_SetTrace(break_check, NULL); +} + +//--------------------------------------------------------------------------- +static void hide_script_waitbox() +{ + if ( box_displayed ) + { + hide_wait_box(); + box_displayed = false; + } +} + +//------------------------------------------------------------------------ +// Called after Python execution finishes +static void end_execution() +{ + hide_script_waitbox(); +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#else + PyEval_SetTrace(NULL, NULL); +#endif +} + +//------------------------------------------------------------------------- +//lint -esym(714,disable_script_timeout) Symbol not referenced +void disable_script_timeout() +{ + // Clear timeout + script_timeout = 0; + + // Uninstall the trace function and hide the waitbox (if it was shown) + end_execution(); +} + +//------------------------------------------------------------------------- +//lint -esym(714,set_script_timeout) Symbol not referenced +int set_script_timeout(int timeout) +{ + // Update the timeout + qswap(timeout, script_timeout); + + // Reset the execution time and hide the waitbox (so it is shown again after timeout elapses) + reset_execution_time(); + hide_script_waitbox(); + + return timeout; +} + +//------------------------------------------------------------------------ +// Return a formatted error or just print it to the console +static void handle_python_error( + char *errbuf, + size_t errbufsize, + bool clear_error = true) +{ + if ( errbufsize > 0 ) + errbuf[0] = '\0'; + + // No exception? + if ( !PyErr_Occurred() ) + return; + + qstring s; + if ( PyW_GetError(&s, clear_error) ) + qstrncpy(errbuf, s.c_str(), errbufsize); +} + +//------------------------------------------------------------------------ +// Helper function to get globals for the __main__ module +// Note: The references are borrowed. No need to free them. +static PyObject *GetMainGlobals() +{ + PyObject *module = PyImport_AddModule(S_MAIN); + return module == NULL ? NULL : 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(); + PYW_GIL_ENSURE; + PyObject *py_result = PyEval_EvalCode( + (PyCodeObject *) py_code, + py_globals, + py_globals); + PYW_GIL_RELEASE; + 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); +} + +//------------------------------------------------------------------------ +// Executes a simple string +static bool idaapi IDAPython_extlang_run_statements( + const char *str, + char *errbuf, + size_t errbufsize) +{ + PyObject *globals = GetMainGlobals(); + bool ok; + if ( globals == NULL ) + { + ok = false; + } + else + { + errbuf[0] = '\0'; + PyErr_Clear(); + begin_execution(); + PYW_GIL_ENSURE; + PyObject *result = PyRun_String( + str, + Py_file_input, + globals, + globals); + PYW_GIL_RELEASE; + Py_XDECREF(result); + end_execution(); + + ok = result != NULL && !PyErr_Occurred(); + + if ( !ok ) + handle_python_error(errbuf, errbufsize); + } + if ( !ok && errbuf[0] == '\0' ) + qstrncpy(errbuf, "internal error", errbufsize); + return ok; +} + +//------------------------------------------------------------------------ +// Simple Python statement runner function for IDC +static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; +static error_t idaapi idc_runpythonstatement( + idc_value_t *argv, + idc_value_t *res) +{ + char errbuf[MAXSTR]; + bool ok = IDAPython_extlang_run_statements(argv[0].c_str(), errbuf, sizeof(errbuf)); + + if ( ok ) + res->set_long(0); + else + res->set_string(errbuf); + + return eOk; +} + +//-------------------------------------------------------------------------- +const char *idaapi set_python_options( + const char *keyword, + int value_type, + const void *value) +{ + do + { + if ( value_type == IDPOPT_NUM ) + { + if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 ) + { + script_timeout = int(*(uval_t *)value); + break; + } + else if ( qstrcmp(keyword, "ALERT_AUTO_SCRIPTS") == 0 ) + { + g_alert_auto_scripts = *(uval_t *)value != 0; + break; + } + else if ( qstrcmp(keyword, "REMOVE_CWD_SYS_PATH") == 0 ) + { + g_remove_cwd_sys_path = *(uval_t *)value != 0; + break; + } + else if ( qstrcmp(keyword, "USE_LOCAL_PYTHON") == 0 ) + { + g_use_local_python = *(uval_t *)value != 0; + break; + } + } + return IDPOPT_BADKEY; + } while (false); + return IDPOPT_OK; +} + +//------------------------------------------------------------------------- +// Check for the presence of a file in IDADIR/python and complain on error +bool CheckScriptFiles() +{ + static const char *const script_files[] = + { + S_IDC_MODNAME ".py", + S_INIT_PY, + "idaapi.py", + "idautils.py" + }; + for ( size_t i=0; i 0 ) + errbuf[0] = '\0'; + + if ( py_result == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + int cvt = CIP_OK; + if ( idc_result != NULL ) + { + idc_result->clear(); + cvt = pyvar_to_idcvar(py_result, idc_result); + if ( cvt < CIP_OK ) + qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); + } + + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_result); + + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile( + const char *name, + ea_t /*current_ea*/, + const char *expr, + char *errbuf, + size_t errbufsize) +{ + PyObject *globals = GetMainGlobals(); + + PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); + if ( code == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + // Set the desired function name + Py_XDECREF(code->co_name); + code->co_name = PyString_FromString(name); + + // Create a function out of code + PyObject *func = PyFunction_New((PyObject *)code, globals); + + if ( func == NULL ) + { +ERR: + handle_python_error(errbuf, errbufsize); + Py_XDECREF(code); + return false; + } + + int err = PyDict_SetItemString(globals, name, func); + Py_XDECREF(func); + if ( err ) + goto ERR; + + return true; +} + +//------------------------------------------------------------------------- +// Run callback for Python external language evaluator +bool idaapi IDAPython_extlang_run( + const char *name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + // Try to extract module name (if any) from the funcname + char modname[MAXSTR] = {0}; + char funcname[MAXSTR] = {0}; + bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); + + ppyobject_vec_t pargs; + boolvec_t decref; + bool ok = true; + PyObject *module = NULL; + do + { + // Convert arguments to python + ok = pyw_convert_idc_args(args, nargs, pargs, &decref, errbuf, errbufsize); + if ( !ok ) + break; + + if ( imported_module ) + { + PYW_GIL_ENSURE; + module = PyImport_ImportModule(modname); + PYW_GIL_RELEASE; + } + else + { + module = PyImport_AddModule(S_MAIN); + QASSERT(30156, module != NULL); + } + + PyObject *globals = PyModule_GetDict(module); + QASSERT(30157, globals != NULL); + + PyObject *func = PyDict_GetItemString(globals, funcname); + if ( func == NULL ) + { + qsnprintf(errbuf, errbufsize, "undefined function %s", name); + ok = false; + break; + } + + PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func); + PYW_GIL_ENSURE; + PyObject *pres = PyEval_EvalCodeEx( + code, + globals, NULL, + &pargs[0], nargs, + NULL, 0, NULL, 0, NULL); + PYW_GIL_RELEASE; + ok = return_python_result(result, pres, errbuf, errbufsize); + } while ( false ); + + pyw_free_idc_args(pargs, &decref); + + if ( imported_module ) + Py_XDECREF(module); + + return ok; +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile_file( + const char *filename, + char *errbuf, + size_t errbufsize) +{ + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Create an object instance +bool idaapi IDAPython_extlang_create_object( + const char *name, // in: object class name + int nargs, // in: number of input arguments + const idc_value_t args[], // in: input arguments + idc_value_t *result, // out: created object or exception + char *errbuf, // out: error message if evaluation fails + size_t errbufsize) // in: size of the error buffer +{ + PyObject *py_mod(NULL), *py_cls(NULL); + ppyobject_vec_t pargs; + + bool ok = false; + do + { + // Parse the object name (to get the module and class name) + char modname[MAXSTR] = {0}; + char clsname[MAXSTR] = {0}; + parse_py_modname(name, modname, clsname, MAXSTR); + + // Get a reference to the module + py_mod = PyW_TryImportModule(modname); + if ( py_mod == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); + break; + } + + // Get the class reference + py_cls = PyW_TryGetAttrString(py_mod, clsname); + if ( py_cls == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); + break; + } + + // Error during conversion? + ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); + if ( !ok ) + break; + + // Call the constructor + PYW_GIL_ENSURE; + PyObject *py_res = PyObject_CallObject(py_cls, pargs.empty() ? NULL : pargs[0]); + PYW_GIL_RELEASE; + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + Py_XDECREF(py_mod); + Py_XDECREF(py_cls); + + // Free the arguments tuple + pyw_free_idc_args(pargs); + return ok; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +bool idaapi IDAPython_extlang_get_attr( + const idc_value_t *obj, // in: object (may be NULL) + const char *attr, // in: attribute name + idc_value_t *result) +{ + PyObject *py_mod(NULL), *py_obj(NULL); + bool is_opaque_obj = false; + int cvt = CIP_FAILED; + do + { + // Get a reference to the module + py_mod = PyW_TryImportModule(S_MAIN); + if ( py_mod == NULL ) + break; + + // Object specified: + // - (1) string contain attribute name in the main module + // - (2) opaque object (we use it as is) + if ( obj != NULL ) + { + // (1) Get attribute from main module + if ( obj->vtype == VT_STR2 ) + py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); + // (2) see if opaque object + else + { + // Convert object (expecting opaque object) + cvt = idcvar_to_pyvar(*obj, &py_obj); + // Only opaque objects are accepted + if ( cvt != CIP_OK_NODECREF ) + { + Py_XDECREF(py_obj); + py_obj = NULL; + cvt = CIP_FAILED; + break; + } + is_opaque_obj = true; + } + // Get the attribute reference + if ( py_obj == NULL ) + break; + } + // No object specified: + else + { + // ...then work with main module + py_obj = py_mod; + } + // Special case: if attribute not passed then retrieve the class + // name associated associated with the passed object + if ( attr == NULL || attr[0] == '\0' ) + { + cvt = CIP_FAILED; + // Get the class + PyObject *cls = PyObject_GetAttrString(py_obj, "__class__"); + if ( cls == NULL ) + break; + + // Get its name + PyObject *name = PyObject_GetAttrString(cls, "__name__"); + Py_DECREF(cls); + if ( name == NULL ) + break; + + // Convert name object to string object + PyObject *string = PyObject_Str(name); + Py_DECREF(name); + if ( string == NULL ) + break; + + // Convert name python string to a C string + const char *clsname = PyString_AsString(name); + if ( clsname == NULL ) + break; + + result->set_string(clsname); + cvt = CIP_OK; //lint !e838 + break; + } + + PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr); + // No attribute? + if ( py_attr == NULL ) + { + cvt = CIP_FAILED; + break; + } + // Don't store result + if ( result == NULL ) + { + cvt = CIP_OK; + // Decrement attribute (because of GetAttrString) + Py_DECREF(py_attr); + } + else + { + cvt = pyvar_to_idcvar(py_attr, result); + // Conversion succeeded and opaque object was passed: + // Since the object will be passed to IDC, it is likely that IDC value will be + // destroyed and also destroying the opaque object with it. That is an undesired effect. + // We increment the reference of the object so that even if the IDC value dies + // the opaque object remains. So by not decrement reference after GetAttrString() call + // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. + if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) + { + // Decrement the reference (that was incremented by GetAttrString()) + Py_DECREF(py_attr); + } + } + } while ( false ); + + // Free main module reference + Py_XDECREF(py_mod); + + // Wasn't working with main module? + if ( obj != NULL && !is_opaque_obj ) + Py_XDECREF(py_obj); + + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +//lint -e{818} +bool idaapi IDAPython_extlang_set_attr( + idc_value_t *obj, // in: object name (may be NULL) + const char *attr, // in: attribute name + idc_value_t *value) +{ + PyObject *py_mod(NULL), *py_obj(NULL); + bool ok = false, is_opaque_obj = false; + do + { + // Get a reference to the module + py_mod = PyW_TryImportModule(S_MAIN); + if ( py_mod == NULL ) + break; + + if ( obj != NULL ) + { + // Get the attribute reference (from just a name) + if ( obj->vtype == VT_STR2 ) + py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); + else + { + int cvt = idcvar_to_pyvar(*obj, &py_obj); + // Only opaque objects are accepted + if ( cvt != CIP_OK_NODECREF ) + { + Py_XDECREF(py_obj); + py_obj = NULL; + } + else + is_opaque_obj = true; + } + // No object to set_attr on? + if ( py_obj == NULL ) + break; + } + else + { + // set_attr on the main module + py_obj = py_mod; + } + // Convert the value + PyObject *py_var(NULL); + int cvt = idcvar_to_pyvar(*value, &py_var); + if ( cvt >= CIP_OK ) + { + ok = PyObject_SetAttrString(py_obj, attr, py_var) != -1; + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_var); + } + } while ( false ); + + Py_XDECREF(py_mod); + + if ( obj != NULL && !is_opaque_obj ) + Py_XDECREF(py_obj); + + return ok; +} + +//------------------------------------------------------------------------- +// Calculator callback for Python external language evaluator +//lint -e{818} +bool idaapi IDAPython_extlang_calcexpr( + ea_t /*current_ea*/, + const char *expr, + idc_value_t *rv, + char *errbuf, + size_t errbufsize) +{ + PyObject *globals = GetMainGlobals(); + if ( globals == NULL ) + return false; + + begin_execution(); + PYW_GIL_ENSURE; + PyObject *result = PyRun_String(expr, Py_eval_input, globals, globals); + PYW_GIL_RELEASE; + end_execution(); + + return return_python_result(rv, result, errbuf, errbufsize); +} + +//------------------------------------------------------------------------- +bool idaapi IDAPython_extlang_call_method( + const idc_value_t *idc_obj, + const char *method_name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + // Check for unsupported usage of call_method. + // Mainly a method call requires an object and a method. + if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) + { + qstrncpy(errbuf, "call_method does not support this operation", errbufsize); + return false; + } + // Behave like run() + else if ( idc_obj == NULL && method_name != NULL ) + return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); + + PyObject *py_obj(NULL), *py_method(NULL); + // Holds conversion status of input object + int obj_cvt; + ppyobject_vec_t pargs; + bool ok = false; + do + { + // Convert input object + obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); + if ( obj_cvt < CIP_OK ) + { + qstrncpy(errbuf, "Failed to convert input object to Python value", errbufsize); + break; + } + + py_method = PyW_TryGetAttrString(py_obj, method_name); + if ( py_method == NULL || !PyCallable_Check(py_method) ) + { + qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); + break; + } + + // Convert arguments to python objects + ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); + if ( !ok ) + break; + + PYW_GIL_ENSURE; + PyObject *py_res = PyObject_CallObject(py_method, pargs.empty() ? NULL : pargs[0]); + PYW_GIL_RELEASE; + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + // Free converted args + pyw_free_idc_args(pargs); + + // Release reference of object if needed + if ( obj_cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_obj); + + Py_XDECREF(py_method); + return ok; +} + +//------------------------------------------------------------------------- +const extlang_t extlang_python = +{ + sizeof(extlang_t), + 0, + "Python", + IDAPython_extlang_compile, + IDAPython_extlang_run, + IDAPython_extlang_calcexpr, + IDAPython_extlang_compile_file, + "py", + IDAPython_extlang_create_object, + IDAPython_extlang_get_attr, + IDAPython_extlang_set_attr, + IDAPython_extlang_call_method, + IDAPython_extlang_run_statements, +}; + +//------------------------------------------------------------------------- +void enable_extlang_python(bool enable) +{ + if ( enable ) + select_extlang(&extlang_python); + else + select_extlang(NULL); +} + +//------------------------------------------------------------------------- +// Execute a line in the Python CLI +bool idaapi IDAPython_cli_execute_line(const char *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 + last_line += 1; + + // Skip empty lines + if ( last_line[0] != '\0' ) + { + // Line ends with ":" or begins with a space character? + bool more = last_line[qstrlen(last_line)-1] == ':' || qisspace(last_line[0]); + if ( more ) + return false; + } + + // + // Pseudo commands + // + qstring s; + do + { + // Help command? + if ( line[0] == '?' ) + s.sprnt("help(%s)", line+1); + // Shell command? + else if ( line[0] == '!' ) + s.sprnt("idaapi.IDAPython_ExecSystem(r'%s')", line+1); + else + break; + // Patch the command line pointer + line = s.c_str(); + } while (false); + + begin_execution(); + PythonEvalOrExec(line); + end_execution(); + + return true; +} + +//------------------------------------------------------------------------- +bool idaapi IDAPYthon_cli_complete_line( + qstring *completion, + const char *prefix, + int n, + const char *line, + int x) +{ + PyObject *py_complete = get_idaapi_attr(S_IDAAPI_COMPLETION); + if ( py_complete == NULL ) + return false; + + PYW_GIL_ENSURE; + PyObject *py_ret = PyObject_CallFunction(py_complete, "sisi", prefix, n, line, x); //lint !e1776 + PYW_GIL_RELEASE; + + Py_DECREF(py_complete); + + // Swallow the error + PyW_GetError(completion); + + bool ok = py_ret != NULL && PyString_Check(py_ret) != 0; + + if ( ok ) + *completion = PyString_AS_STRING(py_ret); + + Py_XDECREF(py_ret); + + return ok; +} + +//------------------------------------------------------------------------- +static const cli_t cli_python = +{ + sizeof(cli_t), + 0, + "Python", + "Python - IDAPython plugin", + "Enter any Python expression", + IDAPython_cli_execute_line, + IDAPYthon_cli_complete_line, + NULL +}; + +//------------------------------------------------------------------------- +// Control the Python CLI status +void enable_python_cli(bool enable) +{ + if ( enable ) + install_command_interpreter(&cli_python); + else + remove_command_interpreter(&cli_python); +} + +//------------------------------------------------------------------------- +// Prints the IDAPython copyright banner +void py_print_banner() +{ + PYW_GIL_ENSURE; + PyRun_SimpleString("print_banner()"); + PYW_GIL_RELEASE; +} + +//------------------------------------------------------------------------ +// Parse plugin options +void parse_plugin_options() +{ + // Get options from IDA + const char *options = get_plugin_options(S_IDAPYTHON); + + // No options? + if ( options == NULL ) + return; + + // User specified 'when' parameter? + const char *p = strchr(options, ';'); + if ( p == NULL ) + { + g_run_when = run_on_db_open; + p = options; + } + else + { + g_run_when = atoi(options); + ++p; + } + qstrncpy(g_run_script, p, sizeof(g_run_script)); +} + +//------------------------------------------------------------------------ +// Converts the global IDC variable "ARGV" into a Python variable. +// The arguments will then be accessible via 'idc' module / 'ARGV' variable. +void convert_idc_args() +{ + PyObject *py_args = PyList_New(0); + + idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); + if ( idc_args != NULL ) + { + idc_value_t attr; + char attr_name[20] = {"0"}; + for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) + { + PyList_Insert(py_args, i, PyString_FromString(attr.c_str())); + qsnprintf(attr_name, sizeof(attr_name), "%d", i); + } + } + + // Get reference to the IDC module (it is imported by init.py) + PyObject *py_mod = PyW_TryImportModule(S_IDC_MODNAME); + if ( py_mod != NULL ) + PyObject_SetAttrString(py_mod, S_IDC_ARGS_VARNAME, py_args); + + Py_DECREF(py_args); + Py_XDECREF(py_mod); +} + +//------------------------------------------------------------------------ +// We install the menu later because the text version crashes if +// add_menu_item is called too early +static int idaapi menu_installer_cb(void *, int code, va_list) +{ + switch ( code ) + { + case ui_ready_to_run: + g_ui_ready = true; + py_print_banner(); + + if ( g_run_when == run_on_ui_ready ) + RunScript(g_run_script); + break; + + case ui_database_inited: + { + convert_idc_args(); + if ( g_run_when == run_on_db_open ) + RunScript(g_run_script); + break; + } + default: + break; + } + return 0; +} + +//------------------------------------------------------------------------- +// remove current directory (empty entry) from the sys.path +static void sanitize_path() +{ + char buf[QMAXPATH]; + qstrncpy(buf, Py_GetPath(), sizeof(buf)); + char *ctx; + qstring newpath; + for ( char *d0 = qstrtok(buf, DELIMITER, &ctx); + d0 != NULL; + d0 = qstrtok(NULL, DELIMITER, &ctx) ) + { + if ( d0[0] == '\0' ) + // skip empty entry + continue; + + if ( !newpath.empty() ) + newpath.append(DELIMITER); + newpath.append(d0); + } + PySys_SetPath(newpath.begin()); +} + + +//------------------------------------------------------------------------- +// we have to do it ourselves because Python 2.7 calls exit() if importing site fails +static bool initsite(void) +{ + PyObject *m; + m = PyImport_ImportModule("site"); + if ( m == NULL ) + { + PyErr_Print(); + Py_Finalize(); + + return false; + } + else + { + Py_DECREF(m); + return true; + } +} + +//------------------------------------------------------------------------- +// Initialize the Python environment +bool IDAPython_Init(void) +{ + // Already initialized? + if ( g_initialized ) + return true; + + // Form the absolute path to IDA\python folder + qstrncpy(g_idapython_dir, idadir(PYTHON_DIR_NAME), sizeof(g_idapython_dir)); + + // Check for the presence of essential files + if ( !CheckScriptFiles() ) + return false; + + char tmp[QMAXPATH]; +#ifdef __LINUX__ + // Export symbols from libpython to resolve imported module deps + // use the standard soname: libpython2.7.so.1.0 +#define PYLIB "libpython" QSTRINGIZE(PY_MAJOR_VERSION) "." QSTRINGIZE(PY_MINOR_VERSION) ".so.1.0" + if ( !dlopen(PYLIB, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) + { + warning("IDAPython dlopen(" PYLIB ") error: %s", dlerror()); + return false; + } +#endif + +#ifdef __MAC__ + // We should set python home to the module's path, otherwise it can pick up stray modules from $PATH + NSModule pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); + // Use dylib functions to find out where the framework was loaded from + const char *buf = (char *)NSLibraryNameForModule(pythonModule); + if ( buf != NULL ) + { + // The path will be something like: + // /System/Library/Frameworks/Python.framework/Versions/2.5/Python + // We need to strip the last part + // use static buffer because Py_SetPythonHome() just stores a pointer + static char pyhomepath[MAXSTR]; + qstrncpy(pyhomepath, buf, MAXSTR); + char * lastslash = strrchr(pyhomepath, '/'); + if ( lastslash != NULL ) + { + *lastslash = 0; + Py_SetPythonHome(pyhomepath); + } + } +#endif + + // Read configuration value + read_user_config_file("python.cfg", set_python_options, NULL); + if ( g_alert_auto_scripts ) + { + if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) + && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" + "Do you want to continue loading IDAPython?", tmp) <= 0 ) + { + return false; + } + } + + if ( g_use_local_python ) + Py_SetPythonHome(g_idapython_dir); + + // don't import "site" right now + Py_NoSiteFlag = 1; + + // Start the interpreter + Py_Initialize(); + + if ( !Py_IsInitialized() ) + { + warning("IDAPython: Py_Initialize() failed"); + return false; + } + + // remove current directory + sanitize_path(); + + // import "site" + if ( !g_use_local_python && !initsite() ) + { + warning("IDAPython: importing \"site\" failed"); + return false; + } + + // Enable multi-threading support + if ( !PyEval_ThreadsInitialized() ) + PyEval_InitThreads(); + + // Init the SWIG wrapper + init_idaapi(); + +#ifdef Py_DEBUG + msg("HexraysPython: Python compiled with DEBUG enabled.\n"); +#endif + + // Set IDAPYTHON_VERSION in Python + qsnprintf( + tmp, + sizeof(tmp), + "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" + "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", + VER_MAJOR, + VER_MINOR, + VER_PATCH, + VER_STATUS, + VER_SERIAL, + g_remove_cwd_sys_path ? "True" : "False"); + + PyRun_SimpleString(tmp); + + // Install extlang. Needs to be done before running init.py + // in case it's calling idaapi.enable_extlang_python(1) + install_extlang(&extlang_python); + + // Execute init.py (for Python side initialization) + qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL); + if ( !PyRunFile(tmp) ) + { + // Try to fetch a one line error string. We must do it before printing + // the traceback information. Make sure that the exception is not cleared + handle_python_error(tmp, sizeof(tmp), false); + + // Print the exception traceback + PyRun_SimpleString("import traceback;traceback.print_exc();"); + + warning("IDAPython: error executing " S_INIT_PY ":\n" + "%s\n" + "\n" + "Refer to the message window to see the full error log.", tmp); + return false; + } + + // Init pywraps and notify_when + if ( !init_pywraps() || !pywraps_nw_init() ) + { + warning("IDAPython: init_pywraps() failed!"); + return false; + } + +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#endif + + // Batch-mode operation: + parse_plugin_options(); + + // Register a RunPythonStatement() function for IDC + set_idc_func_ex( + S_IDC_RUNPYTHON_STATEMENT, + idc_runpythonstatement, + idc_runpythonstatement_args, + 0); + + // A script specified on the command line is run + if ( g_run_when == run_on_init ) + RunScript(g_run_script); + +#ifdef PLUGINFIX + hook_to_notification_point(HT_UI, menu_installer_cb, NULL); +#else + install_python_menus(); + py_print_banner(); +#endif + + // Enable the CLI by default + enable_python_cli(true); + + g_initialized = true; + pywraps_nw_notify(NW_INITIDA_SLOT); + return true; +} + +//------------------------------------------------------------------------- +// Cleaning up Python +void IDAPython_Term(void) +{ +#ifdef PLUGINFIX + unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); +#endif + /* Remove the menu items before termination */ + del_menu_item("File/Python command..."); + + // Notify about IDA closing + pywraps_nw_notify(NW_TERMIDA_SLOT); + + // De-init notify_when + pywraps_nw_term(); + + // Remove the CLI + enable_python_cli(false); + + // Remove the extlang + remove_extlang(&extlang_python); + + // De-init pywraps + deinit_pywraps(); + + // Uninstall IDC function + set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL, 0); + + // Shut the interpreter down + Py_Finalize(); + + g_initialized = false; +} + +//------------------------------------------------------------------------- +// Plugin init routine +int idaapi init(void) +{ + if ( IDAPython_Init() ) + return PLUGIN_KEEP; + else + return PLUGIN_SKIP; +} + +//------------------------------------------------------------------------- +// Plugin term routine +void idaapi term(void) +{ + IDAPython_Term(); +} + +//------------------------------------------------------------------------- +// Plugin hotkey entry point +void idaapi run(int arg) +{ + try + { + switch ( arg ) + { + case IDAPYTHON_RUNSTATEMENT: + IDAPython_RunStatement(); + break; + case IDAPYTHON_ENABLE_EXTLANG: + enable_extlang_python(true); + break; + case IDAPYTHON_DISABLE_EXTLANG: + enable_extlang_python(false); + break; + default: + warning("IDAPython: unknown plugin argument %d", arg); + break; + } + } + catch(...) + { + warning("Exception in Python interpreter. Reloading..."); + IDAPython_Term(); + IDAPython_Init(); + } +} + +//------------------------------------------------------------------------- +// PLUGIN DESCRIPTION BLOCK +//------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_FLAGS | PLUGIN_HIDE, // plugin flags + init, // initialize + term, // terminate. this pointer may be NULL. + run, // invoke plugin + S_IDAPYTHON, // long comment about the plugin + // it could appear in the status line + // or as a hint + // multiline help about the plugin + "IDA Python Plugin\n", + // the preferred short name of the plugin + S_IDAPYTHON, + // the preferred hotkey to run the plugin + NULL +}; diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index ecbd5168..b5c03021 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -709,6 +709,7 @@ def RunPythonStatement(stmt): # */ +/* //--------------------------------------------------------------------------- // qstrvec_t wrapper //--------------------------------------------------------------------------- @@ -812,7 +813,7 @@ static bool qstrvec_t_remove(PyObject *self, size_t idx) sv->erase(sv->begin()+idx); return true; } - +*/ //--------------------------------------------------------------------------- // diff --git a/pywraps/py_idaapi.py b/pywraps/py_idaapi.py index 3744f860..8da0f0fb 100644 --- a/pywraps/py_idaapi.py +++ b/pywraps/py_idaapi.py @@ -207,72 +207,72 @@ def __rdiv__(self, other): return self.__op(3, other, True) # ----------------------------------------------------------------------- # qstrvec_t clinked object -class qstrvec_t(py_clinked_object_t): - """Class representing an qstrvec_t""" +# class qstrvec_t(py_clinked_object_t): +# """Class representing an qstrvec_t""" - def __init__(self, items=None): - py_clinked_object_t.__init__(self) - # Populate the list if needed - if items: - self.from_list(items) +# def __init__(self, items=None): +# py_clinked_object_t.__init__(self) +# # Populate the list if needed +# if items: +# self.from_list(items) - def _create_clink(self): - return _idaapi.qstrvec_t_create() +# def _create_clink(self): +# return _idaapi.qstrvec_t_create() - def _del_clink(self, lnk): - return _idaapi.qstrvec_t_destroy(lnk) +# def _del_clink(self, lnk): +# return _idaapi.qstrvec_t_destroy(lnk) - def _get_clink_ptr(self): - return _idaapi.qstrvec_t_get_clink_ptr(self) +# def _get_clink_ptr(self): +# return _idaapi.qstrvec_t_get_clink_ptr(self) - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.qstrvec_t_assign(self, other) +# def assign(self, other): +# """Copies the contents of 'other' to 'self'""" +# return _idaapi.qstrvec_t_assign(self, other) - def __setitem__(self, idx, s): - """Sets string at the given index""" - return _idaapi.qstrvec_t_set(self, idx, s) +# def __setitem__(self, idx, s): +# """Sets string at the given index""" +# return _idaapi.qstrvec_t_set(self, idx, s) - def __getitem__(self, idx): - """Gets the string at the given index""" - return _idaapi.qstrvec_t_get(self, idx) +# def __getitem__(self, idx): +# """Gets the string at the given index""" +# return _idaapi.qstrvec_t_get(self, idx) - def __get_size(self): - return _idaapi.qstrvec_t_size(self) +# def __get_size(self): +# return _idaapi.qstrvec_t_size(self) - size = property(__get_size) - """Returns the count of elements""" +# size = property(__get_size) +# """Returns the count of elements""" - def addressof(self, idx): - """Returns the address (as number) of the qstring at the given index""" - return _idaapi.qstrvec_t_addressof(self, idx) +# def addressof(self, idx): +# """Returns the address (as number) of the qstring at the given index""" +# return _idaapi.qstrvec_t_addressof(self, idx) - def add(self, s): - """Add a string to the vector""" - return _idaapi.qstrvec_t_add(self, s) +# def add(self, s): +# """Add a string to the vector""" +# return _idaapi.qstrvec_t_add(self, s) - def from_list(self, lst): - """Populates the vector from a Python string list""" - return _idaapi.qstrvec_t_from_list(self, lst) +# def from_list(self, lst): +# """Populates the vector from a Python string list""" +# return _idaapi.qstrvec_t_from_list(self, lst) - def clear(self, qclear=False): - """ - Clears all strings from the vector. - @param qclear: Just reset the size but do not actually free the memory - """ - return _idaapi.qstrvec_t_clear(self, qclear) +# def clear(self, qclear=False): +# """ +# Clears all strings from the vector. +# @param qclear: Just reset the size but do not actually free the memory +# """ +# return _idaapi.qstrvec_t_clear(self, qclear) - def insert(self, idx, s): - """Insert a string into the vector""" - return _idaapi.qstrvec_t_insert(self, idx, s) +# def insert(self, idx, s): +# """Insert a string into the vector""" +# return _idaapi.qstrvec_t_insert(self, idx, s) - def remove(self, idx): - """Removes a string from the vector""" - return _idaapi.qstrvec_t_remove(self, idx) +# def remove(self, idx): +# """Removes a string from the vector""" +# return _idaapi.qstrvec_t_remove(self, idx) # ----------------------------------------------------------------------- class PyIdc_cvt_refclass__(pyidc_cvt_helper__): diff --git a/swig/hexrays.i b/swig/hexrays.i new file mode 100644 index 00000000..4fb76058 --- /dev/null +++ b/swig/hexrays.i @@ -0,0 +1,1111 @@ +//--------------------------------------------------------------------- +// SWIG bindings for Hexray Decompiler's hexrays.hpp +// +// Author: EiNSTeiN_ +// +// Integrated into IDAPython project by the IDAPython Team +//--------------------------------------------------------------------- + +// Suppress 'previous definition of XX' warnings +#pragma SWIG nowarn=302 +// and others... +#pragma SWIG nowarn=312 +#pragma SWIG nowarn=325 +#pragma SWIG nowarn=314 +#pragma SWIG nowarn=362 +#pragma SWIG nowarn=383 +#pragma SWIG nowarn=389 +#pragma SWIG nowarn=401 +#pragma SWIG nowarn=451 +#pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory + +#define _STD_BEGIN +#define typename + +%{ +#include "hexrays.hpp" +%} + +//--------------------------------------------------------------------- +// some defines to calm SWIG down. +#define DEFINE_MEMORY_ALLOCATION_FUNCS() +//#define DECLARE_UNCOPYABLE(f) +#define AS_PRINTF(format_idx, varg_idx) +#define idaapi +#define __fastcall + +%ignore vd_printer_t::vprint; +%ignore string_printer_t::vprint; +%ignore typestring::dstr; +%ignore typestring::multiprint; +%ignore vdui_t::vdui_t; +%ignore cblock_t::find; +%ignore cfunc_t::cfunc_t; +%ignore ctree_item_t::verify; +%ignore ccases_t::find_value; +%ignore ccases_t::print; +%ignore ccase_t::set_insn; +%ignore ccase_t::print; +%ignore carglist_t::print; +%ignore cblock_t::remove_gotos; +%ignore casm_t::genasm; +%ignore cblock_t::use_curly_braces; +%ignore casm_t::print; +%ignore cgoto_t::print; +%ignore cexpr_t::is_aliasable; +%ignore cexpr_t::contains_expr; +%ignore cexpr_t::contains_expr; +%ignore cexpr_t::cexpr_t(mbl_array_t *mba, const lvar_t &v); +%ignore lvar_t::is_promoted_arg; +%ignore lvar_t::lvar_t; +%ignore strtype_info_t::find_strmem; +%ignore typestring::resolve_func_type; +%ignore typestring::common_type; +%ignore typestring::noarray_size; +%ignore file_printer_t::_print; +%ignore file_printer_t; + +%extend cfunc_t { + %immutable rgas; + %immutable stas; +}; + +%rename(dereference_uint16) operator uint16*; +%rename(dereference_const_uint16) operator const uint16*; + +// this is a dummy class template to allow swig to do its thing. +template class std::map { +public: + mapped_type& at(const key_type& _Keyval); + size_t size() const; +}; + +//--------------------------------------------------------------------- +%extend citem_t { + // define these two struct members that can be used for casting. + cinsn_t *cinsn const { return (cinsn_t *)self; } + cexpr_t *cexpr const { return (cexpr_t *)self; } +}; + +#define CITEM_MEMBER_REF(name) \ + name##_t *name const { return self->##name; } + +//--------------------------------------------------------------------- +// swig doesn't very much like the way the union is done in this class so we need to wrap all these up. +%extend cinsn_t { + CITEM_MEMBER_REF(cblock) + CITEM_MEMBER_REF(cexpr) + CITEM_MEMBER_REF(cif) + CITEM_MEMBER_REF(cfor) + CITEM_MEMBER_REF(cwhile) + CITEM_MEMBER_REF(cdo) + CITEM_MEMBER_REF(cswitch) + CITEM_MEMBER_REF(creturn) + CITEM_MEMBER_REF(cgoto) + CITEM_MEMBER_REF(casm) +}; + +#define CEXPR_MEMBER_REF(type, name) \ + type name const { return self->##name; } + +%extend cexpr_t { + CEXPR_MEMBER_REF(cnumber_t*, n) + CEXPR_MEMBER_REF(fnumber_t*, fpc) + const var_ref_t& v { return self->v; } + CEXPR_MEMBER_REF(ea_t, obj_ea) + CEXPR_MEMBER_REF(int, refwidth) + CEXPR_MEMBER_REF(cexpr_t*, x) + CEXPR_MEMBER_REF(cexpr_t*, y) + CEXPR_MEMBER_REF(carglist_t*, a) + CEXPR_MEMBER_REF(int, m) + CEXPR_MEMBER_REF(cexpr_t*, z) + CEXPR_MEMBER_REF(int, ptrsize) + CEXPR_MEMBER_REF(cinsn_t*, insn) + CEXPR_MEMBER_REF(char*, helper) + CEXPR_MEMBER_REF(char*, string) +}; + +%extend ctree_item_t { + CEXPR_MEMBER_REF(citem_t *, it) + CEXPR_MEMBER_REF(lvar_t*, l) + CEXPR_MEMBER_REF(cfunc_t*, f) + const treeloc_t& loc { return self->loc; } +}; + +/* for qvector instanciations where the class is a pointer (cinsn_t, citem_t) we need + to fix the at() return type, otherwise swig mistakenly thinks it is "cinsn_t *&" and nonsense ensues. */ +%extend qvector< cinsn_t *> { + cinsn_t *at(size_t n) { return self->at(n); } +}; +%extend qvector< citem_t *> { + citem_t *at(size_t n) { return self->at(n); } +}; + +// ignore future declarations of at() for these classes +%ignore qvector< cinsn_t *>::at(size_t) const; +%ignore qvector< cinsn_t *>::at(size_t); +%ignore qvector< citem_t *>::at(size_t) const; +%ignore qvector< citem_t *>::at(size_t); +%ignore qvector< citem_t *>::grow; +%ignore qvector< cinsn_t *>::grow; + +//~ %template(qwstrvec_t) qvector; // vector of unicode strings +typedef intvec_t svalvec_t; // vector of signed values +typedef intvec_t eavec_t;// vector of addresses + + +// hexrays templates +%template(user_numforms_t) std::map; +%template(lvar_mapping_t) std::map; +%template(hexwarns_t) qvector; +%template(ctree_items_t) qvector; +%template(user_labels_t) std::map; +%template(user_cmts_t) std::map; +%template(user_iflags_t) std::map; +%template(user_unions_t) std::map; +%template(cinsnptrvec_t) qvector; +%template(eamap_t) std::map; +%template(boundaries_t) std::map; +%template(cfuncptr_t) qrefcnt_t; +%template(qvector_history_t) qvector; +%template(history_t) qstack; +typedef int iterator_word; + +/* no support for nested classes in swig means we need to wrap + this iterator and do some magic... + + to use it, call qlist< cinsn_t >::begin() which will return the + proper iterator type which can then be used to get the current item. +*/ +%{ +typedef qlist::iterator qlist_cinsn_t_iterator; +%} +class qlist_cinsn_t_iterator {}; +%extend qlist_cinsn_t_iterator { + const cinsn_t &cur { return *(*self); } + qlist_cinsn_t_iterator &next(void) { (*self)++; return *self; } +}; + +%extend qlist { + qlist_cinsn_t_iterator begin() { return self->begin(); } + qlist_cinsn_t_iterator end(void) { return self->end(); } + qlist_cinsn_t_iterator insert(qlist_cinsn_t_iterator p, const cinsn_t& x) { return self->insert(p, x); } + void erase(qlist_cinsn_t_iterator p) { self->erase(p); } +}; +%ignore qlist< cinsn_t >::insert(); +%ignore qlist< cinsn_t >::erase(); +%ignore qlist< cinsn_t >::begin(); +%ignore qlist< cinsn_t >::begin() const; +%ignore qlist< cinsn_t >::end(); +%ignore qlist< cinsn_t >::end() const; + +//%template(qvector_meminfo_t) qvector; +%template(qvector_lvar_t) qvector; +%template(qlist_cinsn_t) qlist; +%template(qvector_carg_t) qvector; +%template(qvector_ccase_t) qvector; + +%extend citem_cmt_t { + const char *c_str() const { return self->c_str(); } +}; + +%{ +cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf) +{ + try + { + cfuncptr_t cfunc = decompile(pfn, hf); + return cfunc; + } + catch(...) + { + error("Hex-Rays Python: decompiler threw an exception.\n"); + } + return cfuncptr_t(0); +} +%} + +cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf); +%ignore decompile; + +void qswap(cinsn_t &a, cinsn_t &b); +%include "typemaps.i" + +%typemap(out) void +{ + Py_INCREF(Py_None); + $1obj = Py_None; +} + +%{ + +//--------------------------------------------------------------------- +static int hexrays_cblist_py_call(PyObject *fct, PyObject *args) +{ + PyObject *resultobj; + int result; + int ecode1 = 0 ; + + resultobj = PyEval_CallObject(fct, args); + + if (resultobj == NULL) + { + msg("IDAPython: Hex-rays python callback raised an exception.\n"); + + // we can't do much else than clear the exception since this was not called from Python. + // XXX: print stack trace? + PyErr_Clear(); + return 0; + } + + ecode1 = SWIG_AsVal_int(resultobj, &result); + Py_DECREF(resultobj); + + if (SWIG_IsOK(ecode1)) + return result; + + msg("IDAPython: Hex-rays python callback returned non-integer, value ignored.\n"); + return 0; +} + +//--------------------------------------------------------------------- +static bool idaapi __python_custom_viewer_popup_item_callback(void *ud) +{ + int ret; + PyObject *fct = (PyObject *)ud; + + ret = hexrays_cblist_py_call(fct, NULL); + + return ret ? true : false; +} + +//--------------------------------------------------------------------- +static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_list va) +{ + int ret; + PyObject *fct = (PyObject *)ud; + void *argp = NULL; + PyObject *args = NULL; + + switch(event) + { + case hxe_maturity: + ///< Ctree maturity level is being changed. + ///< cfunc_t *cfunc + ///< ctree_maturity_t new_maturity + { + cfunc_t *arg0 = va_arg(va, cfunc_t *); + ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iOi)", event, arg0obj, arg1); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_cfunc_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 cfunc_t"); + //PyErr_Clear(); + } + } + break; + case hxe_interr: + ///< Internal error has occurred. + ///< int errcode + { + int arg0 = va_argi(va, int); + + args = Py_BuildValue("(ii)", event, arg0); + ret = hexrays_cblist_py_call(fct, args); + + Py_DECREF(args); + } + break; + + case hxe_print_func: + ///< Printing ctree and generating text. + ///< cfunc_t *cfunc + ///< vc_printer_t *vp + ///< Returns: 1 if text has been generated by the plugin + { + cfunc_t *arg0 = va_arg(va, cfunc_t *); + vc_printer_t *arg1 = va_arg(va, vc_printer_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, SWIG_POINTER_OWN | 0 ); + PyObject *arg1obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iOO)", event, arg0obj, arg1obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + //Py_XDECREF(arg1obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_cfunc_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 cfunc_t"); + PyErr_Clear(); + } + if (!SWIG_IsOK(SWIG_ConvertPtr(arg1obj, &argp,SWIGTYPE_p_vc_printer_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #1 vc_printer_t"); + //PyErr_Clear(); + } + } + break; + + // User interface related events: + case hxe_open_pseudocode: + ///< New pseudocode view has been opened. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_switch_pseudocode: + ///< Existing pseudocode view has been reloaded + ///< with a new function. Its text has not been + ///< refreshed yet, only cfunc and mba pointers are ready. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_refresh_pseudocode: + ///< Existing pseudocode text has been refreshed. + ///< vdui_t *vu + ///< See also hxe_text_ready, which happens earlier + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_close_pseudocode: + ///< Pseudocode view is being closed. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_keyboard: + ///< Keyboard has been hit. + ///< vdui_t *vu + ///< int key_code (VK_...) + ///< int shift_state + ///< Should return: 1 if the event has been handled + { + vdui_t *arg0 = va_arg(va, vdui_t *); + int arg1 = va_argi(va, int); + int arg2 = va_argi(va, int); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iOii)", event, arg0obj, arg1, arg2); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_right_click: + ///< Mouse right click. We can add menu items now. + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_double_click: + ///< Mouse double click. + ///< vdui_t *vu + ///< int shift_state + ///< Should return: 1 if the event has been handled + { + vdui_t *arg0 = va_arg(va, vdui_t *); + int arg1 = va_argi(va, int); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iOi)", event, arg0obj, arg1); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_curpos: + ///< Current cursor position has been changed. + ///< (for example, by left-clicking or using keyboard) + ///< vdui_t *vu + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_create_hint: + ///< Create a hint for the current item. + ///< vdui_t *vu + ///< qstring *result_hint + ///< int *implines + ///< Possible return values: + ///< 0: the event has not been handled + ///< 1: hint has been created (should set *implines to nonzero as well) + ///< 2: hint has been created but the standard hints must be + ///< appended by the decompiler + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + case hxe_text_ready: + ///< Decompiled text is ready. + ///< vdui_t *vu + ///< This event can be used to modify the output text (sv). + ///< The text uses regular color codes (see lines.hpp) + ///< COLOR_ADDR is used to store pointers to ctree elements + { + vdui_t *arg0 = va_arg(va, vdui_t *); + PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, SWIG_POINTER_OWN | 0 ); + + args = Py_BuildValue("(iO)", event, arg0obj); + ret = hexrays_cblist_py_call(fct, args); + + //Py_XDECREF(arg0obj); + Py_DECREF(args); + + if (!SWIG_IsOK(SWIG_ConvertPtr(arg0obj, &argp,SWIGTYPE_p_vdui_t, SWIG_POINTER_DISOWN | 0 ))) { + msg("error deleting callback argument #0 vdui_t"); + //PyErr_Clear(); + } + } + break; + default: + //~ msg("IDAPython: Unknown event `%u' occured\n", event); + ret = 0; + break; + } + + return ret; +} + +%} + +%ignore init_hexrays_plugin; +%rename(init_hexrays_plugin) __init_hexrays_plugin; + +%rename(add_custom_viewer_popup_item) __add_custom_viewer_popup_item; + +%ignore add_custom_viewer_popup_item; +%rename(add_custom_viewer_popup_item) __add_custom_viewer_popup_item; + +%ignore install_hexrays_callback; +%rename(install_hexrays_callback) __install_hexrays_callback; + +%ignore remove_hexrays_callback; +%rename(remove_hexrays_callback) __remove_hexrays_callback; + +%inline %{ + +//--------------------------------------------------------------------- +extern hexdsp_t *hexdsp; +bool __init_hexrays_plugin(int flags=0) +{ + // Only initialize one time + if (hexdsp == NULL) + return init_hexrays_plugin(flags); + else + return true; +} + +//--------------------------------------------------------------------- +void __add_custom_viewer_popup_item( + TCustomControl *custom_viewer, + const char *title, + const char *hotkey, + PyObject *custom_viewer_popup_item_callback) +{ + Py_INCREF(custom_viewer_popup_item_callback); + add_custom_viewer_popup_item(custom_viewer, title, hotkey, __python_custom_viewer_popup_item_callback, custom_viewer_popup_item_callback); +}; + +//--------------------------------------------------------------------- +bool __install_hexrays_callback(PyObject *hx_cblist_callback) +{ + if (install_hexrays_callback(__hexrays_python_callback, hx_cblist_callback)) + { + Py_INCREF(hx_cblist_callback); + return true; + } + return false; +} + +//--------------------------------------------------------------------- +int __remove_hexrays_callback(PyObject *hx_cblist_callback) +{ + int result, i; + result = remove_hexrays_callback(__hexrays_python_callback, hx_cblist_callback); + for (i=0;i= cot_empty and self.op <= cot_last: + return self.cexpr + elif self.op >= cit_empty and self.op < cit_end: + return self.cinsn + + raise RuntimeError('unknown op type') +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()): + if k.startswith('cit_'): + cinsn_t.op_to_typename[getattr(globals(), k)] = k[4:] + +""" array used for translating cexpr_t->op type to their names. """ +cexpr_t.op_to_typename = {} +for k in dir(globals()): + if k.startswith('cot_'): + cexpr_t.op_to_typename[getattr(globals(), k)] = k[4:] + +def property_op_to_typename(self): + return self.op_to_typename[self.op] +cinsn_t.opname = property(property_op_to_typename) +cexpr_t.opname = property(property_op_to_typename) + +def cexpr_operands(self): + """ return a dictionary with the operands of a cexpr_t. """ + + if self.op >= cot_comma and self.op <= cot_asgumod or \ + self.op >= cot_lor and self.op <= cot_fdiv or \ + self.op == cot_idx: + return {'x': self.x, 'y': self.y} + + elif self.op == cot_tern: + return {'x': self.x, 'y': self.y, 'z': self.z} + + elif self.op in [cot_fneg, cot_neg, cot_sizeof] or \ + self.op >= cot_lnot and self.op <= cot_predec: + return {'x': self.x} + + elif self.op == cot_cast: + return {'type': self.type, 'x': self.x} + + elif self.op == cot_call: + return {'x': self.x, 'a': self.a} + + elif self.op in [cot_memref, cot_memptr]: + return {'x': self.x, 'm': self.m} + + elif self.op == cot_num: + return {'n': self.n} + + elif self.op == cot_fnum: + return {'fpc': self.fpc} + + elif self.op == cot_str: + return {'string': self.string} + + elif self.op == cot_obj: + return {'obj_ea': self.obj_ea} + + elif self.op == cot_var: + return {'v': self.v} + + elif self.op == cot_helper: + return {'helper': self.helper} + + raise RuntimeError('unknown op type %s' % self.opname) +cexpr_t.operands = property(cexpr_operands) + +def cinsn_details(self): + """ return the details pointer for the cinsn_t object depending on the value of its op member. \ + this is one of the cblock_t, cif_t, etc. objects. """ + + if self.op not in self.op_to_typename: + raise RuntimeError('unknown item->op type') + + opname = self.opname + if opname == 'empty': + return self + + if opname in ['break', 'continue']: + return None + + return getattr(self, 'c' + opname) +cinsn_t.details = property(cinsn_details) + +def cblock_iter(self): + + iter = self.begin() + for i in range(self.size()): + yield iter.cur + iter.next() + + return +cblock_t.__iter__ = cblock_iter +cblock_t.__len__ = cblock_t.size + +# cblock.find(cinsn_t) -> returns the iterator positioned at the given item +def cblock_find(self, item): + + iter = self.begin() + for i in range(self.size()): + if iter.cur == item: + return iter + iter.next() + + return +cblock_t.find = cblock_find + +# cblock.index(cinsn_t) -> returns the index of the given item +def cblock_index(self, item): + + iter = self.begin() + for i in range(self.size()): + if iter.cur == item: + return i + iter.next() + + return +cblock_t.index = cblock_index + +# cblock.at(int) -> returns the item at the given index index +def cblock_at(self, index): + + iter = self.begin() + for i in range(self.size()): + if i == index: + return iter.cur + iter.next() + + return +cblock_t.at = cblock_at + +# cblock.remove(cinsn_t) +def cblock_remove(self, item): + + iter = self.find(item) + self.erase(iter) + + return +cblock_t.remove = cblock_remove + +# cblock.insert(index, cinsn_t) +def cblock_insert(self, index, item): + + pos = self.at(index) + iter = self.find(pos) + self.insert(iter, item) + + return +cblock_t.insert = cblock_insert + +def cfunc___str__(self): + qs = qstring() + p = qstring_printer_t(self, qs, 0) + self.print_func(p) + return qs.c_str() +cfunc_t.__str__ = cfunc___str__ +cfuncptr_t.__str__ = lambda self: str(self.__deref__()) + +def cfunc_typestring(self): + """ Get the function's return type typestring object. The full prototype \ + can be obtained via typestring._print() method. """ + + ts = typestring() + qt = qtype() + + result = self.get_func_type(ts, qt) + if not result: return + + return ts +cfunc_t.typestring = property(cfunc_typestring) +cfuncptr_t.typestring = property(lambda self: self.__deref__().typestring) + +cfunc_t.arguments = property(lambda self: [o for o in self.lvars if o.is_arg_var]) +cfuncptr_t.arguments = property(lambda self: self.__deref__().arguments) + +cfunc_t.lvars = property(cfunc_t.get_lvars) +cfuncptr_t.lvars = property(lambda self: self.__deref__().lvars) +cfunc_t.warnings = property(cfunc_t.get_warnings) +cfuncptr_t.warnings = property(lambda self: self.__deref__().warnings) +cfunc_t.pseudocode = property(cfunc_t.get_pseudocode) +cfuncptr_t.pseudocode = property(lambda self: self.__deref__().get_pseudocode()) +cfunc_t.eamap = property(cfunc_t.get_eamap) +cfuncptr_t.eamap = property(lambda self: self.__deref__().get_eamap()) +cfunc_t.boundaries = property(cfunc_t.get_boundaries) +cfuncptr_t.boundaries = property(lambda self: self.__deref__().get_boundaries()) + +lvar_t.used = property(lvar_t.used) +lvar_t.typed = property(lvar_t.typed) +lvar_t.mreg_done = property(lvar_t.mreg_done) +lvar_t.has_nice_name = property(lvar_t.has_nice_name) +lvar_t.is_unknown_width = property(lvar_t.is_unknown_width) +lvar_t.has_user_info = property(lvar_t.has_user_info) +lvar_t.has_user_name = property(lvar_t.has_user_name) +lvar_t.has_user_type = property(lvar_t.has_user_type) +lvar_t.is_result_var = property(lvar_t.is_result_var) +lvar_t.is_arg_var = property(lvar_t.is_arg_var) +lvar_t.is_fake_var = property(lvar_t.is_fake_var) +lvar_t.is_overlapped_var = property(lvar_t.is_overlapped_var) +lvar_t.is_floating_var = property(lvar_t.is_floating_var) +lvar_t.is_spoiled_var = property(lvar_t.is_spoiled_var) +lvar_t.is_mapdst_var = property(lvar_t.is_mapdst_var) + +# dictify all dict-like types + +def _map___getitem__(self, key): + """ Returns the value associated with the provided key. """ + if type(key) != self.keytype: + raise KeyError('type of key should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + return self.second(self.find(key)) + +def _map___setitem__(self, key, value): + """ Returns the value associated with the provided key. """ + if type(key) != self.keytype: + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if type(value) != self.valuetype: + raise KeyError('type of `value` should be ' + repr(self.valuetype) + ' but got ' + type(value)) + self.insert(key, value) + return + +def _map___delitem__(self, key): + """ Removes the value associated with the provided key. """ + if type(key) != self.keytype: + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + self.erase(self.find(key)) + return + +def _map___contains__(self, key): + """ Returns true if the specified key exists in the . """ + if type(key) != self.keytype: + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if self.find(key) != self.end(): + return True + return False + +def _map_clear(self): + self.clear() + return + +def _map_copy(self): + ret = {} + for k in self.iterkeys(): + ret[k] = self[k] + return ret + +def _map_get(self, key, default=None): + if key in self: + return self[key] + return default + +def _map_iterkeys(self): + iter = self.begin() + while iter != self.end(): + yield self.first(iter) + iter = self.next(iter) + return + +def _map_itervalues(self): + iter = self.begin() + while iter != self.end(): + yield self.second(iter) + iter = self.next(iter) + return + +def _map_iteritems(self): + iter = self.begin() + while iter != self.end(): + yield (self.first(iter), self.second(iter)) + iter = self.next(iter) + return + +def _map_keys(self): + return list(self.iterkeys()) + +def _map_values(self): + return list(self.itervalues()) + +def _map_items(self): + return list(self.iteritems()) + +def _map_has_key(self, key): + return key in self + +def _map_pop(self, key): + """ Sets the value associated with the provided key. """ + if type(key) != self.keytype: + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key not in self: + raise KeyError('key not found') + ret = self[key] + del self[key] + return ret + +def _map_popitem(self): + """ Sets the value associated with the provided key. """ + if len(self) == 0: + raise KeyError('key not found') + key = self.keys()[0] + return (key, self.pop(key)) + +def _map_setdefault(self, key, default=None): + """ Sets the value associated with the provided key. """ + if type(key) != self.keytype: + raise KeyError('type of `key` should be ' + repr(self.keytype) + ' but got ' + repr(type(key))) + if key in self: + return self[key] + self[key] = default + return default + +def _map_as_dict(maptype, name, keytype, valuetype): + + maptype.keytype = keytype + maptype.valuetype = valuetype + + for fctname in ['begin', 'end', 'first', 'second', 'next', \ + 'find', 'insert', 'erase', 'clear', 'size']: + fct = globals()[name + '_' + fctname] + setattr(maptype, '__' + fctname, fct) + + maptype.__len__ = maptype.size + maptype.__getitem__ = maptype.at + + maptype.begin = lambda self, *args: self.__begin(self, *args) + maptype.end = lambda self, *args: self.__end(self, *args) + maptype.first = lambda self, *args: self.__first(*args) + maptype.second = lambda self, *args: self.__second(*args) + maptype.next = lambda self, *args: self.__next(*args) + maptype.find = lambda self, *args: self.__find(self, *args) + maptype.insert = lambda self, *args: self.__insert(self, *args) + maptype.erase = lambda self, *args: self.__erase(self, *args) + maptype.clear = lambda self, *args: self.__clear(self, *args) + maptype.size = lambda self, *args: self.__size(self, *args) + maptype.__getitem__ = _map___getitem__ + maptype.__setitem__ = _map___setitem__ + maptype.__delitem__ = _map___delitem__ + maptype.__contains__ = _map___contains__ + maptype.clear = _map_clear + maptype.copy = _map_copy + maptype.get = _map_get + maptype.iterkeys = _map_iterkeys + maptype.itervalues = _map_itervalues + maptype.iteritems = _map_iteritems + maptype.keys = _map_keys + maptype.values = _map_values + maptype.items = _map_items + maptype.has_key = _map_has_key + maptype.pop = _map_pop + maptype.popitem = _map_popitem + maptype.setdefault = _map_setdefault + +_map_as_dict(user_labels_t, 'user_labels', int, qstring) +_map_as_dict(user_cmts_t, 'user_cmts', treeloc_t, citem_cmt_t) +_map_as_dict(user_numforms_t, 'user_numforms', operand_locator_t, number_format_t) +_map_as_dict(user_iflags_t, 'user_iflags', citem_locator_t, int) +_map_as_dict(user_unions_t, 'user_unions', int, intvec_t) +_map_as_dict(eamap_t, 'eamap', int, cinsnptrvec_t) +#_map_as_dict(boundaries_t, 'boundaries', cinsn_t, areaset_t) + +%} diff --git a/swig/idaapi.i b/swig/idaapi.i index 829c61fe..2eb3df4d 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -100,6 +100,9 @@ static PyObject *type##_get_clink_ptr(PyObject *self) #include "fpro.h" #include #include "graph.hpp" +#ifdef WITH_HEXRAYS +#include "hexrays.hpp" +#endif #include "pywraps.hpp" // @@ -2308,72 +2311,72 @@ class PyIdc_cvt_int64__(pyidc_cvt_helper__): # ----------------------------------------------------------------------- # qstrvec_t clinked object -class qstrvec_t(py_clinked_object_t): - """Class representing an qstrvec_t""" +# class qstrvec_t(py_clinked_object_t): +# """Class representing an qstrvec_t""" - def __init__(self, items=None): - py_clinked_object_t.__init__(self) - # Populate the list if needed - if items: - self.from_list(items) +# def __init__(self, items=None): +# py_clinked_object_t.__init__(self) +# # Populate the list if needed +# if items: +# self.from_list(items) - def _create_clink(self): - return _idaapi.qstrvec_t_create() +# def _create_clink(self): +# return _idaapi.qstrvec_t_create() - def _del_clink(self, lnk): - return _idaapi.qstrvec_t_destroy(lnk) +# def _del_clink(self, lnk): +# return _idaapi.qstrvec_t_destroy(lnk) - def _get_clink_ptr(self): - return _idaapi.qstrvec_t_get_clink_ptr(self) +# def _get_clink_ptr(self): +# return _idaapi.qstrvec_t_get_clink_ptr(self) - def assign(self, other): - """Copies the contents of 'other' to 'self'""" - return _idaapi.qstrvec_t_assign(self, other) +# def assign(self, other): +# """Copies the contents of 'other' to 'self'""" +# return _idaapi.qstrvec_t_assign(self, other) - def __setitem__(self, idx, s): - """Sets string at the given index""" - return _idaapi.qstrvec_t_set(self, idx, s) +# def __setitem__(self, idx, s): +# """Sets string at the given index""" +# return _idaapi.qstrvec_t_set(self, idx, s) - def __getitem__(self, idx): - """Gets the string at the given index""" - return _idaapi.qstrvec_t_get(self, idx) +# def __getitem__(self, idx): +# """Gets the string at the given index""" +# return _idaapi.qstrvec_t_get(self, idx) - def __get_size(self): - return _idaapi.qstrvec_t_size(self) +# def __get_size(self): +# return _idaapi.qstrvec_t_size(self) - size = property(__get_size) - """Returns the count of elements""" +# size = property(__get_size) +# """Returns the count of elements""" - def addressof(self, idx): - """Returns the address (as number) of the qstring at the given index""" - return _idaapi.qstrvec_t_addressof(self, idx) +# def addressof(self, idx): +# """Returns the address (as number) of the qstring at the given index""" +# return _idaapi.qstrvec_t_addressof(self, idx) - def add(self, s): - """Add a string to the vector""" - return _idaapi.qstrvec_t_add(self, s) +# def add(self, s): +# """Add a string to the vector""" +# return _idaapi.qstrvec_t_add(self, s) - def from_list(self, lst): - """Populates the vector from a Python string list""" - return _idaapi.qstrvec_t_from_list(self, lst) +# def from_list(self, lst): +# """Populates the vector from a Python string list""" +# return _idaapi.qstrvec_t_from_list(self, lst) - def clear(self, qclear=False): - """ - Clears all strings from the vector. - @param qclear: Just reset the size but do not actually free the memory - """ - return _idaapi.qstrvec_t_clear(self, qclear) +# def clear(self, qclear=False): +# """ +# Clears all strings from the vector. +# @param qclear: Just reset the size but do not actually free the memory +# """ +# return _idaapi.qstrvec_t_clear(self, qclear) - def insert(self, idx, s): - """Insert a string into the vector""" - return _idaapi.qstrvec_t_insert(self, idx, s) +# def insert(self, idx, s): +# """Insert a string into the vector""" +# return _idaapi.qstrvec_t_insert(self, idx, s) - def remove(self, idx): - """Removes a string from the vector""" - return _idaapi.qstrvec_t_remove(self, idx) +# def remove(self, idx): +# """Removes a string from the vector""" +# return _idaapi.qstrvec_t_remove(self, idx) # ----------------------------------------------------------------------- class PyIdc_cvt_refclass__(pyidc_cvt_helper__): @@ -2677,6 +2680,9 @@ NW_REMOVE = 0x0010 %include "fixup.i" %include "frame.i" %include "funcs.i" +#ifdef WITH_HEXRAYS + %include "hexrays.i" +#endif %inline %{ @@ -2780,6 +2786,7 @@ def RunPythonStatement(stmt): # */ +/* //--------------------------------------------------------------------------- // qstrvec_t wrapper //--------------------------------------------------------------------------- @@ -2883,7 +2890,7 @@ static bool qstrvec_t_remove(PyObject *self, size_t idx) sv->erase(sv->begin()+idx); return true; } - +*/ //--------------------------------------------------------------------------- diff --git a/swig/pro.i b/swig/pro.i index 0a935c8c..d61003f9 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -1,99 +1,136 @@ -%ignore wchar2char; -%ignore hit_counter_t; -%ignore reg_hit_counter; -%ignore create_hit_counter; -%ignore hit_counter_timer; -%ignore print_all_counters; -%ignore incrementer_t; -%ignore reloc_info_t; // swig under mac chokes on this -%ignore qstrvec_t; -%ignore qmutex_create; -%ignore qiterator; -%ignore qrefcnt_t; -%ignore qmutex_free; -%ignore qmutex_lock; -%ignore qmutex_t; -%ignore qmutex_unlock; -%ignore qsem_create; -%ignore qsem_free; -%ignore qsem_post; -%ignore qsem_wait; -%ignore qsemaphore_t; -%ignore qthread_cb_t; -%ignore qthread_create; -%ignore qthread_free; -%ignore qthread_join; -%ignore qthread_kill; -%ignore qthread_self; -%ignore qthread_same; -%ignore qthread_t; -%ignore qhandle_t; -%ignore qpipe_create; -%ignore qpipe_read; -%ignore qpipe_write; -%ignore qpipe_close; -%ignore qwait_for_handles; -%ignore qstrlen; -%ignore qstrcmp; -%ignore qstrstr; -%ignore qstrchr; -%ignore qstrrchr; -%ignore qstring; -%ignore qvector; -%ignore bytevec_t; -%ignore reloc_info_t; -%ignore relobj_t; -%ignore wchar2char; -%ignore u2cstr; -%ignore c2ustr; -%ignore base64_encode; -%ignore base64_decode; -%ignore utf8_unicode; -%ignore win_utf2idb; -%ignore char2oem; -%ignore oem2char; -%ignore set_codepages; -%ignore get_codepages; -%ignore convert_codepage; -%ignore test_bit; -%ignore set_bit; -%ignore clear_bit; -%ignore set_all_bits; -%ignore clear_all_bits; -%ignore interval::overlap; -%ignore interval::includes; -%ignore interval::contains; -%ignore qrotl; -%ignore qrotr; -%ignore setflag; -%ignore read2bytes; -%ignore rotate_left; -%ignore qswap; -%ignore swap32; -%ignore swap16; -%ignore swap_value; -%ignore qalloc_or_throw; -%ignore qrealloc_or_throw; -%ignore get_buffer_for_sysdir; -%ignore get_buffer_for_winerr; -%ignore call_atexits; -%ignore launch_process_params_t; -%ignore launch_process; -%ignore term_process; -%ignore get_process_exit_code; -%ignore BELOW_NORMAL_PRIORITY_CLASS; -%ignore parse_command_line; -%ignore parse_command_line2; -%rename (parse_command_line2) py_parse_command_line; -%ignore qgetenv; -%ignore qsetenv; -%ignore qctime; -%ignore qlocaltime; -%ignore qstrftime; -%ignore qstrftime64; -%ignore qstrtok; -%ignore qstrlwr; -%ignore qstrupr; -%include "pro.h" - -SWIG_DECLARE_PY_CLINKED_OBJECT(qstrvec_t) +//--------------------------------------------------------------------- +%typemap(out) uint64 { +$result = PyLong_FromUnsignedLongLong((unsigned long long) $1); +} + +//--------------------------------------------------------------------- +%typemap(in) uint64 +{ + uint64 $1_temp; + if ( !PyW_GetNumber($input, &$1_temp) ) + { + PyErr_SetString(PyExc_TypeError, "Expected an uint64 type"); + return NULL; + } + $1 = $1_temp; +} + +//--------------------------------------------------------------------- +%ignore wchar2char; +%ignore hit_counter_t; +%ignore reg_hit_counter; +%ignore create_hit_counter; +%ignore hit_counter_timer; +%ignore print_all_counters; +%ignore incrementer_t; +%ignore reloc_info_t; // swig under mac chokes on this +%ignore qmutex_create; +%ignore qiterator; +%ignore qmutex_free; +%ignore qmutex_lock; +%ignore qmutex_t; +%ignore qmutex_unlock; +%ignore qsem_create; +%ignore qsem_free; +%ignore qsem_post; +%ignore qsem_wait; +%ignore qsemaphore_t; +%ignore qthread_cb_t; +%ignore qthread_create; +%ignore qthread_free; +%ignore qthread_join; +%ignore qthread_kill; +%ignore qthread_self; +%ignore qthread_same; +%ignore qthread_t; +%ignore qhandle_t; +%ignore qpipe_create; +%ignore qpipe_read; +%ignore qpipe_write; +%ignore qpipe_close; +%ignore qwait_for_handles; +%ignore qstrlen; +%ignore qstrcmp; +%ignore qstrstr; +%ignore qstrchr; +%ignore qstrrchr; +%ignore bytevec_t; +%ignore reloc_info_t; +%ignore relobj_t; +%ignore wchar2char; +%ignore u2cstr; +%ignore c2ustr; +%ignore base64_encode; +%ignore base64_decode; +%ignore utf8_unicode; +%ignore win_utf2idb; +%ignore char2oem; +%ignore oem2char; +%ignore set_codepages; +%ignore get_codepages; +%ignore convert_codepage; +%ignore test_bit; +%ignore set_bit; +%ignore clear_bit; +%ignore set_all_bits; +%ignore clear_all_bits; +%ignore interval::overlap; +%ignore interval::includes; +%ignore interval::contains; +%ignore qrotl; +%ignore qrotr; +%ignore setflag; +%ignore read2bytes; +%ignore rotate_left; +%ignore qswap; +%ignore swap32; +%ignore swap16; +%ignore swap_value; +%ignore qalloc_or_throw; +%ignore qrealloc_or_throw; +%ignore get_buffer_for_sysdir; +%ignore get_buffer_for_winerr; +%ignore call_atexits; +%ignore launch_process_params_t; +%ignore launch_process; +%ignore term_process; +%ignore get_process_exit_code; +%ignore BELOW_NORMAL_PRIORITY_CLASS; +%ignore parse_command_line; +%ignore parse_command_line2; +%rename (parse_command_line2) py_parse_command_line; +%ignore qgetenv; +%ignore qsetenv; +%ignore qctime; +%ignore qlocaltime; +%ignore qstrftime; +%ignore qstrftime64; +%ignore qstrtok; +%ignore qstrlwr; +%ignore qstrupr; +%include "pro.h" + +//--------------------------------------------------------------------- +%template(uvalvec_t) qvector; // vector of unsigned values +%template(intvec_t) qvector; // vector of integers +%template(qstrvec_t) qvector; // vector of strings +%template(boolvec_t) qvector; // vector of bools + +//--------------------------------------------------------------------- +class qstring { +public: + const char *c_str() const { return self->c_str(); } +}; + +//--------------------------------------------------------------------- +// for obscure reasons swig can't get past this one on its own, it needs a dummy declaration. +class strvec_t { +public: + qstring& at(int _idx) { return self->at(_idx).line; } + size_t size() const { return self->size(); } +}; + +class qtype { +public: + const uchar *c_str() const { return self->c_str(); } +}; diff --git a/swig/typeconv.i b/swig/typeconv.i index b0dbb723..71933375 100644 --- a/swig/typeconv.i +++ b/swig/typeconv.i @@ -1,135 +1,145 @@ -// Convert an incoming Python list to a tid_t[] array -%typemap(in) tid_t[ANY](tid_t temp[$1_dim0]) { - int i, len; - - if (!PySequence_Check($input)) - { - PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); - return NULL; - } - - /* Cap the number of elements to copy */ - len = PySequence_Length($input) < $1_dim0 ? PySequence_Length($input) : $1_dim0; - - for (i =0; i < len; i++) - { - PyObject *o = PySequence_GetItem($input,i); - if (!PyLong_Check(o)) - { - Py_XDECREF(o); - PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); - return NULL; - } - - temp[i] = PyLong_AsUnsignedLong(o); - Py_DECREF(o); - } - $1 = &temp[0]; -} - -%define %cstring_output_maxstr_none(TYPEMAP, SIZE) -%typemap (default) SIZE { - $1 = MAXSTR; - } -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = ($1_ltype) qalloc(MAXSTR+1); -} -%typemap(out) ssize_t { - /* REMOVING ssize_t return value in $symname */ -} -%typemap(argout) (TYPEMAP,SIZE) { - if (result > 0) - { - resultobj = PyString_FromString($1); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree($1); -} -%enddef - -%define %cstring_bounded_output_none(TYPEMAP,MAX) -%typemap(in, numinputs=0) TYPEMAP(char temp[MAX+1]) { - $1 = ($1_ltype) temp; -} -%typemap(argout,fragment="t_output_helper") TYPEMAP { - PyObject *o; - $1[MAX] = 0; - - if ($1 > 0) - { - o = PyString_FromString($1); - } - else - { - o = Py_None; - Py_INCREF(Py_None); - } - $result = t_output_helper($result,o); -} -%enddef - -%define %binary_output_or_none(TYPEMAP, SIZE) -%typemap (default) SIZE { - $1 = MAXSPECSIZE; -} -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = (char *) qalloc(MAXSPECSIZE+1); -} -%typemap(out) ssize_t { - /* REMOVING ssize_t return value in $symname */ -} -%typemap(argout) (TYPEMAP,SIZE) { - if (result > 0) - { - resultobj = PyString_FromStringAndSize((char *)$1, result); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree((void *)$1); -} -%enddef - -%define %binary_output_with_size(TYPEMAP, SIZE) -%typemap (default) SIZE { - size_t ressize = MAXSPECSIZE; - $1 = &ressize; -} -%typemap(in,numinputs=0) (TYPEMAP, SIZE) { - $1 = (char *) qalloc(MAXSPECSIZE+1); -} -%typemap(out) ssize_t { - /* REMOVING ssize_t return value in $symname */ -} -%typemap(argout) (TYPEMAP,SIZE) { - if (result) - { - resultobj = PyString_FromStringAndSize((char *)$1, *$2); - } - else - { - Py_INCREF(Py_None); - resultobj = Py_None; - } - qfree((void *)$1); -} -%enddef - - // Check that the argument is a callable Python object -%typemap(in) PyObject *pyfunc { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Expected a callable object"); - return NULL; - } - $1 = $input; -} - +//--------------------------------------------------------------------- +// Convert an incoming Python list to a tid_t[] array +%typemap(in) tid_t[ANY](tid_t temp[$1_dim0]) { + int i, len; + + if (!PySequence_Check($input)) + { + PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); + return NULL; + } + + /* Cap the number of elements to copy */ + len = PySequence_Length($input) < $1_dim0 ? PySequence_Length($input) : $1_dim0; + + for (i =0; i < len; i++) + { + PyObject *o = PySequence_GetItem($input,i); + if (!PyLong_Check(o)) + { + Py_XDECREF(o); + PyErr_SetString(PyExc_ValueError,"Expecting a sequence of long integers"); + return NULL; + } + + temp[i] = PyLong_AsUnsignedLong(o); + Py_DECREF(o); + } + $1 = &temp[0]; +} + +//--------------------------------------------------------------------- +%define %cstring_output_maxstr_none(TYPEMAP, SIZE) + +%typemap (default) SIZE { + $1 = MAXSTR; + } + +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = ($1_ltype) qalloc(MAXSTR+1); +} + +%typemap(out) ssize_t { + /* REMOVING ssize_t return value in $symname */ +} + +%typemap(argout) (TYPEMAP,SIZE) { + if (result > 0) + { + resultobj = PyString_FromString($1); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree($1); +} +%enddef + +//--------------------------------------------------------------------- +%define %cstring_bounded_output_none(TYPEMAP,MAX) +%typemap(in, numinputs=0) TYPEMAP(char temp[MAX+1]) { + $1 = ($1_ltype) temp; +} +%typemap(argout,fragment="t_output_helper") TYPEMAP { + PyObject *o; + $1[MAX] = 0; + + if ($1 > 0) + { + o = PyString_FromString($1); + } + else + { + o = Py_None; + Py_INCREF(Py_None); + } + $result = t_output_helper($result,o); +} +%enddef + +//--------------------------------------------------------------------- +%define %binary_output_or_none(TYPEMAP, SIZE) +%typemap (default) SIZE { + $1 = MAXSPECSIZE; +} +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = (char *) qalloc(MAXSPECSIZE+1); +} +%typemap(out) ssize_t { + /* REMOVING ssize_t return value in $symname */ +} +%typemap(argout) (TYPEMAP,SIZE) { + if (result > 0) + { + resultobj = PyString_FromStringAndSize((char *)$1, result); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree((void *)$1); +} +%enddef + +//--------------------------------------------------------------------- +%define %binary_output_with_size(TYPEMAP, SIZE) +%typemap (default) SIZE { + size_t ressize = MAXSPECSIZE; + $1 = &ressize; +} +%typemap(in,numinputs=0) (TYPEMAP, SIZE) { + $1 = (char *) qalloc(MAXSPECSIZE+1); +} +%typemap(out) ssize_t { + /* REMOVING ssize_t return value in $symname */ +} +%typemap(argout) (TYPEMAP,SIZE) { + if (result) + { + resultobj = PyString_FromStringAndSize((char *)$1, *$2); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree((void *)$1); +} +%enddef + +//--------------------------------------------------------------------- +// Check that the argument is a callable Python object +%typemap(in) PyObject *pyfunc { + if (!PyCallable_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Expected a callable object"); + return NULL; + } + $1 = $input; +} + // Convert ea_t %typemap(in) ea_t {