From 69d5c83d2813aa15b17eee001e87a7a48ab65c71 Mon Sep 17 00:00:00 2001 From: "elias.bachaalany" Date: Sun, 24 Jun 2012 20:49:11 +0000 Subject: [PATCH] - IDA Pro 6.3 support - The Functions() generator function now accepts function tail start parameter - Added into idc.py: DbgRead/DbgWrite/SetTargetAssembler and stack pointer related functions - Wrapped more type info related functions --- CHANGES.txt | 6 + build.py | 15 +- examples/ex_expr.py | 32 +- python.cfg | 4 + python.cpp | 56 +++- python/idautils.py | 11 +- python/idc.py | 210 +++++++++++-- python/init.py | 2 + pywraps/deploy.bat | 2 +- pywraps/driver.cpp | 2 +- pywraps/py_appcall.py | 10 +- pywraps/py_bytes.hpp | 10 + pywraps/py_graph.hpp | 4 +- pywraps/py_idaapi.hpp | 16 +- pywraps/py_idp.hpp | 2 +- pywraps/py_kernwin.hpp | 11 +- pywraps/py_plgform.hpp | 22 +- pywraps/py_plgform.py | 4 +- pywraps/py_typeinf.hpp | 24 +- swig/auto.i | 1 + swig/bytes.i | 10 + swig/expr.i | 680 ++++++++++++++++++++--------------------- swig/funcs.i | 5 +- swig/graph.i | 4 +- swig/idaapi.i | 8 +- swig/idp.i | 2 +- swig/kernwin.i | 37 ++- swig/name.i | 222 +++++++------- swig/pro.i | 12 +- swig/typeinf.i | 47 ++- tools/swigdocs.py | 23 +- 31 files changed, 908 insertions(+), 586 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index fcfb01a..bc42779 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,11 @@ Please see http://code.google.com/p/idapython/source/list for a detailed list of changes. +Changes from version 1.5.4 to 1.5.5 +------------------------------------ +- IDA Pro 6.3 support +- The Functions() generator function now accepts function tail start parameter +- Added into idc.py: DbgRead/DbgWrite/SetTargetAssembler and stack pointer related functions +- Wrapped more type info related functions Changes from version 1.5.3 to 1.5.4 ------------------------------------ diff --git a/build.py b/build.py index 245d15c..a543f94 100644 --- a/build.py +++ b/build.py @@ -24,7 +24,7 @@ from distutils import sysconfig VERBOSE = True IDA_MAJOR_VERSION = 6 -IDA_MINOR_VERSION = 2 +IDA_MINOR_VERSION = 3 if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] @@ -36,7 +36,7 @@ else: # IDAPython version VERSION_MAJOR = 1 VERSION_MINOR = 5 -VERSION_PATCH = 4 +VERSION_PATCH = 5 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) @@ -319,10 +319,11 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): builder = GCCBuilder() platform_macros = [ "__LINUX__" ] python_libpath = os.path.join(sysconfig.EXEC_PREFIX, "lib") - python_library = "-lpython%d.%d" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) + python_library = "-Bdynamic -lpython%d.%d" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_linux_gcc_64" or "x86_linux_gcc_32") ida_lib = "" - extra_link_parameters = "" + extra_link_parameters = " -s" + builder.compiler_parameters += " -O2" # Platform-specific settings for the Windows build elif platform == "win32": builder = MSVCBuilder() @@ -333,6 +334,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): ida_lib = "ida.lib" SWIG_OPTIONS += " -D__NT__ " extra_link_parameters = "" + builder.compiler_parameters += " -Ox" # Platform-specific settings for the Mac OS X build elif platform == "macosx": builder = GCCBuilder() @@ -342,7 +344,8 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): python_library = "-framework Python" ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_mac_gcc_64" or "x86_mac_gcc_32") ida_lib = ea64 and "-lida64" or "-lida" - extra_link_parameters = "" + extra_link_parameters = " -s" + builder.compiler_parameters += " -O3" assert builder, "Unknown platform! No idea how to build here..." @@ -350,6 +353,8 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64): if ea64: platform_macros.append("__EA64__") + platform_macros.append("NDEBUG") + if not '--no-early-load' in sys.argv: platform_macros.append("PLUGINFIX") diff --git a/examples/ex_expr.py b/examples/ex_expr.py index 765d746..1f97f3c 100644 --- a/examples/ex_expr.py +++ b/examples/ex_expr.py @@ -1,16 +1,16 @@ -# ----------------------------------------------------------------------- -# This is an example illustrating how to extend IDC from Python -# (c) Hex-Rays -# -from idaapi import set_idc_func_ex - -# -def py_power(n, e): - return n ** e - -ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) -if ok: - print("Now the pow() will be present IDC!") -else: - print("Failed to register pow() IDC function") -# +# ----------------------------------------------------------------------- +# This is an example illustrating how to extend IDC from Python +# (c) Hex-Rays +# +from idaapi import set_idc_func_ex + +# +def py_power(n, e): + return n ** e + +ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) +if ok: + print("Now the pow() will be present IDC!") +else: + print("Failed to register pow() IDC function") +# diff --git a/python.cfg b/python.cfg index 8fadcec..f4614c7 100644 --- a/python.cfg +++ b/python.cfg @@ -8,3 +8,7 @@ REMOVE_CWD_SYS_PATH = 0 // Script timeout (in seconds) // (A value of 0 disables the timeout) SCRIPT_TIMEOUT = 3 + +// Use a local Python library +// If enabled, the "lib" directory tree with modules must be present in IDADIR/python +USE_LOCAL_PYTHON = 0 diff --git a/python.cpp b/python.cpp index e968dd7..9b21cea 100644 --- a/python.cpp +++ b/python.cpp @@ -125,6 +125,7 @@ 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(); static void begin_execution(); @@ -369,6 +370,11 @@ const char *idaapi set_python_options( 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); @@ -466,7 +472,10 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf { PyObject *py_execscript = get_idaapi_attr(S_IDAAPI_EXECSCRIPT); if ( py_execscript == NULL ) + { + qstrncpy(errbuf, "Could not find idaapi." S_IDAAPI_EXECSCRIPT " ?!", errbufsz); return false; + } char script[MAXSTR]; qstrncpy(script, FileName, sizeof(script)); @@ -1288,6 +1297,25 @@ static void sanitize_path() 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) @@ -1306,12 +1334,11 @@ bool IDAPython_Init(void) char tmp[QMAXPATH]; #ifdef __LINUX__ // Export symbols from libpython to resolve imported module deps - qsnprintf(tmp, sizeof(tmp), "libpython%d.%d.so.1", - PY_MAJOR_VERSION, - PY_MINOR_VERSION); - if ( !dlopen(tmp, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) + // 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: %s", dlerror()); + warning("IDAPython dlopen(" PYLIB ") error: %s", dlerror()); return false; } #endif @@ -1350,16 +1377,31 @@ bool IDAPython_Init(void) } } + 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(); @@ -1416,7 +1458,7 @@ bool IDAPython_Init(void) parse_plugin_options(); // Register a RunPythonStatement() function for IDC - set_idc_func(S_IDC_RUNPYTHON_STATEMENT, idc_runpythonstatement, idc_runpythonstatement_args); + 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 ) @@ -1463,7 +1505,7 @@ void IDAPython_Term(void) deinit_pywraps(); // Uninstall IDC function - set_idc_func(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL); + set_idc_func_ex(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL, 0); // Shut the interpreter down Py_Finalize(); diff --git a/python/idautils.py b/python/idautils.py index 8a5f0bd..c014ee1 100644 --- a/python/idautils.py +++ b/python/idautils.py @@ -217,9 +217,14 @@ def Functions(start=None, end=None): if not start: start = idaapi.cvar.inf.minEA if not end: end = idaapi.cvar.inf.maxEA - func = idaapi.get_func(start) - if not func: - func = idaapi.get_next_func(start) + # find first function head chunk in the range + chunk = idaapi.get_fchunk(start) + if not chunk: + chunk = idaapi.get_next_fchunk(start) + while chunk and chunk.startEA < end and (chunk.flags & idaapi.FUNC_TAIL) != 0: + chunk = idaapi.get_next_fchunk(chunk.startEA) + func = chunk + while func and func.startEA < end: startea = func.startEA yield startea diff --git a/python/idc.py b/python/idc.py index 684663f..62dc40c 100644 --- a/python/idc.py +++ b/python/idc.py @@ -1772,6 +1772,35 @@ def DbgQword(ea): return __DbgValue(ea, 8) +def DbgRead(ea, size): + """ + Read from debugger memory. + + @param ea: linear address + @param size: size of data to read + @return: data as a string. If failed, If failed, throws an exception + + Thread-safe function (may be called only from the main thread and debthread) + """ + return idaapi.dbg_read_memory(ea, size) + + +def DbgWrite(ea, data): + """ + Write to debugger memory. + + @param ea: linear address + @param data: string to write + @return: number of written bytes (-1 - network/debugger error) + + Thread-safe function (may be called only from the main thread and debthread) + """ + if not idaapi.dbg_can_query(): + return -1 + elif len(data) > 0: + return idaapi.dbg_write_memory(ea, data) + + def GetOriginalByte(ea): """ Get original value of program byte @@ -2867,6 +2896,16 @@ def SetProcessorType (processor, level): """ return idaapi.set_processor_type(processor, level) +def SetTargetAssembler(asmidx): + """ + Set target assembler + @param asmidx: index of the target assembler in the array of + assemblers for the current processor. + + @return: 1-ok, 0-failed + """ + return idaapi.set_target_assembler(asmidx) + SETPROC_COMPAT = idaapi.SETPROC_COMPAT SETPROC_ALL = idaapi.SETPROC_ALL SETPROC_USER = idaapi.SETPROC_USER @@ -4340,6 +4379,75 @@ def SetSpDiff(ea, delta): return idaapi.add_user_stkpnt(ea, delta) +# ---------------------------------------------------------------------------- +# S T A C K +# ---------------------------------------------------------------------------- + +def AddAutoStkPnt2(func_ea, ea, delta): + """ + Add automatical SP register change point + @param func_ea: function start + @param ea: linear address where SP changes + usually this is the end of the instruction which + modifies the stack pointer (cmd.ea+cmd.size) + @param delta: difference between old and new values of SP + @return: 1-ok, 0-failed + """ + pfn = idaapi.get_func(func_ea) + if not pfn: + return 0 + return idaapi.add_auto_stkpnt2(pfn, ea, delta) + +def AddUserStkPnt(ea, delta): + """ + Add user-defined SP register change point. + + @param ea: linear address where SP changes + @param delta: difference between old and new values of SP + + @return: 1-ok, 0-failed + """ + return idaapi.add_user_stkpnt(ea, delta); + +def DelStkPnt(func_ea, ea): + """ + Delete SP register change point + + @param func_ea: function start + @param ea: linear address + @return: 1-ok, 0-failed + """ + pfn = idaapi.get_func(func_ea) + if not pfn: + return 0 + return idaapi.del_stkpnt(pfn, ea) + +def GetMinSpd(func_ea): + """ + Return the address with the minimal spd (stack pointer delta) + If there are no SP change points, then return BADADDR. + + @param func_ea: function start + @return: BADDADDR - no such function + """ + pfn = idaapi.get_func(func_ea) + if not pfn: + return BADADDR + return idaapi.get_min_spd_ea(pfn) + +def RecalcSpd(cur_ea): + """ + Recalculate SP delta for an instruction that stops execution. + + @param cur_ea: linear address of the current instruction + @return: 1 - new stkpnt is added, 0 - nothing is changed + """ + return idaapi.recalc_spd(cur_ea) + + + + + # ---------------------------------------------------------------------------- # E N T R Y P O I N T S # ---------------------------------------------------------------------------- @@ -4458,9 +4566,9 @@ def GetFixupTgtType(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target type: """ - fd = idaapi.get_fixup(ea) + fd = idaapi.fixup_data_t() - if not fd: + if not idaapi.get_fixup(ea, fd): return -1 return fd.type @@ -4504,8 +4612,12 @@ def GetFixupTgtSel(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target selector """ - fd = idaapi.get_fixup(ea) - return -1 if not fd else fd.sel + fd = idaapi.fixup_data_t() + + if not idaapi.get_fixup(ea, fd): + return -1 + + return fd.sel def GetFixupTgtOff(ea): @@ -4517,8 +4629,12 @@ def GetFixupTgtOff(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target offset """ - fd = idaapi.get_fixup(ea) - return -1 if not fd else fd.off + fd = idaapi.fixup_data_t() + + if not idaapi.get_fixup(ea, fd): + return -1 + + return fd.off def GetFixupTgtDispl(ea): @@ -4530,8 +4646,12 @@ def GetFixupTgtDispl(ea): @return: -1 - no fixup at the specified address otherwise returns fixup target displacement """ - fd = idaapi.get_fixup(ea) - return -1 if not fd else fd.displacement + fd = idaapi.fixup_data_t() + + if not idaapi.get_fixup(ea, fd): + return -1 + + return fd.displacement def SetFixup(ea, fixuptype, targetsel, targetoff, displ): @@ -4791,6 +4911,30 @@ def GetMemberQty(sid): return -1 if not s else s.memqty +def GetMemberId(sid, member_offset): + """ + @param sid: structure type ID + @param member_offset:. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + + @return: -1 if bad structure type ID is passed or there is + no member at the specified offset. + otherwise returns the member id. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + m = idaapi.get_member(s, member_offset) + if not m: + return -1 + + return m.id + + def GetStrucPrevOff(sid, offset): """ Get previous offset in a structure @@ -6633,6 +6777,15 @@ def SizeOf(typestr): """ return idaapi.get_type_size0(idaapi.cvar.idati, typestr) +def GetTinfo(ea): + """ + Get type information of function/variable as 'typeinfo' object + + @param ea: the address of the object + @return: None on failure, or (type, fields) tuple. + """ + return idaapi.idc_get_type_raw(ea) + def GuessType(ea): """ Guess type of function/variable @@ -6652,11 +6805,16 @@ def SetType(ea, newtype): @param newtype: the type string in C declaration form. Must contain the closing ';' if specified as an empty string, then the - assciated with 'ea' will be deleted + item associated with 'ea' will be deleted. @return: 1-ok, 0-failed. """ - return idaapi.apply_cdecl2(idaapi.cvar.idati, ea, newtype) + if newtype is not '': + return idaapi.apply_cdecl2(idaapi.cvar.idati, ea, newtype) + if idaapi.has_ti(ea): + idaapi.del_tinfo(ea) + return True + return False def ParseType(inputtype, flags): """ @@ -7680,6 +7838,29 @@ def SetBptAttr(address, bptattr, value): idaapi.update_bpt(bpt) return True +def SetBptCndEx(ea, cnd, is_lowcnd): + """ + Set breakpoint condition + + @param ea: any address in the breakpoint range + @param cnd: breakpoint condition + @param is_lowcnd: 0 - regular condition, 1 - low level condition + + @return: success + """ + bpt = idaapi.bpt_t() + + if not idaapi.get_bpt(ea, bpt): + return False + + bpt.condition = cnd + if not is_lowcnd: + bpt.flags |= BPT_LOWCND + else: + bpt.flags &= ~BPT_LOWCND + + return idaapi.update_bpt(bpt) + def SetBptCnd(ea, cnd): """ @@ -7690,14 +7871,7 @@ def SetBptCnd(ea, cnd): @return: success """ - bpt = idaapi.bpt_t() - - if not idaapi.get_bpt(ea, bpt): - return False - - bpt.condition = cnd - - return idaapi.update_bpt(bpt) + return SetBptCndEx(ea, cnd, 0) def AddBptEx(ea, size, bpttype): diff --git a/python/init.py b/python/init.py index dcdba31..b5d0de1 100644 --- a/python/init.py +++ b/python/init.py @@ -68,6 +68,8 @@ def print_banner(): # ----------------------------------------------------------------------- # Redirect stderr and stdout to the IDA message window +_orig_stdout = sys.stdout; +_orig_stderr = sys.stderr; sys.stdout = sys.stderr = IDAPythonStdOut() # Assign a default sys.argv diff --git a/pywraps/deploy.bat b/pywraps/deploy.bat index 8d222d8..0b9d094 100644 --- a/pywraps/deploy.bat +++ b/pywraps/deploy.bat @@ -3,7 +3,7 @@ rem Please use the same tag for the same .i file rem That means if many insertions are going to happen in one given .i file then don't use more than code marking tag -set PY=c:\python26\python.exe +set PY=c:\python27\python.exe echo. echo -------- DEPLOY started -------------------------------------------------- diff --git a/pywraps/driver.cpp b/pywraps/driver.cpp index 09719d9..2648e24 100644 --- a/pywraps/driver.cpp +++ b/pywraps/driver.cpp @@ -168,5 +168,5 @@ plugin_t PLUGIN = "", // multiline help about the plugin "pywraps", // the preferred short name of the plugin - "Alt-0" // the preferred hotkey to run the plugin + "" // the preferred hotkey to run the plugin }; diff --git a/pywraps/py_appcall.py b/pywraps/py_appcall.py index 18dc316..ac6ca1a 100644 --- a/pywraps/py_appcall.py +++ b/pywraps/py_appcall.py @@ -571,6 +571,14 @@ def test_gpa(): return 1 +# ----------------------------------------------------------------------- +def test_loaddll(): + h = loadlib("twain_32.dll") + if h == 0: + print "failed to load twain32 library!" + return -1 + return 1 + # ----------------------------------------------------------------------- # Packs a simple structure (into the database) and unpacks it back using the idaapi methods def test_pck_idb_raw(): @@ -884,7 +892,7 @@ def test_exec_throw(): # all the tests that take zero parameters tests0 = (test_gpa, test_pck_idb_raw, test_pck_bv_raw, test_unpack_raw, test_pck_idb, test_pck_bv, - test_enum_files, test2, test_exec_throw) + test_enum_files, test2, test_exec_throw, test_loaddll) test_log = None # test log file # ----------------------------------------------------------------------- diff --git a/pywraps/py_bytes.hpp b/pywraps/py_bytes.hpp index 7ed3473..ce0d486 100644 --- a/pywraps/py_bytes.hpp +++ b/pywraps/py_bytes.hpp @@ -152,6 +152,16 @@ static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) //--------------------------------------------------------------------------- /* # +# Conversion options for get_ascii_contents2(): +ACFOPT_ASCII = 0x00000000 # convert Unicode strings to ASCII +ACFOPT_UTF16 = 0x00000001 # return UTF-16 (aka wide-char) array for Unicode strings + # ignored for non-Unicode strings +ACFOPT_UTF8 = 0x00000002 # convert Unicode strings to UTF-8 + # ignored for non-Unicode strings +ACFOPT_CONVMASK = 0x0000000F +ACFOPT_ESCAPE = 0x00000010 # for ACFOPT_ASCII, convert non-printable + # characters to C escapes (\n, \xNN, \uNNNN) + def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII): """ Get contents of ascii string diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp index bfd9adc..602017b 100644 --- a/pywraps/py_graph.hpp +++ b/pywraps/py_graph.hpp @@ -562,7 +562,7 @@ private: void show() { - open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); + open_tform(form, FORM_TAB|FORM_MENU); } static py_graph_t *extract_this(PyObject *self) @@ -666,7 +666,7 @@ private: qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); id.create(grnode); gv = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_MDI | FORM_TAB | FORM_MENU); + open_tform(form, FORM_TAB | FORM_MENU); if ( gv != NULL ) viewer_fit_window(gv); } diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index e9f84d2..0c97fb9 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -619,7 +619,7 @@ def parse_command_line(cmdline): static PyObject *py_parse_command_line(const char *cmdline) { qstrvec_t args; - if ( parse_command_line(cmdline, &args) == 0 ) + if ( parse_command_line2(cmdline, &args, NULL) == 0 ) Py_RETURN_NONE; PyObject *py_list = PyList_New(args.size()); @@ -729,8 +729,8 @@ static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) static bool qstrvec_t_set( - PyObject *self, - size_t idx, + PyObject *self, + size_t idx, const char *s) { qstrvec_t *sv = qstrvec_t_get_clink(self); @@ -741,7 +741,7 @@ static bool qstrvec_t_set( } static bool qstrvec_t_from_list( - PyObject *self, + PyObject *self, PyObject *py_list) { qstrvec_t *sv = qstrvec_t_get_clink(self); @@ -781,13 +781,13 @@ static bool qstrvec_t_clear(PyObject *self, bool qclear) sv->qclear(); else sv->clear(); - + return true; } static bool qstrvec_t_insert( - PyObject *self, - size_t idx, + PyObject *self, + size_t idx, const char *s) { qstrvec_t *sv = qstrvec_t_get_clink(self); @@ -802,7 +802,7 @@ static bool qstrvec_t_remove(PyObject *self, size_t idx) qstrvec_t *sv = qstrvec_t_get_clink(self); if ( sv == NULL || idx >= sv->size() ) return false; - + sv->erase(sv->begin()+idx); return true; } diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp index 58c65f4..847b96b 100644 --- a/pywraps/py_idp.hpp +++ b/pywraps/py_idp.hpp @@ -434,7 +434,7 @@ class IDP_Hooks(object): - cmd.itype must be set >= idaapi.CUSTOM_CMD_ITYPE - cmd.size must be set to the instruction length - @return: Boolean + @return: Boolean - False if the instruction is not recognized - True if the instruction was decoded. idaapi.cmd should be filled in that case. """ diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp index 754b482..91517d8 100644 --- a/pywraps/py_kernwin.hpp +++ b/pywraps/py_kernwin.hpp @@ -494,7 +494,7 @@ in this case. This flag can be used to delay the code execution until the next UI loop run even from the main thread""" -def execute_sync(callable, reqf) +def execute_sync(callable, reqf): """ Executes a function in the context of the main thread. If the current thread not the main thread, then the call is queued and @@ -567,7 +567,7 @@ static int py_execute_sync(PyObject *py_callable, int reqf) /* # -def execute_ui_requests(callable_list) +def execute_ui_requests(callable_list): """ Inserts a list of callables into the UI message processing queue. When the UI is ready it will call one callable. @@ -760,7 +760,11 @@ class UI_Hooks(object): IDA is terminated and the database is already closed. The UI may close its windows in this callback. """ - pass + # if the user forgot to call unhook, do it for him + self.unhook() + + def __term__(self): + self.term() # */ @@ -769,6 +773,7 @@ class UI_Hooks public: virtual ~UI_Hooks() { + unhook(); } bool hook() diff --git a/pywraps/py_plgform.hpp b/pywraps/py_plgform.hpp index 1ffcfda..cd81e39 100644 --- a/pywraps/py_plgform.hpp +++ b/pywraps/py_plgform.hpp @@ -24,10 +24,10 @@ private: PYW_GIL_ENSURE; PyObject *py_result = PyObject_CallMethod( _this->py_obj, - (char *)S_ON_CREATE, "O", + (char *)S_ON_CREATE, "O", PyCObject_FromVoidPtr(form, NULL)); PYW_GIL_RELEASE; - + PyW_ShowCbErr(S_ON_CREATE); Py_XDECREF(py_result); } @@ -40,10 +40,10 @@ private: PYW_GIL_ENSURE; PyObject *py_result = PyObject_CallMethod( _this->py_obj, - (char *)S_ON_CLOSE, "O", + (char *)S_ON_CLOSE, "O", PyCObject_FromVoidPtr(form, NULL)); PYW_GIL_RELEASE; - + PyW_ShowCbErr(S_ON_CLOSE); Py_XDECREF(py_result); @@ -57,7 +57,7 @@ private: { unhook_from_notification_point(HT_UI, s_callback, this); form = NULL; - + // Call DECREF at last, since it may trigger __del__ Py_XDECREF(py_obj); } @@ -69,7 +69,7 @@ public: bool show( PyObject *obj, - const char *caption, + const char *caption, int options) { // Already displayed? @@ -91,7 +91,7 @@ public: form = create_tform(caption, NULL); if ( form == NULL ) return false; - + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) { form = NULL; @@ -119,7 +119,7 @@ public: { return PyCObject_FromVoidPtr(new plgform_t(), destroy); } - + static void destroy(void *obj) { delete (plgform_t *)obj; @@ -137,9 +137,9 @@ static PyObject *plgform_new() static bool plgform_show( PyObject *py_link, - PyObject *py_obj, - const char *caption, - int options = FORM_MDI|FORM_TAB|FORM_MENU|FORM_RESTORE) + PyObject *py_obj, + const char *caption, + int options = FORM_TAB|FORM_MENU|FORM_RESTORE) { DECL_PLGFORM; return plgform->show(py_obj, caption, options); diff --git a/pywraps/py_plgform.py b/pywraps/py_plgform.py index eabddd2..be0bcde 100644 --- a/pywraps/py_plgform.py +++ b/pywraps/py_plgform.py @@ -9,7 +9,7 @@ class PluginForm(object): """ FORM_MDI = 0x01 - """start by default as MDI""" + """start by default as MDI (obsolete)""" FORM_TAB = 0x02 """attached by default to a tab""" FORM_RESTORE = 0x04 @@ -38,7 +38,7 @@ class PluginForm(object): @param caption: The form caption @param options: One of PluginForm.FORM_ constants """ - options |= PluginForm.FORM_MDI|PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE return _idaapi.plgform_show(self.__clink__, self, caption, options) diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp index 32d1858..d589f64 100644 --- a/pywraps/py_typeinf.hpp +++ b/pywraps/py_typeinf.hpp @@ -101,12 +101,12 @@ PyObject *py_unpack_object_from_idb( 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, + &idc_obj, + ti, + type, + fields, + ea, + NULL, pio_flags); // Unpacking failed? @@ -116,7 +116,7 @@ PyObject *py_unpack_object_from_idb( // 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); @@ -169,11 +169,11 @@ PyObject *py_unpack_object_from_bv( idc_value_t idc_obj; error_t err = unpack_object_from_bv( - &idc_obj, - ti, - type, - fields, - bytes, + &idc_obj, + ti, + type, + fields, + bytes, pio_flags); // Unpacking failed? diff --git a/swig/auto.i b/swig/auto.i index 57e72ae..225683e 100644 --- a/swig/auto.i +++ b/swig/auto.i @@ -11,6 +11,7 @@ %ignore auto_save; %ignore auto_term; %ignore ea_without_xrefs; +%ignore postpone_lastinsn_analysis; %include "auto.hpp" diff --git a/swig/bytes.i b/swig/bytes.i index 425d58b..b17d2a9 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -764,6 +764,16 @@ static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) //--------------------------------------------------------------------------- /* # +# Conversion options for get_ascii_contents2(): +ACFOPT_ASCII = 0x00000000 # convert Unicode strings to ASCII +ACFOPT_UTF16 = 0x00000001 # return UTF-16 (aka wide-char) array for Unicode strings + # ignored for non-Unicode strings +ACFOPT_UTF8 = 0x00000002 # convert Unicode strings to UTF-8 + # ignored for non-Unicode strings +ACFOPT_CONVMASK = 0x0000000F +ACFOPT_ESCAPE = 0x00000010 # for ACFOPT_ASCII, convert non-printable + # characters to C escapes (\n, \xNN, \uNNNN) + def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII): """ Get contents of ascii string diff --git a/swig/expr.i b/swig/expr.i index 1934667..b4cacbd 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -1,341 +1,341 @@ -%ignore extfun_t; -%ignore funcset_t; -%ignore extlang_t; -%ignore extlang; -%ignore extlangs_t; -%ignore extlangs; -%ignore register_extlang; -%ignore IDCFuncs; -%ignore set_idc_func; -%ignore set_idc_dtor; -%ignore set_idc_method; -%ignore set_idc_getattr; -%ignore set_idc_setattr; -%ignore set_idc_func_ex; -%ignore run_statements_idc; -%ignore VarLong; -%ignore VarNum; -%ignore extlang_get_attr_exists; -%ignore extlang_create_object_exists; -%ignore create_script_object; -%ignore set_script_attr; -%ignore set_attr_exists; -%ignore get_script_attr; -%ignore extlang_get_attr_exists; -%ignore extlang_compile_file; -%ignore get_extlangs; -%ignore create_idc_object; -%ignore run_script_func; -%ignore VarString; -%ignore VarFloat; -%ignore VarFree; -%ignore calcexpr_long; -%ignore Run; -%ignore ExecuteLine; -%ignore ExecuteFile; -%ignore set_idc_func_body; -%ignore get_idc_func_body; -%ignore idc_stacksize; -%ignore idc_calldepth; -%ignore expr_printf; -%ignore expr_sprintf; -%ignore expr_printfer; -%ignore init_idc; -%ignore term_idc; -%ignore create_default_idc_classes; -%ignore insn_to_idc; -%ignore find_builtin_idc_func; -%ignore idc_mutex; -%ignore idc_lx; -%ignore idc_vars; -%ignore idc_resolve_label; -%ignore idc_resolver_ea; -%ignore setup_lowcnd_regfuncs; -%cstring_output_maxstr_none(char *errbuf, size_t errbufsize); - -%ignore CompileEx; -%rename (CompileEx) CompileEx_wrap; -%ignore Compile; -%rename (Compile) Compile_wrap; -%ignore calcexpr; -%rename (calcexpr) calcexpr_wrap; -%ignore calc_idc_expr; -%rename (calc_idc_expr) calc_idc_expr_wrap; -%ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL); -%ignore CompileLineEx; -%ignore CompileLine; -%rename (CompileLine) CompileLine_wrap; -%{ -// -struct py_idcfunc_ctx_t -{ - PyObject *py_func; - qstring name; - int nargs; - py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) - { - Py_INCREF(py_func); - } - ~py_idcfunc_ctx_t() - { - Py_DECREF(py_func); - } -}; - -//--------------------------------------------------------------------------- -static error_t py_call_idc_func( - void *_ctx, - idc_value_t *argv, - idc_value_t *r) -{ - // Convert IDC arguments to Python list - py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; - int cvt; - ppyobject_vec_t pargs; - char errbuf[MAXSTR]; - if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, NULL, errbuf, sizeof(errbuf)) ) - { - // Error during conversion? Create an IDC exception - return PyW_CreateIdcException(r, errbuf); - } - - // Call the Python function - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallObject( - ctx->py_func, - pargs.empty() ? NULL : pargs[0]); - - error_t err; - if ( PyW_GetError(errbuf, sizeof(errbuf)) ) - { - err = PyW_CreateIdcException(r, errbuf); - Py_XDECREF(py_result); - } - else - { - // Convert the result to IDC - r->clear(); - cvt = pyvar_to_idcvar(py_result, r); - if ( cvt < CIP_OK ) - err = PyW_CreateIdcException(r, "ERROR: bad return value"); - else - err = eOk; - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_result); - } - PYW_GIL_RELEASE; - - // Free the converted args - pyw_free_idc_args(pargs); - - return err; -} - -// -%} - -%inline %{ -// - -//--------------------------------------------------------------------------- -static size_t py_get_call_idc_func() -{ - return (size_t)py_call_idc_func; -} - -//--------------------------------------------------------------------------- -// Internal function: -// - capture the python callable -// - return a C context as a numeric value -static size_t pyw_register_idc_func( - const char *name, - const char *args, - PyObject *py_fp) -{ - return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); -} - -//--------------------------------------------------------------------------- -// Internal function: -// - free the C context -static bool pyw_unregister_idc_func(size_t ctxptr) -{ - // Unregister the function - py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)ctxptr; - bool ok = set_idc_func_ex(ctx->name.c_str(), NULL, NULL, 0); - - // Delete the context - delete ctx; - - return ok; -} - -//--------------------------------------------------------------------------- -static bool py_set_idc_func_ex( - const char *name, - size_t fp_ptr, - const char *args, - int flags) -{ - return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); -} - -//--------------------------------------------------------------------------- -// Compile* functions return false when error so the return -// value must be negated for the error string to be returned -bool CompileEx_wrap( - const char *file, - bool del_macros, - char *errbuf, size_t errbufsize) -{ - return !CompileEx(file, del_macros, errbuf, errbufsize); -} - -bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) -{ - return !Compile(file, errbuf, errbufsize); -} - -bool calcexpr_wrap( - ea_t where, - const char *line, - idc_value_t *rv, - char *errbuf, size_t errbufsize) -{ - return !calcexpr(where, line, rv, errbuf, errbufsize); -} - -bool calc_idc_expr_wrap( - ea_t where, - const char *line, - idc_value_t *rv, - char *errbuf, size_t errbufsize) -{ - return !calc_idc_expr(where, line, rv, errbuf, errbufsize); -} - -bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) -{ - return !CompileLineEx(line, errbuf, errbufsize); -} - -// -%} - -%include "expr.hpp" - -%pythoncode %{ - -# -try: - import types - import ctypes - # Callback for IDC func callback (On Windows, we use stdcall) - # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); - _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) - - # A trampoline function that is called from idcfunc_t that will - # call the Python callback with the argv and r properly serialized to python - call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) -except: - def call_idc_func__(*args): - warning("IDC extensions need ctypes library in order to work") - return 0 - try: - _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) - except: - _IDCFUNC_CB_T = None - - -# -------------------------------------------------------------------------- -EXTFUN_BASE = 0x0001 -"""requires open database""" -EXTFUN_NORET = 0x0002 -"""does not return. the interpreter may clean up its state before calling it.""" -EXTFUN_SAFE = 0x0004 -"""thread safe function. may be called""" - -# -------------------------------------------------------------------------- -class _IdcFunction(object): - """ - Internal class that calls pyw_call_idc_func() with a context - """ - def __init__(self, ctxptr): - self.ctxptr = ctxptr - # Take a reference to the ctypes callback - # (note: this will create a circular reference) - self.cb = _IDCFUNC_CB_T(self) - - fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) - - def __call__(self, args, res): - return call_idc_func__(self.ctxptr, args, res) - - -# -------------------------------------------------------------------------- -# Dictionary to remember IDC function names along with the context pointer -# retrieved by using the internal pyw_register_idc_func() -__IDC_FUNC_CTXS = {} - -# -------------------------------------------------------------------------- -def set_idc_func_ex(name, fp=None, args=(), flags=0): - """ - Extends the IDC language by exposing a new IDC function that is backed up by a Python function - This function also unregisters the IDC function if 'fp' was passed as None - - @param name: IDC function name to expose - @param fp: Python callable that will receive the arguments and return a tuple. - If this argument is None then the IDC function is unregistered - @param args: Arguments. A tuple of idaapi.VT_XXX constants - @param flags: IDC function flags. A combination of EXTFUN_XXX constants - - @return: Boolean. - """ - global __IDC_FUNC_CTXS - - # Get the context - f = __IDC_FUNC_CTXS.get(name, None) - - # Unregistering? - if fp is None: - # Not registered? - if f is None: - return False - - # Break circular reference - del f.cb - - # Delete the name from the dictionary - del __IDC_FUNC_CTXS[name] - - # Delete the context and unregister the function - return _idaapi.pyw_unregister_idc_func(f.ctxptr) - - # Registering a function that is already registered? - if f is not None: - # Unregister it first - set_idc_func_ex(name, None) - - # Convert the tupple argument info to a string - args = "".join([chr(x) for x in args]) - - # Create a context - ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) - if ctxptr == 0: - return False - - # Bind the context with the IdcFunc object - f = _IdcFunction(ctxptr) - - # Remember the Python context - __IDC_FUNC_CTXS[name] = f - - # Register IDC function with a callback - return _idaapi.py_set_idc_func_ex( - name, - f.fp_ptr, - args, - flags) - -# +%ignore extfun_t; +%ignore funcset_t; +%ignore extlang_t; +%ignore extlang; +%ignore extlangs_t; +%ignore extlangs; +%ignore register_extlang; +%ignore IDCFuncs; +%ignore set_idc_func; +%ignore set_idc_dtor; +%ignore set_idc_method; +%ignore set_idc_getattr; +%ignore set_idc_setattr; +%ignore set_idc_func_ex; +%ignore run_statements_idc; +%ignore VarLong; +%ignore VarNum; +%ignore extlang_get_attr_exists; +%ignore extlang_create_object_exists; +%ignore create_script_object; +%ignore set_script_attr; +%ignore set_attr_exists; +%ignore get_script_attr; +%ignore extlang_get_attr_exists; +%ignore extlang_compile_file; +%ignore get_extlangs; +%ignore create_idc_object; +%ignore run_script_func; +%ignore VarString; +%ignore VarFloat; +%ignore VarFree; +%ignore calcexpr_long; +%ignore Run; +%ignore ExecuteLine; +%ignore ExecuteFile; +%ignore set_idc_func_body; +%ignore get_idc_func_body; +%ignore idc_stacksize; +%ignore idc_calldepth; +%ignore expr_printf; +%ignore expr_sprintf; +%ignore expr_printfer; +%ignore init_idc; +%ignore term_idc; +%ignore create_default_idc_classes; +%ignore insn_to_idc; +%ignore find_builtin_idc_func; +%ignore idc_mutex; +%ignore idc_lx; +%ignore idc_vars; +%ignore idc_resolve_label; +%ignore idc_resolver_ea; +%ignore setup_lowcnd_regfuncs; +%cstring_output_maxstr_none(char *errbuf, size_t errbufsize); + +%ignore CompileEx; +%rename (CompileEx) CompileEx_wrap; +%ignore Compile; +%rename (Compile) Compile_wrap; +%ignore calcexpr; +%rename (calcexpr) calcexpr_wrap; +%ignore calc_idc_expr; +%rename (calc_idc_expr) calc_idc_expr_wrap; +%ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL); +%ignore CompileLineEx; +%ignore CompileLine; +%rename (CompileLine) CompileLine_wrap; +%{ +// +struct py_idcfunc_ctx_t +{ + PyObject *py_func; + qstring name; + int nargs; + py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) + { + Py_INCREF(py_func); + } + ~py_idcfunc_ctx_t() + { + Py_DECREF(py_func); + } +}; + +//--------------------------------------------------------------------------- +static error_t py_call_idc_func( + void *_ctx, + idc_value_t *argv, + idc_value_t *r) +{ + // Convert IDC arguments to Python list + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; + int cvt; + ppyobject_vec_t pargs; + char errbuf[MAXSTR]; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, NULL, errbuf, sizeof(errbuf)) ) + { + // Error during conversion? Create an IDC exception + return PyW_CreateIdcException(r, errbuf); + } + + // Call the Python function + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0]); + + error_t err; + if ( PyW_GetError(errbuf, sizeof(errbuf)) ) + { + err = PyW_CreateIdcException(r, errbuf); + Py_XDECREF(py_result); + } + else + { + // Convert the result to IDC + r->clear(); + cvt = pyvar_to_idcvar(py_result, r); + if ( cvt < CIP_OK ) + err = PyW_CreateIdcException(r, "ERROR: bad return value"); + else + err = eOk; + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_result); + } + PYW_GIL_RELEASE; + + // Free the converted args + pyw_free_idc_args(pargs); + + return err; +} + +// +%} + +%inline %{ +// + +//--------------------------------------------------------------------------- +static size_t py_get_call_idc_func() +{ + return (size_t)py_call_idc_func; +} + +//--------------------------------------------------------------------------- +// Internal function: +// - capture the python callable +// - return a C context as a numeric value +static size_t pyw_register_idc_func( + const char *name, + const char *args, + PyObject *py_fp) +{ + return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); +} + +//--------------------------------------------------------------------------- +// Internal function: +// - free the C context +static bool pyw_unregister_idc_func(size_t ctxptr) +{ + // Unregister the function + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)ctxptr; + bool ok = set_idc_func_ex(ctx->name.c_str(), NULL, NULL, 0); + + // Delete the context + delete ctx; + + return ok; +} + +//--------------------------------------------------------------------------- +static bool py_set_idc_func_ex( + const char *name, + size_t fp_ptr, + const char *args, + int flags) +{ + return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); +} + +//--------------------------------------------------------------------------- +// Compile* functions return false when error so the return +// value must be negated for the error string to be returned +bool CompileEx_wrap( + const char *file, + bool del_macros, + char *errbuf, size_t errbufsize) +{ + return !CompileEx(file, del_macros, errbuf, errbufsize); +} + +bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) +{ + return !Compile(file, errbuf, errbufsize); +} + +bool calcexpr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calcexpr(where, line, rv, errbuf, errbufsize); +} + +bool calc_idc_expr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calc_idc_expr(where, line, rv, errbuf, errbufsize); +} + +bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) +{ + return !CompileLineEx(line, errbuf, errbufsize); +} + +// +%} + +%include "expr.hpp" + +%pythoncode %{ + +# +try: + import types + import ctypes + # Callback for IDC func callback (On Windows, we use stdcall) + # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); + _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) + + # A trampoline function that is called from idcfunc_t that will + # call the Python callback with the argv and r properly serialized to python + call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) +except: + def call_idc_func__(*args): + warning("IDC extensions need ctypes library in order to work") + return 0 + try: + _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) + except: + _IDCFUNC_CB_T = None + + +# -------------------------------------------------------------------------- +EXTFUN_BASE = 0x0001 +"""requires open database""" +EXTFUN_NORET = 0x0002 +"""does not return. the interpreter may clean up its state before calling it.""" +EXTFUN_SAFE = 0x0004 +"""thread safe function. may be called""" + +# -------------------------------------------------------------------------- +class _IdcFunction(object): + """ + Internal class that calls pyw_call_idc_func() with a context + """ + def __init__(self, ctxptr): + self.ctxptr = ctxptr + # Take a reference to the ctypes callback + # (note: this will create a circular reference) + self.cb = _IDCFUNC_CB_T(self) + + fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) + + def __call__(self, args, res): + return call_idc_func__(self.ctxptr, args, res) + + +# -------------------------------------------------------------------------- +# Dictionary to remember IDC function names along with the context pointer +# retrieved by using the internal pyw_register_idc_func() +__IDC_FUNC_CTXS = {} + +# -------------------------------------------------------------------------- +def set_idc_func_ex(name, fp=None, args=(), flags=0): + """ + Extends the IDC language by exposing a new IDC function that is backed up by a Python function + This function also unregisters the IDC function if 'fp' was passed as None + + @param name: IDC function name to expose + @param fp: Python callable that will receive the arguments and return a tuple. + If this argument is None then the IDC function is unregistered + @param args: Arguments. A tuple of idaapi.VT_XXX constants + @param flags: IDC function flags. A combination of EXTFUN_XXX constants + + @return: Boolean. + """ + global __IDC_FUNC_CTXS + + # Get the context + f = __IDC_FUNC_CTXS.get(name, None) + + # Unregistering? + if fp is None: + # Not registered? + if f is None: + return False + + # Break circular reference + del f.cb + + # Delete the name from the dictionary + del __IDC_FUNC_CTXS[name] + + # Delete the context and unregister the function + return _idaapi.pyw_unregister_idc_func(f.ctxptr) + + # Registering a function that is already registered? + if f is not None: + # Unregister it first + set_idc_func_ex(name, None) + + # Convert the tupple argument info to a string + args = "".join([chr(x) for x in args]) + + # Create a context + ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) + if ctxptr == 0: + return False + + # Bind the context with the IdcFunc object + f = _IdcFunction(ctxptr) + + # Remember the Python context + __IDC_FUNC_CTXS[name] = f + + # Register IDC function with a callback + return _idaapi.py_set_idc_func_ex( + name, + f.fp_ptr, + args, + flags) + +# %} \ No newline at end of file diff --git a/swig/funcs.i b/swig/funcs.i index fe4affe..5bac8ec 100644 --- a/swig/funcs.i +++ b/swig/funcs.i @@ -62,9 +62,8 @@ static ea_t get_fchunk_referer(ea_t ea, size_t idx) def get_idasgn_desc(n): """ Get information about a signature in the list. - It returns both: - signame - the name of the signature - optlibs - the names of the optional libraries + It returns both: the name of the signature, and names of the + optional libraries @param n: number of signature in the list (0..get_idasgn_qty()-1) @return: None on failure or tuple(signame, optlibs) diff --git a/swig/graph.i b/swig/graph.i index 81daf8f..4f32f24 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -564,7 +564,7 @@ private: void show() { - open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); + open_tform(form, FORM_TAB|FORM_MENU); } static py_graph_t *extract_this(PyObject *self) @@ -668,7 +668,7 @@ private: qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); id.create(grnode); gv = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_MDI | FORM_TAB | FORM_MENU); + open_tform(form, FORM_TAB | FORM_MENU); if ( gv != NULL ) viewer_fit_window(gv); } diff --git a/swig/idaapi.i b/swig/idaapi.i index f280dde..283eea2 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -15,7 +15,7 @@ %constant size_t SIZE_MAX = size_t(-1); // Enable automatic docstring generation -%feature(autodoc); +%feature(autodoc,0); %define SWIG_DECLARE_PY_CLINKED_OBJECT(type) %inline %{ @@ -2056,11 +2056,11 @@ bool pywraps_nw_term() { if ( g_nw == NULL ) return true; - + // If could not deinitialize then return w/o stopping nw if ( !g_nw->deinit() ) return false; - + // Cleanup delete g_nw; g_nw = NULL; @@ -2694,7 +2694,7 @@ def parse_command_line(cmdline): static PyObject *py_parse_command_line(const char *cmdline) { qstrvec_t args; - if ( parse_command_line(cmdline, &args) == 0 ) + if ( parse_command_line2(cmdline, &args, NULL) == 0 ) Py_RETURN_NONE; PyObject *py_list = PyList_New(args.size()); diff --git a/swig/idp.i b/swig/idp.i index 1534e65..ea5c4d3 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -475,7 +475,7 @@ class IDP_Hooks(object): - cmd.itype must be set >= idaapi.CUSTOM_CMD_ITYPE - cmd.size must be set to the instruction length - @return: Boolean + @return: Boolean - False if the instruction is not recognized - True if the instruction was decoded. idaapi.cmd should be filled in that case. """ diff --git a/swig/kernwin.i b/swig/kernwin.i index 2db1d1e..5d348e9 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -619,7 +619,7 @@ in this case. This flag can be used to delay the code execution until the next UI loop run even from the main thread""" -def execute_sync(callable, reqf) +def execute_sync(callable, reqf): """ Executes a function in the context of the main thread. If the current thread not the main thread, then the call is queued and @@ -692,7 +692,7 @@ static int py_execute_sync(PyObject *py_callable, int reqf) /* # -def execute_ui_requests(callable_list) +def execute_ui_requests(callable_list): """ Inserts a list of callables into the UI message processing queue. When the UI is ready it will call one callable. @@ -885,7 +885,11 @@ class UI_Hooks(object): IDA is terminated and the database is already closed. The UI may close its windows in this callback. """ - pass + # if the user forgot to call unhook, do it for him + self.unhook() + + def __term__(self): + self.term() # */ @@ -894,6 +898,7 @@ class UI_Hooks public: virtual ~UI_Hooks() { + unhook(); } bool hook() @@ -2690,10 +2695,10 @@ private: PYW_GIL_ENSURE; PyObject *py_result = PyObject_CallMethod( _this->py_obj, - (char *)S_ON_CREATE, "O", + (char *)S_ON_CREATE, "O", PyCObject_FromVoidPtr(form, NULL)); PYW_GIL_RELEASE; - + PyW_ShowCbErr(S_ON_CREATE); Py_XDECREF(py_result); } @@ -2706,10 +2711,10 @@ private: PYW_GIL_ENSURE; PyObject *py_result = PyObject_CallMethod( _this->py_obj, - (char *)S_ON_CLOSE, "O", + (char *)S_ON_CLOSE, "O", PyCObject_FromVoidPtr(form, NULL)); PYW_GIL_RELEASE; - + PyW_ShowCbErr(S_ON_CLOSE); Py_XDECREF(py_result); @@ -2723,7 +2728,7 @@ private: { unhook_from_notification_point(HT_UI, s_callback, this); form = NULL; - + // Call DECREF at last, since it may trigger __del__ Py_XDECREF(py_obj); } @@ -2735,7 +2740,7 @@ public: bool show( PyObject *obj, - const char *caption, + const char *caption, int options) { // Already displayed? @@ -2757,7 +2762,7 @@ public: form = create_tform(caption, NULL); if ( form == NULL ) return false; - + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) { form = NULL; @@ -2785,7 +2790,7 @@ public: { return PyCObject_FromVoidPtr(new plgform_t(), destroy); } - + static void destroy(void *obj) { delete (plgform_t *)obj; @@ -2805,9 +2810,9 @@ static PyObject *plgform_new() static bool plgform_show( PyObject *py_link, - PyObject *py_obj, - const char *caption, - int options = FORM_MDI|FORM_TAB|FORM_MENU|FORM_RESTORE) + PyObject *py_obj, + const char *caption, + int options = FORM_TAB|FORM_MENU|FORM_RESTORE) { DECL_PLGFORM; return plgform->show(py_obj, caption, options); @@ -5531,7 +5536,7 @@ class PluginForm(object): """ FORM_MDI = 0x01 - """start by default as MDI""" + """start by default as MDI (obsolete)""" FORM_TAB = 0x02 """attached by default to a tab""" FORM_RESTORE = 0x04 @@ -5560,7 +5565,7 @@ class PluginForm(object): @param caption: The form caption @param options: One of PluginForm.FORM_ constants """ - options |= PluginForm.FORM_MDI|PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + options |= PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE return _idaapi.plgform_show(self.__clink__, self, caption, options) diff --git a/swig/name.i b/swig/name.i index 3c3e45d..012b33e 100644 --- a/swig/name.i +++ b/swig/name.i @@ -1,111 +1,111 @@ -%cstring_output_maxstr_none(char *buf, int bufsize); - -%cstring_bounded_output(char *dstname, MAXSTR); -%cstring_bounded_output(char *buf, MAXSTR); - -// This is for get_name_value's output value -%apply unsigned long *OUTPUT { uval_t *value }; - -// FIXME: These should be fixed -%ignore append_struct_fields; -%ignore get_struct_operand; -%ignore set_debug_names; -%ignore get_debug_name; -%ignore nameVa; - -// Unexported & kernel-only -%ignore get_short_name; -%ignore get_long_name; -%ignore get_colored_short_name; -%ignore get_colored_long_name; -%ignore addDummyName; -%ignore convert_debug_names_to_normal; -%ignore convert_name_formats; -%ignore showhide_name; -%ignore clear_lname_bit; -%ignore fix_new_name; -%ignore rename; -%ignore move_names; -%ignore is_noret_name; -%ignore is_exit_name; -%ignore dummy_name_ea; - -%ignore get_debug_names; -%rename (get_debug_names) py_get_debug_names; -%inline %{ -// -//------------------------------------------------------------------------ -PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) -{ - // Get debug names - ea_name_vec_t names; - get_debug_names(ea1, ea2, names); - PyObject *dict = Py_BuildValue("{}"); - if (dict == NULL) - return NULL; - - for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) - { - PyDict_SetItem(dict, - Py_BuildValue(PY_FMT64, it->ea), - PyString_FromString(it->name.c_str())); - } - return dict; -} -//------------------------------------------------------------------------ -// -%} - -%pythoncode %{ -# - -class NearestName: - """ - Utility class to help find the nearest name in a given ea/name dictionary - """ - def __init__(self, ea_names): - self.update(ea_names) - - - def update(self, ea_names): - """Updates the ea/names map""" - self._names = ea_names - self._addrs = ea_names.keys() - self._addrs.sort() - - - def find(self, ea): - """ - Returns a tupple (ea, name, pos) that is the nearest to the passed ea - If no name is matched then None is returned - """ - pos = bisect.bisect_left(self._addrs, ea) - # no match - if pos >= len(self._addrs): - return None - # exact match? - if self._addrs[pos] != ea: - pos -= 1 # go to previous element - if pos < 0: - return None - return self[pos] - - - def _get_item(self, index): - ea = self._addrs[index] - return (ea, self._names[ea], index) - - - def __iter__(self): - return (self._get_item(index) for index in xrange(0, len(self._addrs))) - - - def __getitem__(self, index): - """Returns the tupple (ea, name, index)""" - if index > len(self._addrs): - raise StopIteration - return self._get_item(index) - -# -%} -%include "name.hpp" +%cstring_output_maxstr_none(char *buf, int bufsize); + +%cstring_bounded_output(char *dstname, MAXSTR); +%cstring_bounded_output(char *buf, MAXSTR); + +// This is for get_name_value's output value +%apply unsigned long *OUTPUT { uval_t *value }; + +// FIXME: These should be fixed +%ignore append_struct_fields; +%ignore get_struct_operand; +%ignore set_debug_names; +%ignore get_debug_name; +%ignore nameVa; + +// Unexported & kernel-only +%ignore get_short_name; +%ignore get_long_name; +%ignore get_colored_short_name; +%ignore get_colored_long_name; +%ignore addDummyName; +%ignore convert_debug_names_to_normal; +%ignore convert_name_formats; +%ignore showhide_name; +%ignore clear_lname_bit; +%ignore fix_new_name; +%ignore rename; +%ignore move_names; +%ignore is_noret_name; +%ignore is_exit_name; +%ignore dummy_name_ea; + +%ignore get_debug_names; +%rename (get_debug_names) py_get_debug_names; +%inline %{ +// +//------------------------------------------------------------------------ +PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) +{ + // Get debug names + ea_name_vec_t names; + get_debug_names(ea1, ea2, names); + PyObject *dict = Py_BuildValue("{}"); + if (dict == NULL) + return NULL; + + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } + return dict; +} +//------------------------------------------------------------------------ +// +%} + +%pythoncode %{ +# + +class NearestName: + """ + Utility class to help find the nearest name in a given ea/name dictionary + """ + def __init__(self, ea_names): + self.update(ea_names) + + + def update(self, ea_names): + """Updates the ea/names map""" + self._names = ea_names + self._addrs = ea_names.keys() + self._addrs.sort() + + + def find(self, ea): + """ + Returns a tupple (ea, name, pos) that is the nearest to the passed ea + If no name is matched then None is returned + """ + pos = bisect.bisect_left(self._addrs, ea) + # no match + if pos >= len(self._addrs): + return None + # exact match? + if self._addrs[pos] != ea: + pos -= 1 # go to previous element + if pos < 0: + return None + return self[pos] + + + def _get_item(self, index): + ea = self._addrs[index] + return (ea, self._names[ea], index) + + + def __iter__(self): + return (self._get_item(index) for index in xrange(0, len(self._addrs))) + + + def __getitem__(self, index): + """Returns the tupple (ea, name, index)""" + if index > len(self._addrs): + raise StopIteration + return self._get_item(index) + +# +%} +%include "name.hpp" diff --git a/swig/pro.i b/swig/pro.i index feaebc7..0a935c8 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -83,7 +83,17 @@ %ignore get_process_exit_code; %ignore BELOW_NORMAL_PRIORITY_CLASS; %ignore parse_command_line; -%rename (parse_command_line) py_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) diff --git a/swig/typeinf.i b/swig/typeinf.i index 576701a..392c78f 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -87,6 +87,7 @@ %ignore decorate_name; %ignore gen_decorate_name; %ignore calc_bare_name; +%ignore calc_cpp_name; %ignore predicate_t; %ignore choose_named_type; %ignore get_default_align; @@ -131,6 +132,14 @@ %ignore type_pair_vec_t::add_names; +%ignore format_data_info_t; +%ignore valinfo_t; +%ignore print_c_data; +%ignore format_c_data; +%ignore format_c_number; +%ignore get_enum_member_expr; +%ignore extend_sign; + // Kernel-only symbols %ignore init_til; %ignore save_til; @@ -161,6 +170,7 @@ %rename (load_til) load_til_wrap; %rename (get_type_size0) py_get_type_size0; +%rename (idc_get_type_raw) py_idc_get_type_raw; %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; @@ -266,12 +276,12 @@ PyObject *py_unpack_object_from_idb( 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, + &idc_obj, + ti, + type, + fields, + ea, + NULL, pio_flags); // Unpacking failed? @@ -281,7 +291,7 @@ PyObject *py_unpack_object_from_idb( // 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); @@ -334,11 +344,11 @@ PyObject *py_unpack_object_from_bv( idc_value_t idc_obj; error_t err = unpack_object_from_bv( - &idc_obj, - ti, - type, - fields, - bytes, + &idc_obj, + ti, + type, + fields, + bytes, pio_flags); // Unpacking failed? @@ -519,6 +529,19 @@ int idc_parse_types(const char *input, int flags) return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); } +PyObject *py_idc_get_type_raw(ea_t ea) +{ + qtype type, fields; + if (get_tinfo(ea, &type, &fields)) + { + return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); + } + else + { + Py_RETURN_NONE; + } +} + char *idc_get_type(ea_t ea, char *buf, size_t bufsize) { qtype type, fnames; diff --git a/tools/swigdocs.py b/tools/swigdocs.py index a9b85d4..2d58ee7 100644 --- a/tools/swigdocs.py +++ b/tools/swigdocs.py @@ -5,6 +5,7 @@ # import glob import sys +import os # --------------------------------------------------------------------------- def extract_docs(lines, out): @@ -67,17 +68,29 @@ def extract_docs(lines, out): elif line == S_INLINE: in_inline = True +# --------------------------------------------------------------------------- +def gen_docs_from(file, out): + f = open(file, 'r') + lines = f.readlines() + f.close() + extract_docs(lines, out) + # --------------------------------------------------------------------------- def gen_docs(path = '../swig/', outfn = 'idaapi.py', mask = '*.i'): out = [] + + # idaapi contains definitions used by other modules + # handle it first + idaapi_i = os.path.join(path, 'idaapi.i') + gen_docs_from(idaapi_i, out) + for fn in glob.glob(path + mask): - f = open(fn, 'r') - lines = f.readlines() - f.close() - extract_docs(lines, out) + fn = fn.replace('\\', '/') + if fn != idaapi_i: + gen_docs_from(fn, out) f = open(outfn, 'w') - f.write('"""This is a placeholder module used to document all the IDA SDK functions that are wrapped manually. You still need to import \'idaapi\' and not this module to use the functions"""\n') +# f.write('"""This is a placeholder module used to document all the IDA SDK functions that are wrapped manually. You still need to import \'idaapi\' (not this module) to use the functions"""\n') f.write('\n'.join(out)) f.close()