diff --git a/examples/hexrays/vds17.py b/examples/hexrays/vds17.py index 20bd52c..b247744 100644 --- a/examples/hexrays/vds17.py +++ b/examples/hexrays/vds17.py @@ -13,6 +13,8 @@ import ida_idaapi import ida_hexrays +import ida_lines +import ida_typeinf # -------------------------------------------------------------------------- class func_stroff_ah_t(ida_kernwin.action_handler_t): @@ -20,11 +22,12 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): ida_kernwin.action_handler_t.__init__(self) def activate(self, ctx): - # get current item + # get the current item vu = ida_hexrays.get_widget_vdui(ctx.widget) - vu.get_current_item(idaapi.USE_KEYBOARD) + vu.get_current_item(ida_hexrays.USE_KEYBOARD) - # check the current item is union field + # REGION1, will be referenced latter + # check that the current item is a union field if not vu.item.is_citem(): return 0 e = vu.item.e @@ -41,8 +44,10 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): break if not e.type.is_udt(): return 0 + # END REGION1 - # calculate member's offset + # REGION2 + # calculate the member offset off = 0 e = vu.item.e while True: @@ -55,7 +60,9 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): break if not e2.type.is_udt(): break + # END REGION2 + # REGION3 # go up and collect more member references (in order to calculate the final offset) p = vu.item.e while True: @@ -63,8 +70,8 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): if p2.op == ida_hexrays.cot_memptr: break if p2.op == ida_hexrays.cot_memref: - e2 = p2 - tif = remove_pointer(e2.x.type) + e2 = p2.cexpr + tif = ida_typeinf.remove_pointer(e2.x.type) if not tif.is_union(): off += e2.m p = p2 @@ -81,45 +88,59 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): objsize = add.type.get_ptrarr_objsize() nbytes = delta * objsize off += nbytes - # we can use the calling helpers like WORD/BYTE/... - # to calculate the more precise offset + # we could use helpers like WORD/BYTE/... to calculate a more precise offset # if ( p2->op == cot_call && (e2->exflags & EXFL_LVALUE) != 0 ) break + # END REGION3 + # REGION4 ea = vu.item.e.ea # the item itself may be unaddressable. # TODO: find its addressable parent - if ea == idaapi.BADADDR: + if ea == ida_idaapi.BADADDR: return 0 + # END REGION4 + # REGION5 # prepare the text representation for the item, # use the neighborhoods of cursor - line = idaapi.tag_remove(ida_kernwin.get_custom_viewer_curline(vu.ct, False)) + line = ida_lines.tag_remove(ida_kernwin.get_custom_viewer_curline(vu.ct, False)) line_len = len(line) x = max(0, vu.cpos.x - 10) l = min(10, line_len - vu.cpos.x) + 10 line = line[x:x+l] + # END REGION5 + # REGION6 ops = ida_hexrays.ui_stroff_ops_t() op = ops.push_back() op.offset = off op.text = line + # END REGION6 + # REGION7 class set_union_sel_t(ida_hexrays.ui_stroff_applicator_t): - def __init__(self, eas): + def __init__(self, ea): ida_hexrays.ui_stroff_applicator_t.__init__(self) - self.eas = eas + self.ea = ea - def apply(self, opnum, path): - vu.cfunc.set_user_union_selection(self.eas[opnum], path) + def apply(self, opnum, path, top_tif, spath): + typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, top_tif, '', '') + idaapi.msg("User selected %s of type %s\n" % (spath, typename)) + if path.empty(): + return False + vu.cfunc.set_user_union_selection(self.ea, path) vu.cfunc.save_user_unions() return True + # END REGION7 - su = set_union_sel_t([ea]) + # REGION8 + su = set_union_sel_t(ea) res = ida_hexrays.select_udt_by_offset(None, ops, su) if res != 0: # regenerate ctree vu.refresh_view(True) + # END REGION8 return 1 @@ -131,7 +152,7 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t): # -------------------------------------------------------------------------- if ida_hexrays.init_hexrays_plugin(): - print("Hex-rays version %s has been detected, Structure offsets ready to use" % idaapi.get_hexrays_version()) + print("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays.get_hexrays_version()) ida_kernwin.register_action( ida_kernwin.action_desc_t( "vds17:strchoose", @@ -140,3 +161,121 @@ if ida_hexrays.init_hexrays_plugin(): "Shift+T")) else: print('vds17: Hex-rays is not available.') + +""" +# A few notes about the VDS17 sample + +You can find two VDS17 samples in the IDA Pro install directory: + + python/examples/hexrays/vds17.py + plugins/hexrays_sdk/plugins/vds17 + +The former is an IDAPython plugin and the latter is a C++ IDA Pro plugin. +Actually they have the same functionality. +Just to be more concrete the vds17.py plugin will be used below. + + +## How the user interface works + +Let us suppose that we have the following local types: + + 1 C struct {int c0;int c1;} + 2 U union {int u0;__int16 u1;} + 3 D struct {int d0;C d1;U d2;} + 4 E struct {int e0;D e1;} + 5 res_t struct {int r0;int r1;__int16 r2;} + +and the decompiler generates the following pseudocode: + + void __cdecl f(res_t *r) + { + r->r0 = e->e1.d1.c0; + r->r1 = e->e1.d2.u0; + r->r2 = e->e1.d2.u1; + } + +As we see, it looks good, the decompiler did a really good job. + +But let us imagine that we need reference to the ```e1.d2.u0``` union member on the last line. + +At first we need to load the VDS17 plugin. For that, +place the cursor at the ```u1``` union member on the last line +and use ```Shift-T```. + +The "Structure offsets" dialog appears on the screen. +The left pane of the dialog contains the available local types +and the right pane has the following view: + + [checked] e->e1.d2.u1; | 10h | | + +Use the left pane to select ```E```, expand it, and select (Double-click) on the ```u0``` member. + +The right pane will change to: + + [checked] e->e1.d2.u1; | 10h | [checke] E.e1.d2.u0 | + +Since this is what we want, press the ```OK``` button and the pseudocode gets changed: + + void __cdecl f(res_t *r) + { + r->r0 = e->e1.d1.c0; + r->r1 = e->e1.d2.u0; + r->r2 = e->e1.d2.u0; + } + + +### What do we need this plugin for? + +All union members have the same offset in the parent structure. +The decompiler selects the first suitable union member. +Sometimes we need to change this selection and use another union member. + + +## API details + +Let us look into ```python/examples/hexrays/vds17.py```. + +* REGION1: check that the cursor points to the union member +* REGION2: calculate the member offset +* REGION3: sometimes there is a need to adjust the offset +* REGION4: the pointed item must be addressable + +Then the magic begins: + +* REGION5: we need something to show in the ```Operand``` column of the right pane. + The text around the cursor looks like a good compromise. +* REGION6: we are ready to fill the right pane: we have calculated the offset and + have prepared the descriptive text for the operand. +* REGION7: it is a callback that informs us about the user selection (will be described later) +* REGION8: activate the "Structure offsets" dialog, let the user select, + the callback specified above will update the union member, refresh pseudocode + + +### The devil is in the details + +The main part of callback is the ```apply``` method (REGION7). +It receives two arguments: + +1. ```opnum``` is the number of the selected operand (line) in the right pane of the dialog. + We need something to map the line number to our operand. + In the case of VDS17 ```ea``` performs this role. +2. ```path``` the path that describes the union selection. +3. ```top_tif``` typeinfo of the top-level UDT which user selected +4. ```spath``` the field names path to the selected member + +The union selection path is denotes a concrete member inside a UDT. + +For structure types there is no need in the union selection path because the member +offset uniquely denotes the desired member. + +For unions, on the other hand, the member offset is not enough because all union +members start at the same offset zero. For them, we remember the ordinal number of the +union member in the path. E.g., for the local types given above, the following holds: + +* e1.d1.c0 is denoted by an empty path because it does not have any unions +* e1.d2.u0 is denoted by path consisting of 0: we use the first member of U +* e1.d2.u1 is denoted by path consisting of 1: we use the second member of U + +You can retrieve the union selection path using API call ```get_user_union_selection``` +or apply it using ```set_user_union_selection```. +""" diff --git a/idapyswitch_linux.cpp b/idapyswitch_linux.cpp index 643e86d..73c6537 100644 --- a/idapyswitch_linux.cpp +++ b/idapyswitch_linux.cpp @@ -151,10 +151,10 @@ static bool find_libpython_dt_needed_info( //------------------------------------------------------------------------- static bool patch_dt_needed( const char *path, - const qstring &replacement, + const qstring &_replacement, qstring *errbuf) { - out_verb("Setting relevant DT_NEEDED of \"%s\" to \"%s\"\n", path, replacement.c_str()); + out_verb("Setting relevant DT_NEEDED of \"%s\" to \"%s\"\n", path, _replacement.c_str()); out_ident_inc_t iinc; linput_t *linput = open_linput(path, /*remote=*/ false); if ( linput == nullptr ) @@ -214,13 +214,24 @@ static bool patch_dt_needed( room = qltell(reader.get_linput()) - dt_needed_off - 1; } - const size_t nbytes = replacement.length(); + bytevec_t replacement; + replacement.append(_replacement.c_str(), _replacement.length() + 1); + size_t nbytes = replacement.size(); out_verb("We have room for %" FMT_Z " bytes, and need to write %" FMT_Z "\n", room, nbytes); // and patch if ( room >= nbytes ) { + if ( room > nbytes ) + { + out_verb("Expanding replacement with %" FMT_Z " '\\0' bytes, to " + "override possible previous soname that could derail " + "later computation of available room.\n", room - nbytes); + replacement.resize(room, 0); + nbytes = replacement.size(); + } + FILE *fp = openM(path); if ( fp != nullptr ) { @@ -230,7 +241,7 @@ static bool patch_dt_needed( if ( !args.dry_run ) { // we want to write the zero as well! - if ( qfwrite(fp, replacement.c_str(), nbytes+1) == nbytes+1 ) + if ( qfwrite(fp, replacement.begin(), nbytes) == nbytes ) { out_verb("File \"%s\" successfully patched\n", path); } @@ -243,7 +254,7 @@ static bool patch_dt_needed( else { out("Would write %" FMT_Z " bytes (\"%s\") to file\n", - nbytes, replacement.c_str()); + nbytes, replacement.begin()); } } else @@ -263,7 +274,7 @@ static bool patch_dt_needed( errbuf->sprnt("Replacement \"%s\" has a length of %" FMT_Z " bytes, but there is only room for %" FMT_Z "" " bytes in the file. Cannot proceed.\n", - replacement.c_str(), nbytes, room); + replacement.begin(), nbytes, room); return false; } return true; @@ -460,9 +471,7 @@ static bool split_debug_expand_libpython3_dtneeded_room( out_verb("\"%s\" command successful. Restoring the " "original DT_NEEDED of \"%s\"\n", cmdline.c_str(), dt_needed); - qstring padded_dt_needed(dt_needed); - padded_dt_needed.resize(SLOT_SIZE, '\0'); - if ( !patch_dt_needed(path, padded_dt_needed, errbuf) ) + if ( !patch_dt_needed(path, dt_needed, errbuf) ) return false; } else diff --git a/idapyswitch_win.cpp b/idapyswitch_win.cpp index 0c2f9ac..22fe902 100644 --- a/idapyswitch_win.cpp +++ b/idapyswitch_win.cpp @@ -167,14 +167,19 @@ static bool has_appx_path(qstrvec_t paths) if ( appx_path.empty() ) { HKEY hkey; + bool ok = false; if ( RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx", 0, KEY_READ, &hkey) == ERROR_SUCCESS ) { - if ( !read_string(&appx_path, hkey, L"PackageRoot") ) - appx_path = ""; + ok = read_string(&appx_path, hkey, L"PackageRoot"); RegCloseKey(hkey); } + if ( !ok ) + { + // no Appx support, set to dummy value which doesn't occur in paths + appx_path = ""; + } } - for ( auto path : paths ) + for ( const qstring &path : paths ) if ( path.find(appx_path) != qstring::npos ) return true; diff --git a/idapython.cpp b/idapython.cpp index 90e95b5..11f5cc5 100644 --- a/idapython.cpp +++ b/idapython.cpp @@ -254,6 +254,9 @@ void ida_export idapython_hide_wait_box() #define LEXEC(...) #endif +#define AVERAGE_STEPS_COUNT 10 +#define MAX_STEPS_COUNT ((AVERAGE_STEPS_COUNT * 2) + 1) + //------------------------------------------------------------------------- void execution_t::reset_steps() { @@ -268,7 +271,7 @@ void execution_t::reset_steps() // If we never hit the 'trace' callback while in the 'while True' loop // but always when performing the call to the processor module's 'out/outop' // then the loop will never stop. That was happening on windows (optimized.) - steps_before_action = 1 + rand() % 20; + steps_before_action = 1 + rand() % (AVERAGE_STEPS_COUNT*2); } //------------------------------------------------------------------------- @@ -300,6 +303,7 @@ void execution_t::stop_tracking() void execution_t::sync_to_present_time() { time_t now = time(NULL); + LEXEC("execution_t (%p)::sync_to_present_time() now=%d\n", this, int(now)); for ( size_t i = 0, n = entries.size(); i < n; ++i ) entries[i].etime = now; maybe_hide_wait_box(); @@ -342,16 +346,38 @@ void execution_t::reset_current_start_time() //------------------------------------------------------------------------ int execution_t::on_trace(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg) { - LEXEC("on_trace() (steps=%d, nentries=%d)\n", +#ifdef TESTABLE_BUILD + // ensure there was no decrement overflow + QASSERT(0, execution.steps_before_action <= MAX_STEPS_COUNT); +#endif + LEXEC("on_trace() (steps_before_action=%u, entries.size()=%d, timeout=%d)\n", int(execution.steps_before_action), - int(execution.entries.size())); - // we don't want to query for time at every trace event - if ( execution.steps_before_action-- > 0 ) + int(execution.entries.size()), + execution.timeout); + if ( execution.timeout < 0 ) + { + LEXEC("on_trace()::no timeout currently set (%d).\n", execution.timeout); return 0; + } + + // we don't want to query for time at every trace event + if ( execution.steps_before_action > 0 ) + { + --execution.steps_before_action; + return 0; + } if ( get_active_modal_widget() != NULL ) { LEXEC("on_trace()::a modal widget is active. Not showing our 'interrupt dialog'.\n"); + + // in addition, we want to sync the "start time" to now, so that + // the timeout will be relative to that (otherwise, calling + // ask_file() might end up showing the waitdialog for a fraction + // of a second after `_ida_kernwin.ask_file()` returns, but before + // the `ida_kernwin.ask_file()` one does.) + execution.sync_to_present_time(); + return 0; } @@ -377,8 +403,7 @@ int execution_t::on_trace(PyObject *obj, PyFrameObject *frame, int what, PyObjec { if ( PyErr_Occurred() == NULL ) { - LEXEC("on_trace()::INTERRUPTING (setting 'User interrupted' exception) at line %d\n", - PyFrame_GetLineNumber(frame)); + LEXEC("on_trace()::INTERRUPTING (setting 'User interrupted' exception)\n"); PyErr_SetString(PyExc_KeyboardInterrupt, "User interrupted"); } return -1; diff --git a/out_of_tree/parsed_notifications.zip b/out_of_tree/parsed_notifications.zip index d68c926..3492405 100644 Binary files a/out_of_tree/parsed_notifications.zip and b/out_of_tree/parsed_notifications.zip differ diff --git a/python/idc.py b/python/idc.py index 9c6297c..a9971bd 100644 --- a/python/idc.py +++ b/python/idc.py @@ -444,8 +444,7 @@ def save_database(idbname, flags=0): if len(idbname) == 0: idbname = get_idb_path() mask = ida_loader.DBFL_KILL | ida_loader.DBFL_COMP | ida_loader.DBFL_BAK - res = ida_loader.save_database_ex(idbname, flags & mask) - return res + return ida_loader.save_database(idbname, flags & mask) DBFL_BAK = ida_loader.DBFL_BAK # for compatiblity with older versions, eventually delete this @@ -509,14 +508,14 @@ def delete_all_segments(): ida_name.del_global_name(ea) func = ida_funcs.get_func(ea) if func: - ida_funcs.del_func_cmt(func, False) - ida_funcs.del_func_cmt(func, True) + ida_funcs.set_func_cmt(func, "", False) + ida_funcs.set_func_cmt(func, "", True) ida_funcs.del_func(ea) ida_bytes.del_hidden_range(ea) seg = ida_segment.getseg(ea) if seg: - ida_segment.del_segment_cmt(seg, False) - ida_segment.del_segment_cmt(seg, True) + ida_segment.set_segment_cmt(seg, "", False) + ida_segment.set_segment_cmt(seg, "", True) ida_segment.del_segm(ea, ida_segment.SEGMOD_KEEP | ida_segment.SEGMOD_SILENT) ea = ida_bytes.next_head(ea, ida_ida.cvar.inf.max_ea) @@ -1007,18 +1006,7 @@ def op_offset_high16(ea, n, target): def MakeVar(ea): - """ - Mark the location as "variable" - - @param ea: address to mark - - @return: None - - @note: All that IDA does is to mark the location as "variable". - Nothing else, no additional analysis is performed. - This function may disappear in the future. - """ - ida_bytes.doVar(ea, 1) + pass # Every anterior/posterior line has its number. # Anterior lines have numbers from E_PREV diff --git a/pywraps.cpp b/pywraps.cpp index 14af288..477161c 100644 --- a/pywraps.cpp +++ b/pywraps.cpp @@ -358,6 +358,12 @@ static int get_pyidc_cvt_type(PyObject *py_var) return int(IDAPyIntOrLong_AsLong(attr.o)); } +//------------------------------------------------------------------------- +static inline bool is_pyidc_cvt_type_int64(PyObject *py_var) +{ + return get_pyidc_cvt_type(py_var) == PY_ICID_INT64; +} + //------------------------------------------------------------------------- // Utility function to convert a python object to an IDC object // and sets a python exception on failure. @@ -736,15 +742,9 @@ int ida_export pyvar_to_idcvar( } //------------------------------------------------------------------------- -inline PyObject *cvt_to_pylong(int32 v) -{ - return PyLong_FromLong(v); -} - -inline PyObject *cvt_to_pylong(int64 v) -{ - return PyLong_FromLongLong(v); -} +// helpers to use with idc_value_t::num (which can be 32-, or 64-bit.) +inline PyObject *cvt_to_pylong(int32 v) { return PyLong_FromLong(v); } +inline PyObject *cvt_to_pylong(int64 v) { return PyLong_FromLongLong(v); } //------------------------------------------------------------------------- // Converts an IDC variable to a Python variable @@ -786,8 +786,7 @@ int ida_export idcvar_to_pyvar( if ( *py_var != NULL ) { // Recycling an int64 object? - int t = get_pyidc_cvt_type(py_var->o); - if ( t != PY_ICID_INT64 ) + if ( !is_pyidc_cvt_type_int64(py_var->o) ) return CIP_IMMUTABLE; // Cannot recycle immutable object // Update the attribute PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); @@ -818,7 +817,14 @@ int ida_export idcvar_to_pyvar( case VT_LONG: // Cannot recycle immutable objects if ( *py_var != NULL ) - return CIP_IMMUTABLE; + { + // Recycling an int64 object? + if ( !is_pyidc_cvt_type_int64(py_var->o) ) + return CIP_IMMUTABLE; + // Update the attribute + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, cvt_to_pylong(idc_var.num)); + return CIP_OK; + } *py_var = newref_t(cvt_to_pylong(idc_var.num)); break; case VT_FLOAT: diff --git a/pywraps.hpp b/pywraps.hpp index 59390e4..074f485 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -842,7 +842,9 @@ protected: { // identifier { - ref_t py_id = newref_t(PyObject_GetAttrString(self, "id")); + ref_t py_id; + if ( PyObject_HasAttrString(self, "id") ) + py_id = newref_t(PyObject_GetAttrString(self, "id")); if ( py_id == NULL || !IDAPyStr_Check(py_id.o) ) py_id = newref_t(PyObject_Repr(self)); if ( py_id != NULL && IDAPyStr_Check(py_id.o) ) diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py index 90d6f1a..ef9fcbe 100644 --- a/pywraps/py_kernwin.py +++ b/pywraps/py_kernwin.py @@ -126,6 +126,15 @@ class quick_widget_commands_t: cmd.icon) attach_dynamic_action_to_popup(widget, popup, desc) +class disabled_script_timeout_t(object): + def __enter__(self): + import _ida_idaapi + self.was_timeout = _ida_idaapi.set_script_timeout(0) + + def __exit__(self, type, value, tb): + import _ida_idaapi + _ida_idaapi.set_script_timeout(self.was_timeout) + # ---------------------------------------------------------------------- # bw-compat/deprecated. You shouldn't rely on this in new code from ida_pro import str2user diff --git a/pywraps/py_kernwin_askform.py b/pywraps/py_kernwin_askform.py index 9277cae..c78e92e 100644 --- a/pywraps/py_kernwin_askform.py +++ b/pywraps/py_kernwin_askform.py @@ -1384,15 +1384,12 @@ except: def __call_form_callable(call, *args): assert(len(args)) - old = _ida_idaapi.set_script_timeout(0) - try: + with disabled_script_timeout_t(): if sys.version_info.major >= 3 and isinstance(args[0], str): largs = list(args) largs[0] = largs[0].encode("UTF-8") args = tuple(largs) r = call(*args) - finally: - _ida_idaapi.set_script_timeout(old) return r def ask_form(*args): diff --git a/pywraps/py_kernwin_choose.py b/pywraps/py_kernwin_choose.py index 6da1234..e0b0743 100644 --- a/pywraps/py_kernwin_choose.py +++ b/pywraps/py_kernwin_choose.py @@ -205,9 +205,8 @@ class Choose(object): self.flags |= Choose.CH_MODAL # Disable the timeout - old = _ida_idaapi.set_script_timeout(0) - n = _ida_kernwin.choose_choose(self) - _ida_idaapi.set_script_timeout(old) + with disabled_script_timeout_t(): + n = _ida_kernwin.choose_choose(self) # Delete the modal chooser instance self.Close() diff --git a/release_pydoc_injections2.txt b/release_pydoc_injections2.txt index a649484..a41138f 100644 --- a/release_pydoc_injections2.txt +++ b/release_pydoc_injections2.txt @@ -31124,7 +31124,7 @@ class ui_stroff_applicator_t(__builtin__.object) | __repr__ = _swig_repr(self) | | apply(self, *args) - | apply(self, opnum, path) -> bool + | apply(self, opnum, path, top_tif, spath) -> bool | | ---------------------------------------------------------------------- | Data descriptors defined here: @@ -46114,6 +46114,24 @@ detach_action_from_toolbar(*args) @param name: the action name (C++: const char *) @return: success +Help on class disabled_script_timeout_t in module ida_kernwin: + +class disabled_script_timeout_t(__builtin__.object) + | Methods defined here: + | + | __enter__(self) + | + | __exit__(self, type, value, tb) + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables (if defined) + | + | __weakref__ + | list of weak references to the object (if defined) + Help on class disasm_line_t in module ida_kernwin: class disasm_line_t(__builtin__.object) @@ -73313,15 +73331,6 @@ LoadFile(filepath, pos, ea, size) Help on function MakeVar in module idc: MakeVar(ea) - Mark the location as "variable" - - @param ea: address to mark - - @return: None - - @note: All that IDA does is to mark the location as "variable". - Nothing else, no additional analysis is performed. - This function may disappear in the future. Help on function SaveFile in module idc: diff --git a/release_pydoc_injections3.txt b/release_pydoc_injections3.txt index adf4b2a..e282405 100644 --- a/release_pydoc_injections3.txt +++ b/release_pydoc_injections3.txt @@ -31074,7 +31074,7 @@ class ui_stroff_applicator_t(builtins.object) | delete_ui_stroff_applicator_t(self) | | apply(self, *args) -> 'bool' - | apply(self, opnum, path) -> bool + | apply(self, opnum, path, top_tif, spath) -> bool | | ---------------------------------------------------------------------- | Data descriptors defined here: @@ -46249,6 +46249,24 @@ detach_action_from_toolbar(*args) -> 'bool' @param name: the action name (C++: const char *) @return: success +Help on class disabled_script_timeout_t in module ida_kernwin: + +class disabled_script_timeout_t(builtins.object) + | Methods defined here: + | + | __enter__(self) + | + | __exit__(self, type, value, tb) + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables (if defined) + | + | __weakref__ + | list of weak references to the object (if defined) + Help on class disasm_line_t in module ida_kernwin: class disasm_line_t(builtins.object) @@ -73351,15 +73369,6 @@ LoadFile(filepath, pos, ea, size) Help on function MakeVar in module idc: MakeVar(ea) - Mark the location as "variable" - - @param ea: address to mark - - @return: None - - @note: All that IDA does is to mark the location as "variable". - Nothing else, no additional analysis is performed. - This function may disappear in the future. Help on function SaveFile in module idc: diff --git a/swig/kernwin.i b/swig/kernwin.i index 36ccd6a..af6efcd 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -42,12 +42,12 @@ extern plugin_t PLUGIN; %ignore ask_text; %ignore vwarning; // Note: don't do that for ask_form(), since that calls back into Python. -%thread ask_addr; -%thread ask_seg; -%thread ask_long; -%thread ask_yn; -%thread ask_buttons; -%thread ask_file; +%modal_dialog_triggering_function(ask_addr); +%modal_dialog_triggering_function(ask_seg); +%modal_dialog_triggering_function(ask_long); +%modal_dialog_triggering_function(ask_yn); +%modal_dialog_triggering_function(ask_buttons); +%modal_dialog_triggering_function(ask_file); %ignore simpleline_t::simpleline_t(const qstring &); diff --git a/tools/deploy/header.i.in b/tools/deploy/header.i.in index 5af0dc3..6047753 100644 --- a/tools/deploy/header.i.in +++ b/tools/deploy/header.i.in @@ -1681,6 +1681,21 @@ SWIGINTERN bool __chkreqidb() %} +%define %modal_dialog_triggering_function(NAME) +%thread NAME; +%pythonprepend NAME +%{ + import ida_kernwin + # kludge: we can't use %feature("shadow") for top-level + # functions (see https://github.com/swig/swig/issues/980) + # Thus we'll %pythonprepend some code, and return from it, + # making the original code unreachable. Not pretty, but I + # don't have anything better at the moment. + with ida_kernwin.disabled_script_timeout_t(): + return _ida_kernwin.NAME(*args) +%} +%enddef + %include // If the module is 'pro', don't import pro.h, or the %include