diff --git a/BUILDING.txt b/BUILDING.txt index 0124c9b..9a55369 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -9,8 +9,8 @@ REQUIREMENTS [Tested versions are in brackets] - - IDA and IDA SDK [5.4] - http://www.datarescue.com/idabase/ + - IDA and IDA SDK [5.6] + http://www.hex-rays.com/idapro/ - Python [2.5.1, 2.6.1] http://www.python.org/ @@ -38,26 +38,26 @@ Make sure all the needed tools (compiler, swig) are on the PATH. 1, Unpack the IDAPython source and IDA Pro SDK into the following directory structure: - swigsdk-versions/5.4/ - version 5.4 of the IDA Pro SDK + swigsdk-versions/5.6/ - version 5.4 of the IDA Pro SDK idapython/ - IDAPython source code -2, Patch the SDK using GNU Patch with one of patches from patches/ directory. - You will have to use the -P option depending on which directory you - patch from. This step is not required if there is no SDK patch. - -3, On Mac OS X copy libida.dylib from the IDA install directory to - swigsdk-versions/5.4/libgcc32.mac/ +2, On Mac OS X copy libida.dylib from the IDA install directory to + swigsdk-versions/5.6/lib/gcc32.mac/ and libida64.dylib to - swigsdk-versions/5.4/libgcc64.mac/ + swigsdk-versions/5.6/lib/gcc64.mac/ -4, Build the plugin +3, Build the plugin python build.py It is possible to build the plugin for different Python versions by - running build.py with the corresponding Python binary. The option - --ea64 builds the 64-bit version as well. + running build.py with the corresponding Python binary. + + Some build options: + --ea64: builds the 64-bit version + --no-early-load: builds the IDAPython plugin w/o PLUGIN_FIX plugin flag + (This flag disables the ability to write file loaders in IDAPython) -5, Install the components as described in README.txt +4, Install the components as described in README.txt See build.py for build details and possible tweaks. diff --git a/README.txt b/README.txt index 50d9fba..b8c5b9a 100644 --- a/README.txt +++ b/README.txt @@ -61,6 +61,15 @@ Start IDA with the following command line options: If you want fully unattended execution mode, make sure your script exits with a qexit() call. +By default scripts run after the database is opened. Extended option +format is: + + -OIDAPython:[N;]script.py + +Where N can be: + 0: run script after opening database (default) + 1: run script when UI is ready + 2: run script immediately on plugin load (shortly after IDA starts and before processor modules and loaders) User init file: @@ -71,7 +80,7 @@ ${HOME}/.idapro/ or -C:\Documents and Settings\%USER%\Application Data\Hex-Rays\IDA Pro +%AppData%\Hex-Rays\IDA Pro The user init file is read and executed at the end of the init process. diff --git a/build.py b/build.py index b491d07..2a6e2a4 100644 --- a/build.py +++ b/build.py @@ -22,7 +22,7 @@ from distutils import sysconfig # Start of user configurable options VERBOSE = True IDA_MAJOR_VERSION = 5 -IDA_MINOR_VERSION = 5 +IDA_MINOR_VERSION = 6 if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] else: @@ -32,8 +32,8 @@ else: # IDAPython version VERSION_MAJOR = 1 -VERSION_MINOR = 2 -VERSION_PATCH = 90 +VERSION_MINOR = 3 +VERSION_PATCH = 0 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) @@ -296,7 +296,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): platform_macros = [ "__LINUX__" ] python_libpath = sysconfig.EXEC_PREFIX + os.sep + "lib" python_library = "-lpython%d.%d" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) - ida_libpath = os.path.join(idasdkdir, ea64 and "libgcc64.lnx" or "libgcc32.lnx") + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "gcc64.lnx" or "gcc32.lnx") ida_lib = "" extra_link_parameters = "" # Platform-specific settings for the Windows build @@ -305,7 +305,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): platform_macros = [ "__NT__" ] python_libpath = sysconfig.EXEC_PREFIX + os.sep + "libs" python_library = "python%d%d.lib" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) - ida_libpath = os.path.join(idasdkdir, ea64 and "libvc.w64" or "libvc.w32") + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "vc.w64" or "vc.w32") ida_lib = "ida.lib" SWIG_OPTIONS += " -D__NT__ " extra_link_parameters = "" @@ -316,7 +316,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): platform_macros = [ "__MAC__" ] python_libpath = "." python_library = "-framework Python" - ida_libpath = os.path.join(idasdkdir, ea64 and "libgcc64.mac" or "libgcc32.mac") + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "gcc64.mac64" or "gcc32.mac") ida_lib = ea64 and "-lida64" or "-lida" extra_link_parameters = "" @@ -326,7 +326,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): if ea64: platform_macros.append("__EA64__") - if '--early-load' in sys.argv: + if not '--no-early-load' in sys.argv: platform_macros.append("PLUGINFIX") # Build the wrapper from the interface files @@ -394,7 +394,7 @@ def build_binary_package(ea64, nukeold): binmanifest = [] if nukeold: binmanifest.extend(BINDIST_MANIFEST) - binmanifest.extend([(x, ea64 and "python64" or "python") for x in "python/init.py", "python/idc.py", "python/idautils.py", "idaapi.py"]) + binmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py", "idaapi.py"]) binmanifest.append((plugin_name, "plugins")) build_distribution(binmanifest, BINDISTDIR, ea64, nukeold) diff --git a/python.cpp b/python.cpp index 65688b1..132a90d 100644 --- a/python.cpp +++ b/python.cpp @@ -48,15 +48,20 @@ extern "C" #define IDAPYTHON_DATA_STATEMENT 0 -#ifdef __EA64__ -#define PYTHON_DIR_NAME "python64" -#else #define PYTHON_DIR_NAME "python" -#endif void init_idaapi(void); void idaapi run(int arg); static int initialized = 0; +//-------------------------------------------------------------------------- +// Some utility functions from pywraps / idaapi +int idcvar_to_pyvar(const idc_value_t &idc_var, PyObject **py_var); +int pyvar_to_idcvar(PyObject *py_var, idc_value_t *idc_var, int *gvar_sn = NULL); +PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr); +bool PyGetError(qstring *out = NULL); +bool init_pywraps(); +void deinit_pywraps(); +//-------------------------------------------------------------------------- /* This is a simple tracing code for debugging purposes. */ /* It might evolve into a tracing facility for user scripts. */ @@ -141,34 +146,99 @@ void end_execution() #endif } +/* Return a formatted error or just print it to the console */ +static void handle_python_error(char *errbuf, size_t errbufsize) +{ + PyObject *result; + PyObject *ptype, *pvalue, *ptraceback; + + if ( errbufsize > 0 ) + errbuf[0] = '\0'; + + if (PyErr_Occurred()) + { + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + result = PyObject_Repr(pvalue); + if (result) + { + qsnprintf(errbuf, errbufsize, "ERROR: %s", PyString_AsString(result)); + PyErr_Clear(); + Py_XDECREF(ptype); + Py_XDECREF(pvalue); + Py_XDECREF(ptraceback); + } + else + PyErr_Print(); + } +} + +/* helper function to get globals for the __main__ module */ +PyObject *GetMainGlobals() +{ + PyObject *module = PyImport_AddModule("__main__"); + if (module == NULL) + return NULL; + + return PyModule_GetDict(module); +} + /* Simple Python statement runner function for IDC */ -static const char idc_runpythonstatement_args[] = { VT_STR, 0 }; +static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res) { - begin_execution(); - res->num = PyRun_SimpleString(argv[0].str); - end_execution(); + PyObject *globals = GetMainGlobals(); + if (globals == NULL) + { + res->set_string("internal error"); + } + else + { + PyErr_Clear(); + begin_execution(); + PyObject *result = PyRun_String(argv[0].c_str(), Py_file_input, globals, globals ); + end_execution(); + Py_XDECREF(result); + if ( result == NULL || PyErr_Occurred() ) + { + char errbuf[MAXSTR]; + handle_python_error(errbuf, sizeof(errbuf)); + *res = idc_value_t(errbuf); + if ( errbuf[0] == '\0' ) + res->set_string("internal error"); + else + res->set_string(errbuf); + } + else + { + // success + res->set_long(0); + } + } return eOk; } /* QuickFix for the FILE* incompatibility problem */ int ExecFile(const char *FileName) { - PyObject* PyFileObject = PyFile_FromString((char*)FileName, "r"); + PyObject *PyFileObject = PyFile_FromString((char*)FileName, "r"); - if (!PyFileObject) + PyObject *globals = GetMainGlobals(); + if (globals == NULL) return 0; - if (PyRun_SimpleFile(PyFile_AsFile(PyFileObject), FileName) == 0) + PyErr_Clear(); + PyObject *result = PyRun_File(PyFile_AsFile(PyFileObject), FileName, Py_file_input, globals, globals); + + Py_XDECREF(PyFileObject); + Py_XDECREF(result); + if ( result == NULL || PyErr_Occurred() ) { - Py_DECREF(PyFileObject); - return 1; - } - else - { - Py_DECREF(PyFileObject); - return 0; + if ( !PyErr_Occurred() ) + PyErr_Print(); + return 0; } + + return 1; } /* Check for the presence of a file in IDADIR/python */ @@ -188,11 +258,11 @@ bool CheckFile(char *filename) /* Execute the Python script from the plugin */ /* Default hotkey: Alt-9 */ -void IDAPython_RunScript(char *script) +void IDAPython_RunScript(const char *script) { char statement[MAXSTR+32]; char slashpath[MAXSTR+1]; - char *scriptpath; + const char *scriptpath; int i; @@ -255,15 +325,13 @@ void IDAPython_RunStatement(void) /* Default hotkey: Alt-7 */ void IDAPython_ScriptBox(void) { - PyObject *module; PyObject *dict; PyObject *scriptbox; PyObject *pystr; /* Get globals() */ - /* These two should never fail */ - module = PyImport_AddModule("__main__"); - dict = PyModule_GetDict(module); + /* This should never fail */ + dict = GetMainGlobals(); scriptbox = PyDict_GetItemString(dict, "scriptbox"); @@ -296,37 +364,52 @@ void IDAPython_ScriptBox(void) bool idaapi IDAPython_Menu_Callback(void *ud) { - run((int)ud); + run((size_t)ud); return true; } -/* Return a formatted error or just print it to the console */ -static void handle_python_error(char *errbuf, size_t errbufsize) + +//-------------------------------------------------------------------------- +// This function parses a name into two different components (if it applies). +// The first mode of operation: +// split_mod_attr_name("modname.attrname", mod_buf, attr_buf) +// It splits the full name into two parts. +// +// The second mode of operation: +// split_mod_attr_name("c:\libs\x.py", file_name_buf, NULL) +// +static bool parse_py_modname( + const char *full_name, + char *modname, + char *attrname, size_t sz) { - PyObject *result; - PyObject *ptype, *pvalue, *ptraceback; - - if ( errbufsize > 0 ) - errbuf[0] = '\0'; - - if (PyErr_Occurred()) + if (attrname == NULL) + { + // take the filename.ext part + qstrncpy(modname, qbasename(full_name), sz); + // take the filename part only + qsplitfile(modname, NULL, NULL); + return true; + } + else + { + const char *p = strchr(full_name, '.'); + if (p == NULL) { - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - result = PyObject_Repr(pvalue); - if (result) - { - qsnprintf(errbuf, errbufsize, "ERROR: %s", PyString_AsString(result)); - PyErr_Clear(); - Py_XDECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - } - else - PyErr_Print(); + qstrncpy(modname, "idaapi", sz); + qstrncpy(attrname, full_name, sz); } + else + { + qstrncpy(modname, full_name, p - full_name + 1); + qstrncpy(attrname, p + 1, sz); + } + return p != NULL; + } } -/* Convert return value from Python to IDC or report about an error */ +/* Convert return value from Python to IDC or report about an error. */ +/* This function also decrements the reference "result" (python variable) */ static bool return_python_result(idc_value_t *rv, PyObject *result, char *errbuf, @@ -340,48 +423,24 @@ static bool return_python_result(idc_value_t *rv, handle_python_error(errbuf, errbufsize); return false; } - - if (PyInt_Check(result)) + bool ok = true; + if (pyvar_to_idcvar(result, rv) <= 0) { - rv->num = PyInt_AsLong(result); - rv->vtype = VT_LONG; - Py_XDECREF(result); - return true; + qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); + ok = false; } - - if (PyString_Check(result)) - { - rv->str = (char *)qalloc(PyString_Size(result)+1); - if (!rv->str) - return false; - qstrncpy(rv->str, PyString_AsString(result), MAXSTR); - rv->vtype = VT_STR; - Py_XDECREF(result); - return true; - } - - if (PyFloat_Check(result)) - { - double dresult = PyFloat_AsDouble(result); - ieee_realcvt((void *)&dresult, rv->e, 3); - rv->vtype = VT_FLOAT; - Py_XDECREF(result); - return true; - } - - qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); - return false; + Py_XDECREF(result); + return 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) + ea_t /*current_ea*/, + const char *expr, + char *errbuf, + size_t errbufsize) { - PyObject *main = PyImport_AddModule("__main__"); QASSERT(main != NULL); - PyObject *globals = PyModule_GetDict(main); QASSERT(globals != NULL); + PyObject *globals = GetMainGlobals(); QASSERT(globals != NULL); PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); if (code == NULL) @@ -413,77 +472,211 @@ bool idaapi IDAPython_extlang_compile(const char *name, /* 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) + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) { // convert arguments to python qvector pargs; + qvector do_decref; - for (int i=0; i 0; + // decrement reference only if not an opaque object + if (r == 1) + Py_DECREF(py_res); + } while (false); + + Py_XDECREF(py_mod); + Py_XDECREF(py_cls); + + // Free the arguments tuple + if (py_args != NULL) + Py_DECREF(py_args); + + return ok; +} + +// Returns: success + /* Calculator callback for Python external language evaluator */ bool idaapi IDAPython_extlang_calcexpr(ea_t /*current_ea*/, const char *expr, @@ -492,18 +685,16 @@ bool idaapi IDAPython_extlang_calcexpr(ea_t /*current_ea*/, size_t errbufsize) { PyObject *result; - PyObject *module = PyImport_AddModule("__main__"); - if (module == NULL) + PyObject *globals = GetMainGlobals(); + if (globals == NULL) return false; - PyObject *globals = PyModule_GetDict(module); - - begin_execution(); + begin_execution(); result = PyRun_String(expr, Py_eval_input, globals, globals); - end_execution(); + end_execution(); - VarFree(rv); + rv->clear(); return return_python_result(rv, result, errbuf, errbufsize); } @@ -517,25 +708,52 @@ extlang_t extlang_python = IDAPython_extlang_run, IDAPython_extlang_calcexpr, IDAPython_extlang_compile_file, - "py" + "py", + IDAPython_extlang_create_object }; void enable_extlang_python(bool enable) { +#if IDA_SDK_VERSION < 560 +#define SELECT_EXTLANG register_extlang +#else +#define SELECT_EXTLANG select_extlang +#endif if (enable) - register_extlang(&extlang_python); + SELECT_EXTLANG(&extlang_python); else - register_extlang(NULL); + SELECT_EXTLANG(NULL); +#undef SELECT_EXTLANG } #if IDA_SDK_VERSION >= 540 /* Execute a line in the Python CLI */ bool idaapi IDAPython_cli_execute_line(const char *line) { - begin_execution(); - PyRun_SimpleString(line); - end_execution(); - return true; + const char *first_line = strrchr(line, '\n'); + if (first_line == NULL) + first_line = line; + else + first_line += 1; + + // skip empty lines + if (first_line[0] != '\0') + { + // take a copy of the line so we r-trim it + char *tline = qstrdup(first_line); + trim(tline); + // line ends with ":" or begins with a space character? + bool more = tline[qstrlen(tline)-1] == ':' || isspace(first_line[0]); + qfree(tline); + + if ( more ) + return false; + } + begin_execution(); + PyRun_SimpleString(line); + end_execution(); + + return true; } cli_t cli_python = @@ -560,6 +778,12 @@ void enable_python_cli(bool enable) } #endif +/* Prints the IDAPython copyright banner */ +void print_banner() +{ + PyRun_SimpleString("print_banner()"); +} + /* Install python menu items */ static void install_python_menus() { @@ -593,22 +817,65 @@ static void install_python_menus() (void *)IDAPYTHON_SCRIPTBOX); } +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) +}; + +static int g_run_when = -1; +static char g_run_script[QMAXPATH]; + +/* Parse plugin options */ +void parse_options() +{ + const char *options = get_plugin_options("IDAPython"); + if ( options == NULL ) + return; + const char *p = strchr(options, ';'); + if ( p == NULL ) + { + g_run_when = run_on_db_open; + qstrncpy(g_run_script, options, sizeof(g_run_script)); + } + else + { + g_run_when = atoi(options); + qstrncpy(g_run_script, p+1, sizeof(g_run_script)); + } +} + /* 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) { - if ( code != ui_ready_to_run ) - return 0; + const char *script; + int when; - install_python_menus(); - unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); + switch ( code ) + { + case ui_ready_to_run: + print_banner(); + install_python_menus(); + + if ( g_run_when == run_on_ui_ready ) + IDAPython_RunScript(g_run_script); + break; + + case ui_database_inited: + if ( g_run_when == run_on_db_open ) + IDAPython_RunScript(g_run_script); + break; + + default: + break; + } return 0; } /* Initialize the Python environment */ bool IDAPython_Init(void) { - char *options; char tmp[MAXSTR+64]; bool result = true; @@ -631,9 +898,9 @@ bool IDAPython_Init(void) qsnprintf(tmp, sizeof(tmp), "libpython%d.%d.so", PY_MAJOR_VERSION, PY_MINOR_VERSION); - if (!dlopen(tmp, RTLD_NOLOAD | RTLD_GLOBAL)) + if (!dlopen(tmp, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY)) { - warning("IDAPython: dlopen(%s) failed", tmp); + warning("IDAPython: %s", dlerror()); return false; } #endif @@ -648,7 +915,6 @@ bool IDAPython_Init(void) /* Init the SWIG wrapper */ init_idaapi(); - /* Set IDAPYTHON_VERSION in Python */ qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)", \ VER_MAJOR, @@ -656,32 +922,41 @@ bool IDAPython_Init(void) VER_PATCH, VER_STATUS, VER_SERIAL); - begin_execution(); + begin_execution(); PyRun_SimpleString(tmp); - end_execution(); + end_execution(); /* Pull in the Python side of init */ qmakepath(tmp, MAXSTR, idadir(PYTHON_DIR_NAME), "init.py", NULL); if (!ExecFile(tmp)) { - warning("IDAPython: error executing init.py"); + handle_python_error(tmp, sizeof(tmp)); + warning("IDAPython: error executing init.py:\n%s", tmp); return false; } + /* Init pywraps (hand made/custom wrapper) */ + if (!init_pywraps()) + { + warning("IDAPython: init_pywraps() failed!"); + return false; + } + #ifdef ENABLE_PYTHON_PROFILING PyEval_SetTrace(tracefunc, NULL); #endif /* Batch-mode operation: */ /* A script specified on the command line is run */ - options = (char *)get_plugin_options("IDAPython"); - if (options) - IDAPython_RunScript(options); + parse_options(); + if ( g_run_when == run_on_init ) + IDAPython_RunScript(g_run_script); #ifdef PLUGINFIX hook_to_notification_point(HT_UI, menu_installer_cb, NULL); #else install_python_menus(); + print_banner(); #endif /* Register a RunPythonStatement() function for IDC */ set_idc_func("RunPythonStatement", idc_runpythonstatement, idc_runpythonstatement_args); @@ -691,6 +966,10 @@ bool IDAPython_Init(void) enable_python_cli(true); #endif +#if IDA_SDK_VERSION >= 560 + install_extlang(&extlang_python); +#endif + initialized = 1; return true; @@ -714,8 +993,14 @@ void IDAPython_Term(void) #endif /* Remove the extlang */ +#if IDA_SDK_VERSION >= 560 + remove_extlang(&extlang_python); +#else register_extlang(NULL); +#endif + /* De-init pywraps */ + deinit_pywraps(); /* Shut the interpreter down */ Py_Finalize(); diff --git a/python/idc.py b/python/idc.py index 999aa91..790e576 100644 --- a/python/idc.py +++ b/python/idc.py @@ -438,10 +438,12 @@ def Eval(expr): if err: return "IDC_FAILURE: "+err else: - if rv.vtype == '\x01': # string + if rv.vtype == '\x01': # VT_STR return rv.str elif rv.vtype == '\x02': # long return rv.num + elif rv.vtype == '\x07': # VT_STR2 + return rv.c_str() else: raise NotImplementedError, "Eval() supports only expressions returning strings or longs" @@ -6165,6 +6167,13 @@ def GetType(ea): """ return idaapi.idc_get_type(ea) +def SizeOf(typestr): + """ + Returns the size of the type. It is equivalent to IDC's sizeof(). + Use name, tp, fld = idc.ParseType() ; Sizeof(fld) to retrieve the size + @return: -1 if typestring is not valid otherwise the size of the type + """ + return idaapi.get_type_size0(idaapi.cvar.idati, typestr) def GuessType(ea): """ @@ -6191,6 +6200,16 @@ def SetType(ea, newtype): """ return idaapi.apply_cdecl(ea, newtype) +def ParseType(inputtype, flags): + """ + Parse type declaration + + @param inputtype: file name or C declarations (depending on the flags) + @param flags: combination of PT_... constants or 0 + + @return: None on failure or (name, type, fields) tuple + """ + return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags) def ParseTypes(inputtype, flags): """ diff --git a/python/init.py b/python/init.py index 89c7773..21e144a 100644 --- a/python/init.py +++ b/python/init.py @@ -113,18 +113,12 @@ sys.stdout = sys.stderr = MyStdOut() sys.argv = [ "" ] # Have to make sure Python finds our modules -if _idaapi.idainfo_is_64bit(_idaapi.cvar.inf): - pythonDir = "python64" -else: - pythonDir = "python" -sys.path.append(_idaapi.idadir(pythonDir)) - -print_banner() +sys.path.append(_idaapi.idadir("python")) #----------------------------------------------------------- # Import all the required modules #----------------------------------------------------------- -from idaapi import Choose, get_user_idadir, cvar, Choose2 +from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall from idc import * from idautils import * import idaapi diff --git a/swig/bytes.i b/swig/bytes.i index aad50b5..0044bb1 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -22,8 +22,8 @@ %ignore clrFlbits; %ignore get_8bit; %ignore get_ascii_char; -%ignore del_typeinfo; -%ignore del_operand_typeinfo; +%ignore del_opinfo; +%ignore del_one_opinfo; %ignore doCode; %ignore get_repeatable_cmt; %ignore get_any_indented_cmt; @@ -72,11 +72,11 @@ %clear(const void *buf, size_t size); %clear(void *buf, ssize_t size); -%clear(typeinfo_t *); +%clear(opinfo_t *); %rename (nextthat) py_nextthat; %rename (prevthat) py_prevthat; - + %{ // //------------------------------------------------------------------------ diff --git a/swig/dbg.i b/swig/dbg.i index 3692e22..b1fe1ef 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -10,7 +10,7 @@ typedef struct %rename (get_manual_regions) py_get_manual_regions; %ignore set_manual_regions; %include "dbg.hpp" - +%ignore DBG_Callback; %feature("director") DBG_Hooks; %{ @@ -250,7 +250,7 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va) return 0; } } - catch (Swig::DirectorException &e) + catch (Swig::DirectorException &) { msg("Exception in IDP Hook function:\n"); if (PyErr_Occurred()) diff --git a/swig/diskio.i b/swig/diskio.i index 8362b87..800f04f 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -27,6 +27,7 @@ %ignore qlfile; %ignore make_linput; %ignore unmake_linput; +%ignore create_remote_linput; // FIXME: These should be wrapped for completeness %ignore eread; @@ -44,6 +45,7 @@ %{ // +//-------------------------------------------------------------------------- int idaapi py_enumerate_files_cb(const char *file, void *ud) { PyObject *py_file = PyString_FromString(file); @@ -72,6 +74,12 @@ private: OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close }; + //-------------------------------------------------------------------------- + void _from_cobject(PyObject *pycobject) + { + this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); + } + //-------------------------------------------------------------------------- void assign(const loader_input_t &rhs) { @@ -79,19 +87,30 @@ private: li = rhs.li; own = OWN_FROM_LI; } -public: + //-------------------------------------------------------------------------- loader_input_t(const loader_input_t &rhs) { assign(rhs); } - +public: + // Special attribute that tells the pyvar_to_idcvar how to convert this + // class from and to IDC. The value of this variable must be set to two + int __idc_cvt_id__; //-------------------------------------------------------------------------- - loader_input_t() + loader_input_t(PyObject *pycobject = NULL) { - li = NULL; - own = OWN_NONE; - fn.qclear(); + __idc_cvt_id__ = 2; // Opaque object + if (pycobject != NULL && PyCObject_Check(pycobject)) + { + _from_cobject(pycobject); + } + else + { + li = NULL; + own = OWN_NONE; + fn.qclear(); + } } //-------------------------------------------------------------------------- @@ -152,7 +171,7 @@ public: if (!PyCObject_Check(pycobject)) return NULL; loader_input_t *l = new loader_input_t(); - l->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); + l->_from_cobject(pycobject); return l; } @@ -285,6 +304,12 @@ public: Py_RETURN_NONE; } + //-------------------------------------------------------------------------- + int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) + { + return ::file2base(li, pos, ea1, ea2, patchable); + } + //-------------------------------------------------------------------------- int32 size() { @@ -308,6 +333,7 @@ public: }; +//-------------------------------------------------------------------------- PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) { do diff --git a/swig/expr.i b/swig/expr.i index b091177..5ab1175 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -2,6 +2,8 @@ %ignore funcset_t; %ignore extlang_t; %ignore extlang; +%ignore extlangs_t; +%ignore extlangs; %ignore register_extlang; %ignore IDCFuncs; %ignore set_idc_func; @@ -28,6 +30,8 @@ %ignore find_builtin_idc_func; %ignore idc_lx; %ignore idc_vars; +%ignore idc_resolve_label; +%ignore idc_resolver_ea; %cstring_output_maxstr_none(char *errbuf, size_t errbufsize); @@ -65,7 +69,7 @@ bool calc_idc_expr_wrap(ea_t where,const char *line, idc_value_t *rv, char *errb return !calc_idc_expr(where, line, rv, errbuf, errbufsize); } %} - + %ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL); %rename (CompileLine) CompileLine_wrap; diff --git a/swig/fpro.i b/swig/fpro.i index a3c7011..0f47ffe 100644 --- a/swig/fpro.i +++ b/swig/fpro.i @@ -7,13 +7,29 @@ private: bool own; qstring fn; + //-------------------------------------------------------------------------- void assign(const qfile_t &rhs) { fn = rhs.fn; fp = rhs.fp; own = false; } + //-------------------------------------------------------------------------- + bool _from_fp(FILE *fp) + { + if (fp == NULL) + return false; + own = false; + fn.sprnt("", fp); + fp = fp; + return true; + } + inline void _from_cobject(PyObject *pycobject) + { + _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); + } public: + int __idc_cvt_id__; //-------------------------------------------------------------------------- qfile_t(const qfile_t &rhs) { @@ -21,11 +37,14 @@ public: } //-------------------------------------------------------------------------- - qfile_t() + qfile_t(PyObject *pycobject = NULL) { fp = NULL; own = true; fn.qclear(); + __idc_cvt_id__ = 2; // Opaque object + if (pycobject != NULL && PyCObject_Check(pycobject)) + _from_cobject(pycobject); } //-------------------------------------------------------------------------- diff --git a/swig/gdl.i b/swig/gdl.i index f617ecb..46cbaf8 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -35,6 +35,7 @@ } %pythoncode %{ +# # ----------------------------------------------------------------------- class BasicBlock: def __init__(self, id, bb, f): @@ -96,5 +97,5 @@ class FlowChart: if index >= self.size: raise StopIteration return BasicBlock(index, self._q[index], self) - +# %} diff --git a/swig/graph.i b/swig/graph.i index 48ee34f..ffd8a03 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -481,12 +481,6 @@ private: //grcode_user_draw, // render node of a user-defined graph return ret; } - static PyObject *PyObject_TryGetAttrString(PyObject *object, const char *attr) - { - if (!PyObject_HasAttrString(object, attr)) - return NULL; - return PyObject_GetAttrString(object, attr); - } void unbind() { @@ -494,7 +488,9 @@ private: return; // Unbind this object from the python object - PyObject_SetAttrString(self, S_M_THIS, PyCObject_FromVoidPtr(NULL, NULL)); + PyObject *py_cobj = PyCObject_FromVoidPtr(NULL, NULL); + PyObject_SetAttrString(self, S_M_THIS, py_cobj); + Py_DECREF(py_cobj); Py_XDECREF(self); self = NULL; } @@ -600,10 +596,15 @@ private: Py_XDECREF(attr); } - // Bind py_graph_t to python object + + // Keep a reference this->self = self; Py_INCREF(self); - PyObject_SetAttrString(self, S_M_THIS, PyCObject_FromVoidPtr(this, NULL)); + + // Bind py_graph_t to python object + PyObject *py_cobj = PyCObject_FromVoidPtr(this, NULL); + PyObject_SetAttrString(self, S_M_THIS, py_cobj); + Py_DECREF(py_cobj); // Create form HWND hwnd = NULL; diff --git a/swig/ida.i b/swig/ida.i index a0849ff..77709eb 100644 --- a/swig/ida.i +++ b/swig/ida.i @@ -4,6 +4,9 @@ %ignore idainfo::retrieve; %ignore idainfo::read; %ignore idainfo::write; +%ignore idainfo::align_short_demnames; +%ignore idainfo::align_strtype; +%ignore idainfo::align_long_demnames; %ignore setflag(uchar &where,uchar bit,int value); %ignore setflag(ushort &where,ushort bit,int value); diff --git a/swig/idaapi.i b/swig/idaapi.i index f7ddf11..8b0de8c 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -45,24 +45,697 @@ #include "typeinf.hpp" #include "ua.hpp" #include "xref.hpp" +#include "ieee.h" +#include "err.h" #include "fpro.h" #include #ifdef __NT__ #include "graph.hpp" #endif + +// + +#ifndef PYUL_DEFINED +#define PYUL_DEFINED + typedef unsigned PY_LONG_LONG pyul_t; +#endif + +//------------------------------------------------------------------------ +static const char PY_IDC_CLASS_NAME[] = "py_idc_object_class"; +static const char PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; +static const char PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; +static const char PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; +static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; + +#ifndef __PYWRAPS__ +static const char S_PY_IDAAPI_MODNAME[] = "idaapi"; +#else +static const char S_PY_IDAAPI_MODNAME[] = "__main__"; +#endif + +#define PY_CLSID_CVT_INT64 0 +#define PY_CLSID_APPCALL_SKEL_OBJ 1 +#define PY_CLSID_CVT_BYREF 2 +#define PY_CLSID_LAST 3 + +//------------------------------------------------------------------------ +// PyIdc conversion object ids +#define PY_ICID_INT64 0 +#define PY_ICID_BYREF 1 +#define PY_ICID_OPAQUE 2 + +static PyObject *py_cvt_helper_module = NULL; +static bool pywraps_initialized = false; + +//------------------------------------------------------------------------ +idc_class_t *py_idc_cvt_opaque_t = NULL; +static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; +static error_t idaapi py_idc_opaque_dtor( + idc_value_t *argv, + idc_value_t *res) +{ + // Get the value from the object + idc_value_t idc_val; + VarGetAttr(&argv[0], PY_IDCCVT_VALUE_ATTR, &idc_val); + + // Extract the Python object reference + PyObject *py_obj = (PyObject *)idc_val.pvoid; + // Decrease its reference (and eventually destroy it) + Py_DECREF(py_obj); + return eOk; +} + +//------------------------------------------------------------------------ +// This function must be called on initialization +bool init_pywraps() +{ + if (pywraps_initialized) + return true; + + // Take a reference to the idaapi python module + // (We need it to create instances of certain classes) + if (py_cvt_helper_module == NULL) + { + // Take a reference to the module so we can create the needed class instances + py_cvt_helper_module = PyImport_ImportModule(S_PY_IDAAPI_MODNAME); + if (py_cvt_helper_module == NULL) + return false; + } + + if (py_idc_cvt_opaque_t == NULL) + { + // Add the class + py_idc_cvt_opaque_t = add_idc_class(S_PY_IDC_OPAQUE_T); + if (py_idc_cvt_opaque_t == NULL) + return false; + + // Form the dtor name + char dtor_name[MAXSTR]; + qstrncpy(dtor_name, S_PY_IDC_OPAQUE_T, sizeof(dtor_name)); + qstrncat(dtor_name, ".dtor", sizeof(dtor_name)); + + // register the dtor function + if (!set_idc_func(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args)) + return false; + + // Link the dtor function to the class + set_idc_dtor(py_idc_cvt_opaque_t, dtor_name); + } + pywraps_initialized = true; + return true; +} + +//------------------------------------------------------------------------ +// This function must be called on de-initialization +void deinit_pywraps() +{ + if (!pywraps_initialized) + return; + pywraps_initialized = false; + Py_XDECREF(py_cvt_helper_module); + py_cvt_helper_module = NULL; +} +//------------------------------------------------------------------------ +// Gets a class type reference in idaapi +// With the class type reference we can create a new instance of that type +// This function takes a reference to the idaapi module and keeps the reference +static PyObject *get_idaapi_class_reference(const int class_id) +{ + if (class_id >= PY_CLSID_LAST) + return NULL; + + // Some class names. The array is parallel with the PY_CLSID_xxx consts + static const char *class_names[]= + { + "PyIdc_cvt_int64__", + "Appcall_object__", + "PyIdc_cvt_refclass__" + }; + + return PyObject_GetAttrString(py_cvt_helper_module, class_names[class_id]); +} + +//------------------------------------------------------------------------ +// Returns an attribute or NULL +// No errors will be set if the attribute did not exist +PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr) +{ + if (!PyObject_HasAttrString(py_var, attr)) + return NULL; + return PyObject_GetAttrString(py_var, attr); +} + +//------------------------------------------------------------------------- +// Converts a Python number into an IDC value (32 or 64bits) +// The function will first try to convert the number into a 32bit value +// If the number does not fit then VT_INT64 will be used +// NB: This function cannot properly detect if the Python value should be +// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which +// can fit in a C long but Python creates a PyLong object for it. +// And because of that we are confused as to whether to convert to 32 or 64 +bool PyGetNumber(PyObject *py_var, idc_value_t *idc_var) +{ + if (!(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var))) + return false; + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if (!PyErr_Occurred()) + { + idc_var->set_long(l); + return true; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if (!PyErr_Occurred()) + { + idc_var->set_long(l); + return true; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + return true; +} + +//------------------------------------------------------------------------- +// Checks if a given object is of sequence type +bool PyIsSequenceType(PyObject *obj) +{ + if (!PySequence_Check(obj)) + return false; + Py_ssize_t sz = PySequence_Size(obj); + if (sz == -1 || PyErr_Occurred() != NULL) + { + PyErr_Clear(); + return false; + } + return true; +} + +//------------------------------------------------------------------------- +// Returns the string representation of an object +bool PyObjectToString(PyObject *obj, qstring *out) +{ + PyObject *py_str = PyObject_Str(obj); + if (py_str != NULL) + { + *out = PyString_AsString(py_str); + Py_DECREF(py_str); + return true; + } + else + { + out->qclear(); + return false; + } +} + +//-------------------------------------------------------------------------- +// Checks if a Python error occured and fills the out parameter with the +// exception string +bool PyGetError(qstring *out = NULL) +{ + PyObject *py_err; + if ( (py_err = PyErr_Occurred()) == NULL) + return false; + + PyObject *err_type, *err_value, *err_traceback; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + if ( out != NULL ) + PyObjectToString(err_value, out); + return true; +} + +//------------------------------------------------------------------------- +// Checks if the given py_var is a special PyIdc_cvt_helper object. +// It does that by examining the magic attribute and returns its numeric value. +// It returns -1 if the object is not a recognized helper object. +// Any Python object can be treated as an cvt object if this attribute is created. +static int get_pyidc_cvt_type(PyObject *py_var) +{ + // Check if this our special by reference object + PyObject *attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_ID_ATTR); + if (attr == NULL) + return -1; + if (!(PyInt_Check(attr) || PyLong_Check(attr))) + { + Py_DECREF(attr); + return -1; + } + int r = (int)PyInt_AsLong(attr); + Py_DECREF(attr); + return r; +} + +//------------------------------------------------------------------------- +// Utility function to create opaque / convertible Python <-> IDC variables +// The referred Python variable will have its reference increased +static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) +{ + // Create an IDC object of this special helper class + if (VarObject(idc_var, py_idc_cvt_opaque_t) != eOk) + return false; + + // Store the CVT id + idc_value_t idc_val; + idc_val.set_long(PY_ICID_OPAQUE); + VarSetAttr(idc_var, PY_IDCCVT_ID_ATTR, &idc_val); + + // Store the value as a PVOID referencing the given Python object + idc_val.set_pvoid(py_var); + VarSetAttr(idc_var, PY_IDCCVT_VALUE_ATTR, &idc_val); + + return true; +} + +//------------------------------------------------------------------------- +// Converts a Python variable into an IDC variable +// This function returns: +// 0 - failure +// 1 - success +// 2 - success but do not decrement the reference of the py_var (used by opaque values) +int pyvar_to_idcvar( + PyObject *py_var, + idc_value_t *idc_var, + int *gvar_sn = NULL) +{ + PyObject *attr; + // None / NULL + if (py_var == NULL || py_var == Py_None) + idc_var->set_long(0); + // Numbers? + else if (PyGetNumber(py_var, idc_var)) + return 1; + // String + else if (PyString_Check(py_var)) + idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var)+1); + // Float + else if (PyBool_Check(py_var)) + idc_var->set_long(py_var == Py_True ? 1 : 0); + // Boolean + else if (PyFloat_Check(py_var)) + { + double dresult = PyFloat_AsDouble(py_var); + ieee_realcvt((void *)&dresult, idc_var->e, 3); + idc_var->vtype = VT_FLOAT; + } + // void* + else if (PyCObject_Check(py_var)) + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var)); + else if (PyList_CheckExact(py_var) || PyIsSequenceType(py_var)) + { + // Create the object + VarObject(idc_var); + + // Determine list size and type + bool is_seq = !PyList_CheckExact(py_var); + Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var); + bool ok = true; + qstring attr_name; + + // Convert each item + for (Py_ssize_t i=0;i 0; + if (ok) + { + // Form the attribute name + PyObject *py_int = PyInt_FromSsize_t(i); + ok = PyObjectToString(py_int, &attr_name); + if (!ok) + break; + Py_DECREF(py_int); + // Store the attribute + VarSetAttr(idc_var, attr_name.c_str(), &v); + } + // Sequences return a new reference for GetItem() + if (is_seq) + Py_DECREF(py_var); + if (!ok) + break; + } + return ok ? 1 : 0; + } + // Dictionary: we convert to an IDC object + else if (PyDict_Check(py_var)) + { + // Create an empty IDC object + VarObject(idc_var); + + // Get the dict.items() list + PyObject *py_items = PyDict_Items(py_var); + + // Get the size of the list + qstring key_name; + bool ok = true; + Py_ssize_t size = PySequence_Size(py_items); + for (Py_ssize_t i=0;i (key, value) + PyObject *py_item = PyList_GetItem(py_items, i); + + // Extract key/value + PyObject *key = PySequence_GetItem(py_item, 0); + PyObject *val = PySequence_GetItem(py_item, 1); + + // Get key's string representation + PyObjectToString(key, &key_name); + + // Convert the attribute into an IDC value + idc_value_t v; + ok = pyvar_to_idcvar(val, &v, gvar_sn) > 0; + if (ok) + { + // Store the attribute + VarSetAttr(idc_var, key_name.c_str(), &v); + } + Py_XDECREF(key); + Py_XDECREF(val); + if (!ok) + break; + } + // Decrement attribute reference + Py_DECREF(py_items); + return ok ? 1 : 0; + } + // Objects: + // - pyidc_cvt objects: int64, byref, opaque + // - other python objects + else + { + // Get the type + int cvt_id = get_pyidc_cvt_type(py_var); + switch (cvt_id) + { + // + // INT64 + // + case PY_ICID_INT64: + // Get the value attribute + attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_VALUE_ATTR); + if (attr == NULL) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr)); + Py_DECREF(attr); + return 1; + // + // BYREF + // + case PY_ICID_BYREF: + { + // BYREF always require this parameter + if (gvar_sn == NULL) + return 0; + + // Get the value attribute + attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_VALUE_ATTR); + if (attr == NULL) + return 0; + + // Create a global variable + char buf[MAXSTR]; + qsnprintf(buf, sizeof(buf), PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); + idc_value_t *gvar = add_idc_gvar(buf); + // Convert the python value into the IDC global variable + bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) > 0; + if (ok) + { + (*gvar_sn)++; + // Create a reference to this global variable + VarRef(idc_var, gvar); + } + Py_DECREF(attr); + return ok ? 1 : 0; + } + // + // OPAQUE + // + case PY_ICID_OPAQUE: + { + if (!create_py_idc_opaque_obj(py_var, idc_var)) + return 0; + return 2; // do not decrement the reference + } + // + // Other objects + // + default: + // A normal object? + PyObject *py_dir = PyObject_Dir(py_var); + Py_ssize_t size = PyList_Size(py_dir); + if (py_dir == NULL || !PyList_Check(py_dir) || size == 0) + { + Py_XDECREF(py_dir); + return 0; + } + // Create the IDC object + VarObject(idc_var); + for (Py_ssize_t i=0;i 2 ) + && (strncmp(field_name, "__", 2) == 0 ) + && (strncmp(field_name+len-2, "__", 2) == 0) ) + { + continue; + } + idc_value_t v; + // Get the non-private attribute from the object + attr = PyObject_GetAttrString(py_var, field_name); + if (attr == NULL + // Convert the attribute into an IDC value + || pyvar_to_idcvar(attr, &v, gvar_sn) <= 0) + { + Py_XDECREF(attr); + return 0; + } + // Store the attribute + VarSetAttr(idc_var, field_name, &v); + // Decrement attribute reference + Py_DECREF(attr); + } + } + } + return 1; +} + +//------------------------------------------------------------------------- +// Converts an IDC variable to a Python variable +// If py_var points to an existing object then the object will be updated +// If py_var points to an existing immutable object then ZERO is returned +// Return codes: +#define CIP_FAILED -1 // Conversion error +#define CIP_IMMUTABLE 0 // Immutable object passed. Will not update the object but no error occured +#define CIP_OK 1 // Success +#define CIP_OK_NODECREF 2 // Success but do not decrement its reference +int idcvar_to_pyvar( + const idc_value_t &idc_var, + PyObject **py_var) +{ + switch (idc_var.vtype) + { + case VT_PVOID: + if (*py_var == NULL) + *py_var = PyCObject_FromVoidPtr(idc_var.pvoid, NULL); + else + return CIP_IMMUTABLE; + break; + case VT_INT64: + { + // Recycle? + if (*py_var != NULL) + { + // Recycling an int64 object? + int t = get_pyidc_cvt_type(*py_var); + if (t != PY_ICID_INT64) + return CIP_IMMUTABLE; // Cannot recycle immutable object + // Update the attribute + PyObject_SetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + return CIP_OK; + } + PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_CVT_INT64); + if (py_cls == NULL) + return CIP_FAILED; + *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); + Py_DECREF(py_cls); + break; + } + case VT_STR: + *py_var = PyString_FromString(idc_var.str); + break; + case VT_STR2: + if (*py_var == NULL) + { + const qstring &s = idc_var.qstr(); + *py_var = PyString_FromStringAndSize(s.begin(), s.length()); + break; + } + else + return CIP_IMMUTABLE; // Cannot recycle immutable object + case VT_LONG: + // Cannot recycle immutable objects + if (*py_var != NULL) + return CIP_IMMUTABLE; +#ifdef __EA64__ + *py_var = PyLong_FromLongLong(idc_var.num); +#else + *py_var = PyLong_FromLong(idc_var.num); +#endif + break; + case VT_FLOAT: + if (*py_var == NULL) + { + double x; + if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) + INTERR(); + *py_var = PyFloat_FromDouble(x); + break; + } + else + return CIP_IMMUTABLE; + case VT_REF: + { + if (*py_var == NULL) + { + PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_CVT_BYREF); + if (py_cls == NULL) + return CIP_FAILED; + // Create a byref object with None value. We populate it later + *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); + Py_DECREF(py_cls); + } + int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var); + if (t != PY_ICID_BYREF) + return CIP_FAILED; + + // Dereference + // (Since we are not using VREF_COPY flag, we can safely const_cast) + idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); + if (dref_v == NULL) + return CIP_FAILED; + + // Can we recycle the object? + PyObject *new_py_val = PyObject_TryGetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR); + if (new_py_val != NULL) + { + // Recycle + t = idcvar_to_pyvar(*dref_v, &new_py_val); + Py_XDECREF(new_py_val); // DECREF because of GetAttrStr + // Success? Nothing more to be done + if (t == CIP_OK) + return CIP_OK; + // Clear it so we don't recycle it + new_py_val = NULL; + } + // Try to convert (not recycle) + if (idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK) + return CIP_FAILED; + // Update the attribute + PyObject_SetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR, new_py_val); + Py_DECREF(new_py_val); + break; + } + // Can convert back into a Python object or Python dictionary + // (Depending if py_var will be recycled and it was a dictionary) + case VT_OBJ: + { + // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields + idc_value_t idc_val; + if ( VarGetAttr(&idc_var, PY_IDCCVT_ID_ATTR, &idc_val) == eOk + && VarGetAttr(&idc_var, PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) + { + // Extract the object + *py_var = (PyObject *) idc_val.pvoid; + return CIP_OK_NODECREF; + } + PyObject *obj; + bool is_dict = false; + // Need to create a new object? + if (*py_var == NULL) + { + PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_APPCALL_SKEL_OBJ); + if (py_cls == NULL) + return CIP_FAILED; + obj = PyObject_CallFunctionObjArgs(py_cls, NULL); + Py_DECREF(py_cls); + if (obj == NULL) + return CIP_FAILED; + } + else + { + // Recycle existing variable + obj = *py_var; + if (PyDict_Check(obj)) + is_dict = true; + } + // Walk the IDC attributes and store into python + for (const char *attr_name = VarFirstAttr(&idc_var); + attr_name != NULL; + attr_name=VarNextAttr(&idc_var, attr_name)) + { + // Get the attribute + idc_value_t v; + VarGetAttr(&idc_var, attr_name, &v, true); + // Convert attribute to a python value + PyObject *py_attr(NULL); + int cvt = idcvar_to_pyvar(v, &py_attr); + if (cvt <= CIP_IMMUTABLE) + { + // Delete the object (if we created it) + if (*py_var == NULL) + Py_DECREF(obj); + return CIP_FAILED; + } + if (is_dict) + PyDict_SetItemString(obj, attr_name, py_attr); + else + PyObject_SetAttrString(obj, attr_name, py_attr); + if (cvt == CIP_OK) + Py_DECREF(py_attr); + } + *py_var = obj; + break; + } + // Unhandled type + default: + *py_var = NULL; + return CIP_FAILED; + } + return CIP_OK; +} + +// %} // Do not create separate wrappers for default arguments %feature("compactdefaultargs"); #ifdef __EA64__ -%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFF; -%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFF; -%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFF; -#else -%constant ea_t BADADDR = 0xFFFFFFFF; -%constant sel_t BADSEL = 0xFFFFFFFF; -%constant nodeidx_t BADNODE = 0xFFFFFFFF; +#ifdef __GNUC__ +%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFll; +%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFll; +%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFll; +#else // __GNUC__ +%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFui64; +%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFui64; +%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFui64; +#endif // __GNUC__ +#else //__EA64__ +%constant ea_t BADADDR = 0xFFFFFFFFL; +%constant sel_t BADSEL = 0xFFFFFFFFL; +%constant nodeidx_t BADNODE = 0xFFFFFFFFL; #endif // Help SWIG to figure out the ulonglong type @@ -166,3 +839,122 @@ idainfo *get_inf_structure(void) void enable_extlang_python(bool enable); void enable_python_cli(bool enable); } + +%pythoncode %{ +# + +# ----------------------------------------------------------------------- +# Seek constants +SEEK_SET = 0 # from the file start +SEEK_CUR = 1 # from the current position +SEEK_END = 2 # from the file end + +# ----------------------------------------------------------------------- +# This is a special helper object that helps detect which kind +# of object is this python object wrapping and how to convert it +# back and from IDC. +# This object is characterized by its special attribute and its value +class PyIdc_cvt_helper__(object): + def __init__(self, cvt_id, value): + # 0 = int64 object + # 1 = byref object + # 2 = opaque object + self.__idc_cvt_id__ = cvt_id + self.value = value + + def __set_value(self, v): + self.__idc_cvt_value__ = v + def __get_value(self): + return self.__idc_cvt_value__ + value = property(__get_value, __set_value) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_int64__(PyIdc_cvt_helper__): + """Helper class for explicitly representing VT_INT64 values""" + def __init__(self, v): + # id = 0 = int64 object + super(self.__class__, self).__init__(0, v) + + # operation table + op_table = \ + { + 0: lambda a, b: a + b, + 1: lambda a, b: a - b, + 2: lambda a, b: a * b, + 3: lambda a, b: a / b + } + # carries the operation given its number + def op(self, op_n, other, rev=False): + a = self.value + # other operand of same type? then take its value field + if type(other) == type(self): + b = other.value + else: + b = other + if rev: + t = a + a = b + b = t + # construct a new object and return as the result + return self.__class__(self.op_table[op_n](a, b)) + + # overloaded operators + def __add__(self, other): return self.op(0, other) + def __sub__(self, other): return self.op(1, other) + def __mul__(self, other): return self.op(2, other) + def __div__(self, other): return self.op(3, other) + def __radd__(self, other): return self.op(0, other, True) + def __rsub__(self, other): return self.op(1, other, True) + def __rmul__(self, other): return self.op(2, other, True) + def __rdiv__(self, other): return self.op(3, other, True) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_refclass__(PyIdc_cvt_helper__): + """Helper class for representing references to immutable objects""" + def __init__(self, v): + # id = one = byref object + super(self.__class__, self).__init__(1, v) + + def cstr(self): + return as_cstr(self.value) + +# ----------------------------------------------------------------------- +# This object can be passed to IDC and back to Python transparently +# The attribute "__idc_cvt_value__" is used +class PyIdc_cvt_opaque__(PyIdc_cvt_helper__): + def __init__(self, v): + # id = two = opaque object + super(self.__class__, self).__init__(2, v) + +# ----------------------------------------------------------------------- +def as_cstr(val): + """ + Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) + It scans for the first \x00 and returns the string value up to that point. + """ + if isinstance(val, PyIdc_cvt_refclass__): + val = val.value + n = 0 + for x in val: + if ord(x) == 0: + break + n = n + 1 + return val[:n] + +# ----------------------------------------------------------------------- +def as_unicode(s): + """Convenience function to convert a string into appropriate unicode format""" + # use UTF16 big/little endian, depending on the environment? + return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) + +# ----------------------------------------------------------------------- +def as_uint32(v): + return v & 0xffffffff + +# ----------------------------------------------------------------------- +def as_int32(v): + return -((~v & 0xffffffff)+1) + +# + +%} \ No newline at end of file diff --git a/swig/idd.i b/swig/idd.i index fd04d69..9cd4f3c 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -1,7 +1,7 @@ %ignore debugger_t; %ignore memory_info_t; %ignore register_info_t; - +%ignore appcall; %apply unsigned char { char dtyp }; %include "idd.hpp" @@ -11,11 +11,7 @@ %{ // -#ifndef PYUL_DEFINED -#define PYUL_DEFINED - typedef unsigned PY_LONG_LONG pyul_t; -#endif - +//------------------------------------------------------------------------- bool dbg_can_query() { // Reject the request only if no debugger is set @@ -23,6 +19,7 @@ bool dbg_can_query() return !(dbg == NULL || (!dbg->may_disturb() && get_process_state() != DSTATE_SUSP)); } +//------------------------------------------------------------------------- PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) { PyObject *py_list = PyList_New(areas.size()); @@ -32,19 +29,20 @@ PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) { const memory_info_t &mi = *it; // startEA endEA name sclass sbase bitness perm - PyList_SetItem(py_list, i, - Py_BuildValue("(KKssKii)", + PyList_SetItem(py_list, i, + Py_BuildValue("(KKssKii)", pyul_t(mi.startEA), pyul_t(mi.endEA), mi.name.c_str(), mi.sclass.c_str(), pyul_t(mi.sbase), - (unsigned int)(mi.bitness), + (unsigned int)(mi.bitness), (unsigned int)mi.perm)); } return py_list; } +//------------------------------------------------------------------------- PyObject *dbg_get_memory_info() { if (!dbg_can_query()) @@ -59,11 +57,12 @@ PyObject *dbg_get_memory_info() return meminfo_vec_t_to_py(areas); } +//------------------------------------------------------------------------- PyObject *dbg_get_registers() { if (dbg == NULL) Py_RETURN_NONE; - + PyObject *py_list = PyList_New(dbg->registers_size); for (int i=0;iregisters_size;i++) @@ -89,11 +88,11 @@ PyObject *dbg_get_registers() } // name flags class dtyp bit_strings bit_strings_default_mask - PyList_SetItem(py_list, i, - Py_BuildValue("(sIIINI)", - ri.name, - ri.flags, - (unsigned int)ri.register_class, + PyList_SetItem(py_list, i, + Py_BuildValue("(sIIINI)", + ri.name, + ri.flags, + (unsigned int)ri.register_class, (unsigned int)ri.dtyp, py_bits, (unsigned int)ri.bit_strings_default)); @@ -101,6 +100,7 @@ PyObject *dbg_get_registers() return py_list; } +//------------------------------------------------------------------------- PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) { if (!dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value)) @@ -113,6 +113,7 @@ PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) return Py_BuildValue("K", pyul_t(answer)); } +//------------------------------------------------------------------------- PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) { if (!dbg_can_query() || !PyNumber_Check(py_ea) || !PyNumber_Check(py_sz)) @@ -139,6 +140,7 @@ PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) return ret; } +//------------------------------------------------------------------------- PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) { if (!dbg_can_query() || !PyString_Check(py_buf) || !PyNumber_Check(py_ea)) @@ -151,9 +153,145 @@ PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) Py_RETURN_FALSE; Py_RETURN_TRUE; } + +//------------------------------------------------------------------------- +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list) +{ + if (!PyList_Check(arg_list)) + return NULL; + + const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); + const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); + + // Convert Python arguments into IDC values + qvector idc_args; + int sn = 0; + Py_ssize_t nargs = PyList_Size(arg_list); + idc_args.resize(nargs); + bool ok = true; + for (Py_ssize_t i=0;i%s\n", int(i), s.c_str()); + } + // Convert it + if (pyvar_to_idcvar(py_item, &idc_args[i], &sn) <= 0) + { + ok = false; + break; + } + } + + // Set exception message + if (!ok) + { + PyErr_SetString(PyExc_ValueError, "PyAppCall: Failed to convert Python values to IDC values"); + return NULL; + } + + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("input variables:\n" + "----------------\n"); + qstring s; + for (Py_ssize_t i=0;i %} +%rename (appcall) py_appcall; + %inline %{ // @@ -163,6 +301,12 @@ PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value); PyObject *dbg_get_registers(); PyObject *dbg_get_memory_info(); bool dbg_can_query(); +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list); // char get_event_module_name(const debug_event_t* ev, char *buf, size_t bufsize) @@ -213,3 +357,352 @@ bool can_exc_continue(const debug_event_t* ev) return ev->exc.can_cont; } %} + +%pythoncode %{ +# +import types + +# ----------------------------------------------------------------------- +# This class is used with |Appcall.array() method +class Appcall_array__(object): + def __init__(self, tp): + self.__type = tp + + def pack(self, L): + t = type(L) + if not (t == types.ListType or t == types.TupleType): + raise ValueError, "Either a list or a type must be passed" + self.__size = len(L) + if self.__size == 1: + self.__typedobj = Appcall__.typedobj(self.__type + ";") + else: + self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) + # Now store the object in a string buffer + ok, buf = self.__typedobj.store(L) + if ok: + return Appcall__.byref(buf) + else: + return None + + def try_to_convert_to_list(self, obj): + if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): + return obj + # at this point, we are sure we have an "idc list" + # let us convert to a Python list + return [getattr(obj, str(x)) for x in xrange(0, self.__size)] + + def unpack(self, buf, as_list=True): + # take the value from the special ref object + if isinstance(buf, PyIdc_cvt_refclass__): + buf = buf.value + + # we can only unpack from strings + if type(buf) != types.StringType: + raise ValueError, "Cannot unpack this type!" + # now unpack + ok, obj = self.__typedobj.retrieve(buf) + if not ok: + raise ValueError, "Failed while unpacking!" + if not as_list: + return obj + return self.try_to_convert_to_list(obj) + +# ----------------------------------------------------------------------- +# This class is used with the obj() method +class Appcall_object__(object): + """Helper class used to initialize empty objects""" + def __init__(self, **kwds): + self.__dict__ = kwds + + def __getitem__(self, idx): + return getattr(self, idx) + +# ----------------------------------------------------------------------- +# Wrapper class for the appcall() +class Appcall_callable__(object): + """ + Helper class to issue appcalls using a natural syntax: + appcall.FunctionNameInTheDatabase(arguments, ....) + or + appcall["Function@8"](arguments, ...) + or + f8 = appcall["Function@8"] + f8(arg1, arg2, ...) + or + o = appcall.obj() + i = byref(5) + appcall.funcname(arg1, i, "hello", o) + """ + def __init__(self, ea, tp = None, fld = None): + """Initializes an appcall with a given function ea""" + self.__ea = ea + self.__type = tp + self.__fields = fld + self.__options = None # Appcall options + + def __get_options(self): + return self.__options if self.__options != None else Appcall__.get_appcall_options() + def __set_options(self, v): + self.__options = v + """Sets the Appcall options locally to this Appcall instance""" + options = property(__get_options, __set_options) + + def __call__(self, *args): + """Make object callable. We redirect execution to idaapi.appcall()""" + if self.ea == None: + raise ValueError, "Object not callable!" + + # unpack arguments and convert to a list + arg_list = [x for x in args] + + # Save appcall options and set new global options + old_opt = Appcall__.get_appcall_options() + Appcall__.set_appcall_options(self.options) + + # Do the Appcall (use the wrapped version) + e_obj = None + try: + r = _idaapi.appcall( + self.ea, + _idaapi.get_current_thread(), + self.type, + self.fields, + arg_list) + except Exception, e: + e_obj = e + + # Restore appcall options + Appcall__.set_appcall_options(old_opt) + + # Return or re-raise exception + if e_obj: + raise Exception, e_obj + return r + + def __get_ea(self): + return self.__ea + def __set_ea(self, val): + self.__ea = val + """Returns or sets the EA associated with this object""" + ea = property(__get_ea, __set_ea) + + def __get_size(self): + if self.__type == None: + return -1 + r = _idaapi.get_type_size0(_idaapi.cvar.idati, self.__type) + if not r: + return -1 + return r + """Returns the size of the type""" + size = property(__get_size) + + def __get_type(self): + return self.__type + """Returns the typestring""" + type = property(__get_type) + + def __get_fields(self): + return self.__fields + """Returns the typestring""" + fields = property(__get_fields) + + def retrieve(self, src=None, flags=0): + """ + Unpacks a typed object from the database if an ea is given or from a string if a string was passed + @param src: the address of the object or a string + @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). + """ + + # Nothing passed? Take the address and unpack from the database + if not src: + src = self.ea + + if type(src) == types.StringType: + return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) + else: + return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) + + def store(self, obj, dest_ea=None, base_ea=0, flags=0): + """ + Packs an object into a given ea if provided or into a string if no address was passed. + + @return: - If packing to a string then a Tuple(Boolean, packed_string or error code) + - If packing to the database then a return code is returned (0 is success) + """ + + # no ea passed? thus pack to a string + if not dest_ea: + return _idaapi.pack_object_to_bv(obj, _idaapi.cvar.idati, self.type, self.fields, base_ea, flags) + else: + return _idaapi.pack_object_to_idb(obj, _idaapi.cvar.idati, self.type, self.fields, dest_ea, flags) + +# ----------------------------------------------------------------------- +class Appcall_consts__(object): + def __init__(self, default=0): + self.__default = default + + def __getattr__(self, attr): + return Appcall__.valueof(attr, self.__default) + +# ----------------------------------------------------------------------- +class Appcall__(object): + """ + Only set up the appcall, do not run it. + you should call CleanupAppcall() when finished + """ + APPCALL_MANUAL = 0x1 + """ + Return debug event information + If this bit is set, exceptions during appcall + will generate idc exceptions with full + information about the exception + """ + APPCALL_DEBEV = 0x2 + + def __init__(self): + self.__consts = Appcall_consts__() + + def __get_consts(self): + return self.__consts + """Use Appcall.Consts.CONST_NAME to access constants""" + Consts = property(__get_consts) + + @staticmethod + def __name_or_ea(name_or_ea): + """Function that accepts a name or an ea and checks if the address is enabled. + If a name is passed then idaapi.get_name_ea() is applied to retrieve the name + @return: Returns the resolved EA or raises an exception if the address is not enabled + """ + + # a string? try to resolve it + if type(name_or_ea) == types.StringType: + ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) + else: + ea = name_or_ea + # could not resolve name or invalid address? + if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): + raise ValueError, "Undefined function " + name_or_ea + return ea + + @staticmethod + def proto(name_or_ea, prototype, flags = None): + """Allows you to instantiate an appcall with the desired prototype""" + + # resolve and raise exception on error + ea = Appcall__.__name_or_ea(name_or_ea) + # parse the type + if not flags: + flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) + if not result: + raise ValueError, "Could not parse type: " + prototype + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + def __getattr__(self, name_or_ea): + """Allows you to call functions as if they were member functions""" + # resolve and raise exception on error + ea = self.__name_or_ea(name_or_ea) + if ea == _idaapi.BADADDR: + raise ValueError, "Undefined function " + name + # Return the callable method + return Appcall_callable__(ea) + + def __getitem__(self, idx): + """ + Use self[func_name] syntax if the function name contains invalid characters for an attribute name + See __getattr___ + """ + return self.__getattr__(idx) + + @staticmethod + def valueof(name, default=0): + """ + Returns the numeric value of a given name string. + If the name could not be resolved then the default value will be returned + """ + t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) + if t == 0: # NT_NONE + v = default + return v + + @staticmethod + def int64(v): + """Whenever a 64bit number is needed use this method to construct an object""" + return PyIdc_cvt_int64__(v) + + @staticmethod + def byref(val): + """ + Method to create references to immutable objects + Currently we support references to int/strings + Objects need not be passed by reference (this will be done automatically) + """ + return PyIdc_cvt_refclass__(val) + + @staticmethod + def buffer(str = None, size = 0, fill="\x00"): + """ + Creates a string buffer. The returned value (r) will be a byref object. + Use r.value to get the contents and r.size to get the buffer's size + """ + if not str: + str = "" + left = size - len(str) + if left > 0: + str = str + (fill * left) + r = Appcall__.byref(str) + r.size = size + return r + + @staticmethod + def obj(**kwds): + """Returns an empty object or objects with attributes as passed via its keywords arguments""" + return Appcall_object__(**kwds) + + @staticmethod + def cstr(val): + return as_cstr(val) + + @staticmethod + def unicode(s): + return as_unicode(s) + + @staticmethod + def array(type_name): + """Defines an array type. Later you need to pack() / unpack()""" + return Appcall_array__(type_name) + + @staticmethod + def typedobj(typestr, ea=None): + """ + Parses a type string and returns an appcall object. + One can then use retrieve() member method + @param ea: Optional parameter that later can be used to retrieve the type + @return: Appcall object + """ + # parse the type + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP + if not result: + raise ValueError, "Could not parse type: " + typestr + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + @staticmethod + def set_appcall_options(opt): + old_opt = Appcall__.get_appcall_options() + _idaapi.cvar.inf.appcall_options = opt + return old_opt + + @staticmethod + def get_appcall_options(): + return _idaapi.cvar.inf.appcall_options + + @staticmethod + def cleanup_appcall(tid = 0): + """Equivalent to IDC's CleanupAppcall()""" + return _idaapi.cleanup_appcall(tid) + +Appcall = Appcall__() +# +%} \ No newline at end of file diff --git a/swig/idp.i b/swig/idp.i index c3dc8e4..9afed54 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -50,7 +50,7 @@ %ignore processor_t::gen_stkvar_def; %ignore processor_t::u_outspec; %ignore processor_t::is_align_insn; - +%ignore IDB_Callback; %ignore processor_t::idp_notify; %ignore processor_t::notify; %ignore processor_t::set_idp_options; @@ -113,7 +113,7 @@ int idaapi IDB_Callback(void *ud, int notification_code, va_list va) class IDB_Hooks *proxy = (class IDB_Hooks *)ud; ea_t ea, ea2; bool repeatable_cmt; - type_t *type; + /*type_t *type;*/ /* p_list *fnames; */ int n; enum_t id; @@ -269,7 +269,7 @@ int idaapi IDB_Callback(void *ud, int notification_code, va_list va) return proxy->segm_moved(ea, ea2, size); } } - catch (Swig::DirectorException &e) + catch (Swig::DirectorException &) { msg("Exception in IDP Hook function:\n"); if (PyErr_Occurred()) diff --git a/swig/kernwin.i b/swig/kernwin.i index 704e06a..2a7d94a 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -156,14 +156,6 @@ uint32 choose_choose(PyObject *self, // -//------------------------------------------------------------------------ -static PyObject *PyObject_TryGetAttrString(PyObject *object, const char *attr) -{ - if (!PyObject_HasAttrString(object, attr)) - return NULL; - return PyObject_GetAttrString(object, attr); -} - //------------------------------------------------------------------------ // Some defines #define POPUP_NAMES_COUNT 4 diff --git a/swig/loader.i b/swig/loader.i index 2de578b..ab798c9 100644 --- a/swig/loader.i +++ b/swig/loader.i @@ -13,6 +13,7 @@ // TODO: These could be wrapped if needed %ignore load_info_t; +%ignore add_plugin_option; %ignore build_loaders_list; %ignore free_loaders_list; %ignore get_loader_name_from_dll; diff --git a/swig/typeinf.i b/swig/typeinf.i index 4d0079d..136feb1 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -10,6 +10,10 @@ %ignore get_de; %ignore skip_ptr_type_header; %ignore skip_array_type_header; +%ignore unpack_object_from_idb; +%ignore unpack_object_from_bv; +%ignore pack_object_to_idb; +%ignore pack_object_to_bv; %ignore typend; %ignore typlen; %ignore typncpy; @@ -133,22 +137,230 @@ %ignore build_anon_type_name; %ignore type_names; %ignore get_compiler_id; - +%ignore reloc_info_t; +%ignore relobj_t; +%ignore regobj_t; %ignore build_func_type; %include "typeinf.hpp" +%{ +// +//------------------------------------------------------------------------- +// Utility function to convert a python object to an IDC object +// and sets a python exception on failure. +static bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj) +{ + int sn = 0; + if (pyvar_to_idcvar(py_obj, idc_obj, &sn) <= 0) + { + PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); + return false; + } + return true; +} +// +%} // Custom wrappers %rename (load_til) load_til_wrap; +%rename (get_type_size0) py_get_type_size0; +%rename (unpack_object_from_idb) py_unpack_object_from_idb; +%rename (unpack_object_from_bv) py_unpack_object_from_bv; +%rename (pack_object_to_idb) py_pack_object_to_idb; +%rename (pack_object_to_bv) py_pack_object_to_bv; %inline %{ +// +//------------------------------------------------------------------------- +PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) +{ + qtype fields, type; + qstring name; + bool ok = parse_decl(ti, decl, &name, &type, &fields, flags); + if (!ok) + Py_RETURN_NONE; + return Py_BuildValue("(sss)", + name.c_str(), + (char *)type.c_str(), + (char *)fields.c_str()); +} + +//------------------------------------------------------------------------- +PyObject *py_get_type_size0(const til_t *ti, PyObject *tp) +{ + if (!PyString_Check(tp)) + { + PyErr_SetString(PyExc_ValueError, "String expected!"); + return NULL; + } + size_t sz = get_type_size0(ti, (type_t *)PyString_AsString(tp)); + if (sz == BADSIZE) + Py_RETURN_NONE; + return PyInt_FromLong(sz); +} + +//------------------------------------------------------------------------- +// Read a typed idc object from the database +PyObject *py_unpack_object_from_idb( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags) +{ + if (!PyString_Check(py_type) && !PyString_Check(py_fields)) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Unpack + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + idc_value_t idc_obj; + error_t err = unpack_object_from_idb(&idc_obj, ti, type, fields, ea, NULL, pio_flags); + + // Unpacking failed? + if (err != eOk) + return Py_BuildValue("(ii)", 0, err); + + // Convert + PyObject *py_ret(NULL); + err = idcvar_to_pyvar(idc_obj, &py_ret); + // Conversion failed? + if (err != CIP_OK) + return Py_BuildValue("(ii)", 0, err); + return Py_BuildValue("(iO)", 1, py_ret); +} + +//------------------------------------------------------------------------- +// Read a typed idc object from the byte vector +PyObject *py_unpack_object_from_bv( + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + PyObject *py_bytes, + int pio_flags) +{ + if (!PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes)) + { + PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); + return NULL; + } + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Make a byte vector + bytevec_t bytes; + bytes.resize(PyString_Size(py_bytes)); + memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); + + idc_value_t idc_obj; + error_t err = unpack_object_from_bv(&idc_obj, ti, type, fields, bytes, pio_flags); + + // Unpacking failed? + if (err != eOk) + return Py_BuildValue("(ii)", 0, err); + + // Convert + PyObject *py_ret(NULL); + err = idcvar_to_pyvar(idc_obj, &py_ret); + // Conversion failed? + if (err != CIP_OK) + return Py_BuildValue("(ii)", 0, err); + return Py_BuildValue("(iO)", 1, py_ret); +} + +//------------------------------------------------------------------------- +// Write a typed idc object to the database +// Raises an exception if wrong parameters were passed or conversion fails +// Returns the error_t returned by idasdk.pack_object_to_idb +PyObject *py_pack_object_to_idb( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t ea, + int pio_flags) +{ + if (!PyString_Check(py_type) && !PyString_Check(py_fields)) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + if (!convert_pyobj_to_idc_exc(py_obj, &idc_obj)) + return NULL; + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Pack + error_t err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + return PyInt_FromLong(err); +} + +//------------------------------------------------------------------------- +// Returns a tuple(Boolean, PackedBuffer or Error Code) +PyObject *py_pack_object_to_bv( + PyObject *py_obj, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t base_ea, + int pio_flags=0) +{ + if (!PyString_Check(py_type) && !PyString_Check(py_fields)) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + if (!convert_pyobj_to_idc_exc(py_obj, &idc_obj)) + return NULL; + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Pack + relobj_t bytes; + error_t err = pack_object_to_bv( + &idc_obj, + ti, + type, + fields, + &bytes, + NULL, + pio_flags); + do + { + if (err != eOk) + break; + if (!bytes.relocate(base_ea, inf.mf)) + { + err = -1; + break; + } + return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); + } while (false); + return Py_BuildValue("(ii)", 0, err); +} +// til_t * load_til(const char *tildir, const char *name) { char errbuf[4096]; til_t *res; - + res = load_til(tildir, name, errbuf, sizeof(errbuf)); - + if (!res) { PyErr_SetString(PyExc_RuntimeError, errbuf); @@ -165,9 +377,9 @@ til_t * load_til_header_wrap(const char *tildir, const char *name) { char errbuf[4096]; til_t *res; - + res = load_til_header(tildir, name, errbuf, sizeof(errbuf));; - + if (!res) { PyErr_SetString(PyExc_RuntimeError, errbuf); @@ -196,7 +408,7 @@ char *idc_get_type(ea_t ea, char *buf, size_t bufsize) { type_t type[MAXSTR]; p_list fnames[MAXSTR]; - + if (get_ti(ea, type, sizeof(type), fnames, sizeof(fnames))) { int code = print_type_to_one_line(buf, bufsize, idati, type, @@ -211,7 +423,7 @@ char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) { type_t type[MAXSTR]; p_list fnames[MAXSTR]; - + if (guess_type(ea, type, sizeof(type), fnames, sizeof(fnames))) { int code = print_type_to_one_line(buf, bufsize, idati, type, @@ -234,7 +446,7 @@ int idc_set_local_type(int ordinal, const char *dcl, int flags) qstring name; qtype type; qtype fields; - + if (!parse_decl(idati, dcl, &name, &type, &fields, flags)) return 0;