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..65a22ad 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..076c81c 100644 --- a/release_pydoc_injections2.txt +++ b/release_pydoc_injections2.txt @@ -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..596cc06 100644 --- a/release_pydoc_injections3.txt +++ b/release_pydoc_injections3.txt @@ -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