#ifndef __PYWRAPS_HPP__ #define __PYWRAPS_HPP__ #include //------------------------------------------------------------------------ // The following are to be used whenever Py_BuildValue-style // format specifiers are needed. #define PY_BV_EA "K" // Convert a C unsigned long long to a Python long integer object. #define PY_BV_SZ "n" // Convert a C Py_ssize_t to a Python integer or long integer. #define PY_BV_UVAL PY_BV_EA #define PY_BV_ASIZE PY_BV_EA #define PY_BV_SEL PY_BV_EA #define PY_BV_SVAL "L" // Convert a C long long to a Python long integer object #define PY_BV_TYPE "y" #define PY_BV_FIELDCMTS "y" #define PY_BV_FIELDS "y" #define PY_BV_BYTES "y" typedef unsigned PY_LONG_LONG bvea_t; typedef Py_ssize_t bvsz_t; typedef bvea_t bvuval_t; typedef bvea_t bvasize_t; typedef bvea_t bvsel_t; typedef PY_LONG_LONG bvsval_t; //------------------------------------------------------------------------- // a few forward decls class insn_t; class op_t; struct switch_info_t; struct jobj_t; struct jarr_t; struct jvalue_t; // "A pointer can be explicitly converted to any integral type large // enough to hold it. The mapping function is implementation-defined." // - C++03 // => G++ (and probably MSVC) will typically first sign-extend the pointer. // // int bar(void *p) { return foo(uint64(p)); } // // translates to: // // mov 0x8(%ebp),%eax // mov %eax,%edx // sar $0x1f,%edx ; <---- boom! // mov %eax,(%esp) // mov %edx,0x4(%esp) // call 2d <_Z3barPv+0x16> #define PTR2U64(Binding) (uint64(Binding)) //------------------------------------------------------------------------ #define S_IDA_IDAAPI_MODNAME "ida_idaapi" #define S_IDA_NALT_MODNAME "ida_nalt" #define S_IDA_UA_MODNAME "ida_ua" #define S_IDA_KERNWIN_MODNAME "ida_kernwin" #define S_IDA_MOVES_MODNAME "ida_moves" #define S_IDC_MODNAME "idc" #define S_IDAAPI_EXECSCRIPT "IDAPython_ExecScript" #define S_IDAAPI_FINDCOMPLETIONS "IDAPython_Completion" #define S_IDAAPI_FORMATEXC "IDAPython_FormatExc" #define S_IDAAPI_LOADPROCMOD "IDAPython_LoadProcMod" #define S_IDAAPI_UNLOADPROCMOD "IDAPython_UnLoadProcMod" //------------------------------------------------------------------------ // String constants used static const char S_PYINVOKE0[] = "_py_invoke0"; static const char S_PY_SWIEX_CLSNAME[] = "switch_info_t"; static const char S_PY_OP_T_CLSNAME[] = "op_t"; static const char S_PROPS[] = "props"; static const char S_NAME[] = "name"; static const char S_TITLE[] = "title"; static const char S_COLS[] = "cols"; static const char S_ASM_KEYWORD[] = "asm_keyword"; static const char S_MENU_NAME[] = "menu_name"; static const char S_HOTKEY[] = "hotkey"; static const char S_EMBEDDED[] = "embedded"; static const char S_POPUP_NAMES[] = "popup_names"; static const char S_FLAGS[] = "flags"; static const char S_FLAGS2[] = "flags2"; static const char S_VALUE_SIZE[] = "value_size"; static const char S_MAY_CREATE_AT[] = "may_create_at"; static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; static const char S_ID[] = "id"; static const char S_PRINTF[] = "printf"; static const char S_TEXT_WIDTH[] = "text_width"; static const char S_SCAN[] = "scan"; static const char S_ANALYZE[] = "analyze"; static const char S_CBSIZE[] = "cbsize"; static const char S_ON_CLICK[] = "OnClick"; static const char S_ON_CLOSE[] = "OnClose"; static const char S_ON_DBL_CLICK[] = "OnDblClick"; static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; static const char S_ON_KEYDOWN[] = "OnKeydown"; static const char S_ON_FIND_COMPLETIONS[] = "OnFindCompletions"; static const char S_ON_CREATE[] = "OnCreate"; static const char S_ON_POPUP[] = "OnPopup"; static const char S_ON_HINT[] = "OnHint"; static const char S_ON_EDGE_HINT[] = "OnEdgeHint"; static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; static const char S_ON_EDIT_LINE[] = "OnEditLine"; static const char S_ON_INSERT_LINE[] = "OnInsertLine"; static const char S_ON_GET_LINE[] = "OnGetLine"; static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; static const char S_ON_REFRESH[] = "OnRefresh"; static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; static const char S_ON_SELECT_LINE[] = "OnSelectLine"; static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; static const char S_ON_GET_ICON[] = "OnGetIcon"; static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; static const char S_ON_GET_SIZE[] = "OnGetSize"; static const char S_ON_GETTEXT[] = "OnGetText"; static const char S_ON_GET_EA[] = "OnGetEA"; static const char S_ON_GET_DIRTREE[] = "OnGetDirTree"; static const char S_ON_INDEX_TO_INODE[] = "OnIndexToInode"; static const char S_ON_INDEX_TO_DIFFPOS[] = "OnIndexToDiffpos"; static const char S_ON_LAZY_LOAD_DIR[] = "OnLazyLoadDir"; static const char S_ON_ACTIVATE[] = "OnActivate"; static const char S_ON_DEACTIVATE[] = "OnDeactivate"; static const char S_ON_SELECT[] = "OnSelect"; static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; static const char S_ON_INIT[] = "OnInit"; static const char S_M_EDGES[] = "_edges"; static const char S_M_NODES[] = "_nodes"; static const char S_M_THIS[] = "_this"; static const char S_M_TITLE[] = "_title"; static const char S_CLINK_NAME[] = "__clink__"; static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; static const char S_MAIN[] = "__main__"; static const char S_FILE[] = "__file__"; #define VALID_CAPSULE_NAME "$valid$" #define INVALID_CAPSULE_NAME "$INvalid$" #ifdef __PYWRAPS__ static const char S_PY_IDA_IDAAPI_MODNAME[] = "__main__"; #else static const char S_PY_IDA_IDAAPI_MODNAME[] = S_IDA_IDAAPI_MODNAME; #endif //------------------------------------------------------------------------ // PyIdc conversion object IDs #define PY_ICID_INT64 0 #define PY_ICID_BYREF 1 #define PY_ICID_OPAQUE 2 //------------------------------------------------------------------------ // Constants used by the pyvar_to_idcvar and idcvar_to_pyvar functions #define CIP_FAILED -1 // Conversion error #define CIP_IMMUTABLE 0 // Immutable object passed. Will not update the object but no error occurred #define CIP_OK 1 // Success #define CIP_OK_OPAQUE 2 // Success, but the data pointed to by the PyObject* is an opaque object. //------------------------------------------------------------------------- inline bool PyUnicode_as_qstring(qstring *out, PyObject *obj) { PyObject *utf8 = PyUnicode_AsUTF8String(obj); bool ok = utf8 != nullptr; if ( ok ) { char *buffer = nullptr; Py_ssize_t length = 0; ok = PyBytes_AsStringAndSize(utf8, &buffer, &length) >= 0; if ( ok ) { out->qclear(); out->append(buffer, length); } } Py_XDECREF(utf8); return ok; } //------------------------------------------------------------------------- inline PyObject *PyUnicode_from_qstring(const qstring &s) { return PyUnicode_FromStringAndSize(s.c_str(), s.length()); } //------------------------------------------------------------------------- inline bool PyBytes_as_bytevec_t(bytevec_t *out, PyObject *obj) { char *buffer = nullptr; Py_ssize_t length = 0; bool ok = PyBytes_AsStringAndSize(obj, &buffer, &length) >= 0; if ( ok ) { out->qclear(); out->append((const uchar *) buffer, length); } return ok; } //------------------------------------------------------------------------- inline bool PyBytes_as_qtype(qtype *out, PyObject *obj) { bytevec_t bytes; bool ok = PyBytes_as_bytevec_t(&bytes, obj); if ( ok ) { out->qclear(); out->append(bytes.begin(), bytes.size()); } return ok; } //--------------------------------------------------------------------------- class gil_lock_t { private: PyGILState_STATE state; public: gil_lock_t() { state = PyGILState_Ensure(); } ~gil_lock_t() { PyGILState_Release(state); } }; // Declare a variable to acquire/release the GIL #define PYW_GIL_GET gil_lock_t lock; // Let's declare just the relevant bits, in order to not have to force // including 'kernwin.hpp' in all files #ifndef __KERNWIN_HPP idaman uint32 ida_export_data debug; #define IDA_DEBUG_PLUGIN 0x00000020 THREAD_SAFE AS_PRINTF(1, 2) inline int msg(const char *format, ...); #endif // __KERNWIN_HPP #ifdef Py_LIMITED_API # define GIL_CHKCONDFAIL (false) #else # define GIL_CHKCONDFAIL (((debug & IDA_DEBUG_PLUGIN) != 0) && !PyGILState_Check()) #endif #define PYW_GIL_CHECK_LOCKED_SCOPE() \ do \ { \ if ( GIL_CHKCONDFAIL ) \ { \ msg("*** WARNING: Code at %s:%d should have the GIL, but apparently doesn't ***\n", \ __FILE__, __LINE__); \ if ( under_debugger ) \ BPT; \ } \ } while ( false ) //------------------------------------------------------------------------- struct exc_report_t { ~exc_report_t() { if ( PyErr_Occurred() ) PyErr_Print(); } }; #define PYW_GIL_GET_AND_REPORT_ERROR PYW_GIL_GET; exc_report_t exc; // Returns the linked object (void *) from a PyObject idaman void * ida_export pyobj_get_clink(PyObject *pyobj); //------------------------------------------------------------------------- // The base for a reference. Will automatically increase the reference // counter for the object when it is assigned from another ref_t, // and decrease the reference counter when destroyed. // This is meant to be used whenever possible, in order to prevent // situations where, e.g., a given code path is taken and we return from // a function without first decreasing the reference counter. // // Note: You should never, ever have to Py_[INCREF|DECREF] the 'o' object yourself. // Note: These simple ref_t cannot be created with a PyObject* directly // (that would be the role of 'newref_t'/'borref_t' below.) // In other words: simple 'ref_t' instances are never created from the // result of calling the CPython API. They are only used when in // idapython land. // In yet other words: the CPython API only deals in terms of // 'New references' and 'Borrowed references'. Those are implemented, // respectively, by the 'newref_t' and 'borref_t' classes below. // This 'ref_t' is only used for internal handling. struct ref_t { PyObject *o; ref_t() : o(nullptr) {} ref_t(const ref_t &other) : o(other.o) { incref(); } ~ref_t() { decref(); } ref_t &operator=(const ref_t &other) { // We *must* first (possibly) set & incref the other object, // because decref() might call the Python's deallocator, which // might have side-effects, that might affect this ref_t // instance. // If that's too many 'might' to your taste, let me illustrate. // // py_plgform.hpp's 'plgform_t' holds a 'ref_t' instance, named 'py_obj'. // If the actual, Qt widget wrapped by that plgform_t gets destroyed, // plgform_t::unhook() will be called, which will assign an // empty ref_t instance to its 'py_obj'. // That will decrement the refcount, and might call the deallocator: // the plgform_t::destroy static function. // That function will 'delete' the plgform_t object. // But, in the ~plgform_t() destructor, the 'py_obj' object will be // destroyed too: decreasing once again the refcnt (which now falls to -1). // At this point, all hell breaks loose (or is allowed to). PyObject *was = o; o = other.o; incref(); if ( was != nullptr ) Py_DECREF(was); return *this; } void incref() const { if ( o != nullptr ) Py_INCREF(o); } void decref() const { if ( o != nullptr ) { QASSERT(30469, o->ob_refcnt > 0); Py_DECREF(o); } } bool operator==(PyObject *other) const { return o == other; } bool operator!=(PyObject *other) const { return !((*this) == other); } bool operator==(const ref_t &other) const { return o == other.o; } bool operator!=(const ref_t &other) const { return !((*this) == other); } operator bool() const { return o != nullptr; } }; struct borref_t; //------------------------------------------------------------------------- // A 'new' reference. Typically used when the CPython implementation returns // a PyObject* whose refcnt was already increased, and that the caller is // responsible for releasing. // // This implements the 'New reference' idea at http://docs.python.org/2/c-api/intro.html: // --- // "When a function passes ownership of a reference on to its caller, // the caller is said to receive a new reference" // --- // E.g., from "PyObject_GetAttrString"'s doc: // --- // "Return value: New reference. // Retrieve an attribute named attr_name from object o[...]" // --- struct newref_t : public ref_t { newref_t() = delete; newref_t(const newref_t &other) = delete; newref_t &operator=(const newref_t &other) = delete; newref_t(const borref_t &other) = delete; newref_t &operator=(const borref_t &other) = delete; newref_t(PyObject *_o) { #ifdef _DEBUG QASSERT(30409, _o == nullptr || _o->ob_refcnt >= 1); #endif o = _o; } }; //------------------------------------------------------------------------- // A 'borrowed' reference. Typically used when the CPython implementation returns // a PyObject* whose ownership is _not_ transferred to the caller. // Therefore, and since the caller wants to make sure the object is not // released while it is using it, it must first increase the reference count, // and then decrease it. // // This is similar to the simpler 'ref_t' in that it first increases, and then // decreases the reference count. The difference is that 'borref_t' instances // can be created with a PyObject*, while 'ref_t' instances cannot (by design). // // This implements the 'Borrowed reference' idea at http://docs.python.org/2/c-api/intro.html: // --- // "When no ownership is transferred, the caller is said to borrow the reference. // Nothing needs to be done for a borrowed reference." // --- struct borref_t : public ref_t { borref_t() = delete; borref_t(const newref_t &other) = delete; borref_t &operator=(const newref_t &other) = delete; borref_t(const borref_t &other) = delete; borref_t &operator=(const borref_t &other) = delete; borref_t(PyObject *_o) { o = _o; incref(); // ~ref_t() will decref(), so we need to incref. } }; //------------------------------------------------------------------------ // Vector of ref_t struct ref_vec_t : public qvector { void to_pyobject_pointers(qvector *out) { size_t _n = size(); out->resize(_n); for ( size_t i = 0; i < _n; ++i ) out->at(i) = at(i).o; } }; #ifdef _MSC_VER // warning C4190: 'PyW_TryImportModule' has C-linkage specified, but returns UDT 'ref_t' which is incompatible with C #pragma warning(disable : 4190) #elif defined(__MAC__) GCC_DIAG_OFF(return-type-c-linkage); #endif // Tries to import a module and swallows the exception if it fails and returns nullptr // Return value: New reference. idaman ref_t ida_export PyW_TryImportModule(const char *name); // Tries to get an attribute and swallows the exception if it fails and returns nullptr idaman ref_t ida_export PyW_TryGetAttrString(PyObject *py_var, const char *attr); // Converts a Python number (LONGLONG or normal integer) to an IDC variable (VT_LONG or VT_INT64) idaman bool ida_export PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); // Returns a qstring from a Python attribute string idaman bool ida_export PyW_GetStringAttr( PyObject *py_obj, const char *attr_name, qstring *str); // Deprecated. Please use specific functions instead. idaman bool ida_export PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64 = nullptr); // Checks if an Python object can be treated like a sequence idaman bool ida_export PyW_IsSequenceType(PyObject *obj); // Returns an error string from the last exception (and clears it) idaman bool ida_export PyW_GetError(qstring *out = nullptr, bool clear_err = true); // If an error occurred (it calls PyGetError) it displays it and return TRUE // This function is used when calling callbacks idaman bool ida_export PyW_ShowCbErr(const char *cb_name); // Utility function to create linked class instances idaman ref_t ida_export create_linked_class_instance(const char *modname, const char *clsname, void *lnk); // Returns the string representation of a PyObject idaman bool ida_export PyW_ObjectToString(PyObject *obj, qstring *out); // Utility function to convert a python object to an IDC object // and sets a python exception on failure. idaman bool ida_export pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj); // Creates and initializes an IDC exception idaman error_t ida_export PyW_CreateIdcException(idc_value_t *res, const char *msg); // // Conversion functions // #define PYWCVTF_AS_TUPLE 0x1 #define PYWCVTF_INT64_AS_UNSIGNED_PYLONG 0x2 // don't wrap int64 into 'PyIdc_cvt_int64__' objects, but make them 'long' instead #define PYWCVTF_STR_AS_BYTES 0x4 // VT_STR objects will be converted into 'bytes', not strings coming from UTF-8 data // Converts from IDC to Python idaman bool ida_export pyw_convert_idc_args( const idc_value_t args[], int nargs, ref_vec_t &pargs, uint32 flags, qstring *errbuf = nullptr); // Converts from IDC to Python // We support converting VT_REF IDC variable types idaman int ida_export idcvar_to_pyvar( const idc_value_t &idc_var, ref_t *py_var, uint32 flags=0); //------------------------------------------------------------------------- // Converts Python variable to IDC variable // gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF idaman int ida_export pyvar_to_idcvar( const ref_t &py_var, idc_value_t *idc_var, int *gvar_sn=nullptr); //------------------------------------------------------------------------- // Walks a Python sequence and calls the callback idaman Py_ssize_t ida_export pyvar_walk_seq( PyObject *py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud)=nullptr, void *ud = nullptr, size_t maxsize=size_t(-1)); // Converts a vector to a Python list object idaman ref_t ida_export PyW_SizeVecToPyList(const sizevec_t &vec); idaman ref_t ida_export PyW_UvalVecToPyList(const uvalvec_t &vec); idaman ref_t ida_export PyW_StrVecToPyList(const qstrvec_t &vec); idaman ref_t ida_export PyW_TidVecToPyList(const qvector &vec); // Converts a Python list, to a vector of the given type. // An exception will be raised in case: // - py_list is not a sequence // - a member of py_list cannot be converted to the numeric target type idaman Py_ssize_t ida_export PyW_PySeqToSizeVec(sizevec_t *out, PyObject *py_list, size_t maxsize=size_t(-1)); idaman Py_ssize_t ida_export PyW_PySeqToEaVec(eavec_t *out, PyObject *py_list, size_t maxsize=size_t(-1)); idaman Py_ssize_t ida_export PyW_PySeqToStrVec(qstrvec_t *out, PyObject *py_list, size_t maxsize=size_t(-1)); idaman Py_ssize_t ida_export PyW_PySeqToTidVec(qvector *out, PyObject *py_list, size_t maxsize=size_t(-1)); idaman PyObject *ida_export PyW_from_jvalue_t(const jvalue_t &v); idaman bool ida_export PyW_to_jvalue_t(jvalue_t *out, PyObject *py); idaman PyObject *ida_export PyW_from_jobj_t(const jobj_t &o); idaman bool ida_export PyW_to_jobj_t(jobj_t *out, PyObject *py); #ifndef LUMINA_HPP // I'd rather put the def of ea64_t and ea64vec_t into pro.h... #ifdef __EA64__ typedef ea_t ea64_t; #else typedef uint64 ea64_t; #endif typedef qvector ea64vec_t; #endif // LUMINA_HPP idaman Py_ssize_t ida_export PyW_PySeqToEa64Vec(ea64vec_t *out, PyObject *py_list, size_t maxsize=size_t(-1)); //------------------------------------------------------------------------- #include idaman PyObject *ida_export meminfo_vec_t_to_py(meminfo_vec_t &ranges); //------------------------------------------------------------------------- idaman void ida_export PyW_register_compiled_form(PyObject *py_form); //------------------------------------------------------------------------- idaman void ida_export PyW_unregister_compiled_form(PyObject *py_form); // #define PYGDBG_ENABLED #ifdef PYGDBG_ENABLED #define PYGLOG(...) msg(__VA_ARGS__) #else #define PYGLOG(...) #endif //------------------------------------------------------------------------- struct pycall_res_t { pycall_res_t(PyObject *pyo) : result(pyo) { PYGLOG("return code: %p\n", result.o); } ~pycall_res_t() { if ( PyErr_Occurred() ) PyErr_Print(); } inline bool success() const { return result.o != nullptr; } newref_t result; private: pycall_res_t() = delete; }; #include //------------------------------------------------------------------------- // CustomIDAMemo wrappers //-------------------------------------------------------------------------i class lookup_info_t; class py_customidamemo_t; struct lookup_entry_t { lookup_entry_t() : view(nullptr), py_view(nullptr) {} TWidget *view; py_customidamemo_t *py_view; }; DECLARE_TYPE_AS_MOVABLE(lookup_entry_t); typedef qvector lookup_entries_t; #include #define PY_LINFO_HLPPRM_new_entry (lookup_info_t *_this, py_customidamemo_t *py_view) #define PY_LINFO_PARAMS_new_entry (py_customidamemo_t *py_view) #define PY_LINFO_TRANSM_new_entry (this, py_view) #define PY_LINFO_HLPPRM_commit (lookup_info_t *_this, lookup_entry_t &e, TWidget *view) #define PY_LINFO_PARAMS_commit (lookup_entry_t &e, TWidget *view) #define PY_LINFO_TRANSM_commit (this, e, view) #define PY_LINFO_HLPPRM_find_by_view (const lookup_info_t *_this, py_customidamemo_t **out_py_view, const TWidget *view) #define PY_LINFO_PARAMS_find_by_view (py_customidamemo_t **out_py_view, const TWidget *view) const #define PY_LINFO_TRANSM_find_by_view (this, out_py_view, view) #define PY_LINFO_HLPPRM_find_by_py_view (const lookup_info_t *_this, TWidget **out_view, const py_customidamemo_t *py_view) #define PY_LINFO_PARAMS_find_by_py_view (TWidget **out_view, const py_customidamemo_t *py_view) const #define PY_LINFO_TRANSM_find_by_py_view (this, out_view, py_view) #define PY_LINFO_HLPPRM_del_by_py_view (lookup_info_t *_this, const py_customidamemo_t *py_view) #define PY_LINFO_PARAMS_del_by_py_view (const py_customidamemo_t *py_view) #define PY_LINFO_TRANSM_del_by_py_view (this, py_view) #define DECL_LINFO_HELPER(decl, RType, MName, MParams) \ decl RType ida_export lookup_info_t_##MName MParams #define DECL_LINFO_HELPERS(decl) \ DECL_LINFO_HELPER(decl, lookup_entry_t&, new_entry, PY_LINFO_HLPPRM_new_entry); \ DECL_LINFO_HELPER(decl, void, commit, PY_LINFO_HLPPRM_commit); \ DECL_LINFO_HELPER(decl, bool, find_by_view, PY_LINFO_HLPPRM_find_by_view);\ DECL_LINFO_HELPER(decl, bool, find_by_py_view, PY_LINFO_HLPPRM_find_by_py_view);\ DECL_LINFO_HELPER(decl, bool, del_by_py_view, PY_LINFO_HLPPRM_del_by_py_view); DECL_LINFO_HELPERS(idaman); class py_customidamemo_t; class lookup_info_t { DECL_LINFO_HELPERS(friend); public: #define PY_LINFO_TRAMPOLINE(RType, MName, MParams, PNames) \ RType MName MParams { return lookup_info_t_##MName PNames; } PY_LINFO_TRAMPOLINE(lookup_entry_t&, new_entry, PY_LINFO_PARAMS_new_entry, PY_LINFO_TRANSM_new_entry); PY_LINFO_TRAMPOLINE(void, commit, PY_LINFO_PARAMS_commit, PY_LINFO_TRANSM_commit); PY_LINFO_TRAMPOLINE(bool, find_by_view, PY_LINFO_PARAMS_find_by_view, PY_LINFO_TRANSM_find_by_view); PY_LINFO_TRAMPOLINE(bool, find_by_py_view, PY_LINFO_PARAMS_find_by_py_view, PY_LINFO_TRANSM_find_by_py_view); PY_LINFO_TRAMPOLINE(bool, del_by_py_view, PY_LINFO_PARAMS_del_by_py_view, PY_LINFO_TRANSM_del_by_py_view); #undef PY_LINFO_TRAMPOLINE private: lookup_entries_t entries; }; #ifdef __NT__ #ifdef PLUGIN_SUBMODULE #define plugin_export_data __declspec(dllimport) #else #define plugin_export_data __declspec(dllexport) #endif #else // unix #define plugin_export_data __attribute__((visibility("default"))) #endif //------------------------------------------------------------------------- struct pycim_callback_id_t { qstring name; int have; }; struct pycim_callbacks_ids_t : public qvector { void add(const char *_n, int _h) { pycim_callback_id_t &o = push_back(); o.name = _n; o.have = _h; } }; //------------------------------------------------------------------------- // //------------------------------------------------------------------------- #define PY_CIM_HLPPRM_create_groups (py_customidamemo_t *_this, PyObject *groups_infos) #define PY_CIM_PARAMS_create_groups (PyObject *groups_infos) #define PY_CIM_TRANSM_create_groups (this, groups_infos) #define PY_CIM_HLPPRM_delete_groups (py_customidamemo_t *_this, PyObject *groups, PyObject *new_current) #define PY_CIM_PARAMS_delete_groups (PyObject *groups, PyObject *new_current) #define PY_CIM_TRANSM_delete_groups (this, groups, new_current) #define PY_CIM_HLPPRM_set_groups_visibility (py_customidamemo_t *_this, PyObject *groups, PyObject *expand, PyObject *new_current) #define PY_CIM_PARAMS_set_groups_visibility (PyObject *groups, PyObject *expand, PyObject *new_current) #define PY_CIM_TRANSM_set_groups_visibility (this, groups, expand, new_current) #define PY_CIM_HLPPRM_collect_pyobject_callbacks (py_customidamemo_t *_this, PyObject *self) #define PY_CIM_PARAMS_collect_pyobject_callbacks (PyObject *in_self) #define PY_CIM_TRANSM_collect_pyobject_callbacks (this, in_self) #define PY_CIM_HLPPRM_collect_class_callbacks_ids (py_customidamemo_t *_this, pycim_callbacks_ids_t *out) #define PY_CIM_PARAMS_collect_class_callbacks_ids (pycim_callbacks_ids_t *out) #define PY_CIM_TRANSM_collect_class_callbacks_ids (this, out) #define PY_CIM_HLPPRM_bind (py_customidamemo_t *_this, PyObject *self, TWidget *view) #define PY_CIM_PARAMS_bind (PyObject *in_self, TWidget *in_view) #define PY_CIM_TRANSM_bind (this, in_self, in_view) #define PY_CIM_HLPPRM_unbind (py_customidamemo_t *_this) #define PY_CIM_PARAMS_unbind () #define PY_CIM_TRANSM_unbind (this) #define DECL_CIM_HELPER(decl, RType, MName, MParams) \ decl RType ida_export py_customidamemo_t_##MName MParams #define DECL_CIM_HELPERS(decl) \ DECL_CIM_HELPER(decl, PyObject*, create_groups, PY_CIM_HLPPRM_create_groups); \ DECL_CIM_HELPER(decl, PyObject*, delete_groups, PY_CIM_HLPPRM_delete_groups); \ DECL_CIM_HELPER(decl, PyObject*, set_groups_visibility, PY_CIM_HLPPRM_set_groups_visibility);\ \ DECL_CIM_HELPER(decl, bool, collect_pyobject_callbacks, PY_CIM_HLPPRM_collect_pyobject_callbacks); \ DECL_CIM_HELPER(decl, void, collect_class_callbacks_ids, PY_CIM_HLPPRM_collect_class_callbacks_ids); \ DECL_CIM_HELPER(decl, bool, bind, PY_CIM_HLPPRM_bind); \ DECL_CIM_HELPER(decl, void, unbind, PY_CIM_HLPPRM_unbind); \ DECL_CIM_HELPERS(idaman); //------------------------------------------------------------------------- #define PY_CIM_TRAMPOLINE(RType, MName, MParams, PNames) \ RType MName MParams { return py_customidamemo_t_##MName PNames; } class py_customidamemo_t { // can use up to 16 bits; not more! (GRCODE_HAVE_* uses the rest) enum { GRBASE_HAVE_VIEW_MOUSE_MOVED = 0x0001, }; int cb_flags; // View events void on_view_mouse_moved(const view_mouse_event_t *event); // View events that are bound with 'set_custom_viewer_handler()'. static void idaapi s_on_view_mouse_moved( TWidget *cv, int shift, view_mouse_event_t *e, void *ud); DECL_CIM_HELPERS(friend); protected: ref_t self; TWidget *view; pycim_callbacks_ids_t cbids; PY_CIM_TRAMPOLINE(bool, collect_pyobject_callbacks, PY_CIM_PARAMS_collect_pyobject_callbacks, PY_CIM_TRANSM_collect_pyobject_callbacks); PY_CIM_TRAMPOLINE(virtual void, collect_class_callbacks_ids, PY_CIM_PARAMS_collect_class_callbacks_ids, PY_CIM_TRANSM_collect_class_callbacks_ids); PY_CIM_TRAMPOLINE(bool, bind, PY_CIM_PARAMS_bind, PY_CIM_TRANSM_bind); PY_CIM_TRAMPOLINE(void, unbind, PY_CIM_PARAMS_unbind, PY_CIM_TRANSM_unbind); friend TWidget *pycim_get_widget(PyObject *self); public: py_customidamemo_t(); virtual ~py_customidamemo_t(); virtual void refresh() { refresh_viewer(view); } inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } PY_CIM_TRAMPOLINE(PyObject*, create_groups, PY_CIM_PARAMS_create_groups, PY_CIM_TRANSM_create_groups); PY_CIM_TRAMPOLINE(PyObject*, delete_groups, PY_CIM_PARAMS_delete_groups, PY_CIM_TRANSM_delete_groups); PY_CIM_TRAMPOLINE(PyObject*, set_groups_visibility, PY_CIM_PARAMS_set_groups_visibility, PY_CIM_TRANSM_set_groups_visibility); }; #undef PY_CIM_TRAMPOLINE #undef DECL_CIM_HELPERS #undef DECL_CIM_HELPER //------------------------------------------------------------------------- inline void *view_extract_this(PyObject *self) { PYW_GIL_CHECK_LOCKED_SCOPE(); ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS)); if ( py_this == nullptr || !PyCapsule_IsValid(py_this.o, VALID_CAPSULE_NAME) ) return nullptr; return PyCapsule_GetPointer(py_this.o, VALID_CAPSULE_NAME); } //------------------------------------------------------------------------- // //------------------------------------------------------------------------- #include #define DECL_REG_UNREG_REFCOUNTED(Type) \ idaman void ida_export til_register_python_##Type##_instance(Type *inst); \ idaman void ida_export til_deregister_python_##Type##_instance(Type *inst); DECL_REG_UNREG_REFCOUNTED(tinfo_t); DECL_REG_UNREG_REFCOUNTED(ptr_type_data_t); DECL_REG_UNREG_REFCOUNTED(array_type_data_t); DECL_REG_UNREG_REFCOUNTED(func_type_data_t); DECL_REG_UNREG_REFCOUNTED(udt_type_data_t); #undef DECL_REG_UNREG_REFCOUNTED //------------------------------------------------------------------------- // Context structure used by register/unregister timer struct py_timer_ctx_t { py_timer_ctx_t() : timer_id(nullptr) {} qtimer_t timer_id; ref_t pyfunc; }; //------------------------------------------------------------------------- idaman py_timer_ctx_t *ida_export python_timer_new(PyObject *py_callback); idaman void ida_export python_timer_del(py_timer_ctx_t *t); //------------------------------------------------------------------------- idaman ref_t ida_export try_create_swig_wrapper(ref_t mod, const char *clsname, void *cobj); //------------------------------------------------------------------------- idaman ssize_t ida_export get_callable_arg_count(ref_t callable); //------------------------------------------------------------------------- // Useful for small operations that must not be interrupted: e.g., when // wrapping an insn_t into a SWiG proxy object, or when destroying // such an instance from the kernel. Use 'uninterruptible_op_t' if you can. idaman void ida_export set_interruptible_state(bool interruptible); struct uninterruptible_op_t { uninterruptible_op_t() { set_interruptible_state(false); } ~uninterruptible_op_t() { set_interruptible_state(true); } }; //------------------------------------------------------------------------- struct new_execution_t; idaman void ida_export setup_new_execution(new_execution_t *instance, bool setup); struct new_execution_t { bool created; new_execution_t() { setup_new_execution(this, true); } ~new_execution_t() { setup_new_execution(this, false); } }; //------------------------------------------------------------------------- enum run_script_when_t { RSW_UNKNOWN = 0, RSW_ui_database_inited, // run script after opening database (default) RSW_ui_ready_to_run, // run script when UI is ready RSW_on_init, // run script immediately on plugin load (shortly after IDA starts) }; //------------------------------------------------------------------------- struct idapython_plugin_config_t { struct run_script_t { qstring path; run_script_when_t when; run_script_t() : when(RSW_UNKNOWN) {} }; run_script_t run_script; uint32 execution_timeout = 0; bool alert_auto_scripts = true; bool remove_cwd_sys_path = false; bool autoimport_compat_idaapi = true; bool namespace_aware = true; bool repl_use_sys_displayhook = true; bool idausr_syspath = true; }; //------------------------------------------------------------------------- struct idapython_plugin_t : public plugmod_t, public event_listener_t { idapython_plugin_config_t config; lookup_info_t pycim_lookup_info; qstring idapython_dir; qstring requested_plugin_path; #ifdef __MAC__ qvector pyhomepath; #endif bool initialized; bool owning_interpreter; bool ui_ready; #ifdef TESTABLE_BUILD int user_code_lenient; #endif idapython_plugin_t(); ~idapython_plugin_t(); bool init(); void parse_plugin_options(); ref_t get_sys_displayhook(); #ifdef TESTABLE_BUILD bool is_user_code_lenient(); #endif virtual bool idaapi run(size_t arg) override; virtual ssize_t idaapi on_event(ssize_t code, va_list va) override; static ssize_t idaapi on_idb_notification(void *, int code, va_list va); static idapython_plugin_t *get_instance() { return instance; } static bool idaapi extlang_compile_file( const char *path, const char *requested_namespace, qstring *errbuf) { return get_instance()->_extlang_compile_file(path, requested_namespace, errbuf); } static bool idaapi extlang_compile_expr( const char *name, ea_t current_ea, const char *expr, qstring *errbuf) { return get_instance()->_extlang_compile_expr(name, current_ea, expr, errbuf); } static bool idaapi extlang_eval_expr( idc_value_t *rv, ea_t current_ea, const char *expr, qstring *errbuf) { return get_instance()->_extlang_eval_expr(rv, current_ea, expr, errbuf); } static bool idaapi extlang_load_procmod( idc_value_t *procobj, const char *path, qstring *errbuf) { return get_instance()->_extlang_load_procmod(procobj, path, errbuf); } static bool idaapi extlang_unload_procmod( const char *path, qstring *errbuf) { return get_instance()->_extlang_unload_procmod(path, errbuf); } static bool idaapi extlang_create_object( idc_value_t *result, const char *name, const idc_value_t args[], size_t nargs, qstring *errbuf) { return get_instance()->_extlang_create_object(result, name, args, nargs, errbuf); } static bool idaapi extlang_eval_snippet( const char *str, qstring *errbuf) { return get_instance()->_extlang_eval_snippet(str, errbuf); } static bool idaapi extlang_call_func( idc_value_t *result, const char *name, const idc_value_t args[], size_t nargs, qstring *errbuf) { return get_instance()->_extlang_call_func(result, name, args, nargs, errbuf); } static bool idaapi extlang_call_method( idc_value_t *result, const idc_value_t *idc_obj, const char *method_name, const idc_value_t args[], size_t nargs, qstring *errbuf) { return get_instance()->_extlang_call_method(result, idc_obj, method_name, args, nargs, errbuf); } static bool idaapi extlang_get_attr( idc_value_t *result, const idc_value_t *obj, const char *attr) { return get_instance()->_extlang_get_attr(result, obj, attr); } static bool idaapi extlang_set_attr( idc_value_t *obj, const char *attr, const idc_value_t &value) { return get_instance()->_extlang_set_attr(obj, attr, value); } static bool idaapi cli_execute_line( const char *line) { return get_instance()->_cli_execute_line(line); } static bool idaapi cli_find_completions( qstrvec_t *out_completions, qstrvec_t *out_hints, qstrvec_t *out_docs, int *out_match_start, int *out_match_end, const char *line, int x) { return get_instance()->_cli_find_completions(out_completions, out_hints, out_docs, out_match_start, out_match_end, line, x); } static bool idaapi exec_script( const char *path, PyObject *globals, qstring *errbuf) { return get_instance()->_exec_script(path, globals, errbuf); } private: bool _extlang_compile_file( const char *path, const char *requested_namespace, qstring *errbuf); bool _extlang_compile_expr( const char *name, ea_t current_ea, const char *expr, qstring *errbuf); bool _extlang_eval_expr( idc_value_t *rv, ea_t current_ea, const char *expr, qstring *errbuf); bool _extlang_load_procmod( idc_value_t *procobj, const char *path, qstring *errbuf); bool _extlang_unload_procmod( const char *path, qstring *errbuf); bool _extlang_create_object( idc_value_t *result, const char *name, const idc_value_t args[], size_t nargs, qstring *errbuf); bool _extlang_eval_snippet( const char *str, qstring *errbuf); bool _extlang_call_func( idc_value_t *result, const char *name, const idc_value_t args[], size_t nargs, qstring *errbuf); bool _extlang_call_method( idc_value_t *result, const idc_value_t *idc_obj, const char *method_name, const idc_value_t args[], size_t nargs, qstring *errbuf); bool _extlang_get_attr( idc_value_t *result, const idc_value_t *obj, const char *attr); bool _extlang_set_attr( idc_value_t *obj, const char *attr, const idc_value_t &value); bool _cli_execute_line( const char *line); bool _cli_find_completions( qstrvec_t *out_completions, qstrvec_t *out_hints, qstrvec_t *out_docs, int *out_match_start, int *out_match_end, const char *line, int x); bool _handle_file( const char *path, PyObject *globals, qstring *errbuf, const char *idaapi_executor_func_name = S_IDAAPI_EXECSCRIPT, idc_value_t *second_res = nullptr, bool want_tuple = false); bool _exec_script( const char *path, PyObject *globals, qstring *errbuf); bool _run_user_script(); bool _check_python_dir(); void _prepare_sys_path(); bool _run_init_py(); PyObject *_get_module_globals(const char *modname=nullptr); PyObject *_get_module_globals_from_path( const char *path, const char *requested_namespace); static idapython_plugin_t *instance; }; idaman idapython_plugin_t *ida_export get_plugin_instance(); //------------------------------------------------------------------------- py_customidamemo_t::py_customidamemo_t() : cb_flags(0), self(newref_t(nullptr)), view(nullptr) { PYGLOG("%p: py_customidamemo_t()\n", this); } //------------------------------------------------------------------------- py_customidamemo_t::~py_customidamemo_t() { PYGLOG("%p: ~py_customidamemo_t()\n", this); unbind(); get_plugin_instance()->pycim_lookup_info.del_by_py_view(this); } //------------------------------------------------------------------------- idaman void ida_export idapython_register_hook( hook_type_t hook_type, hook_cb_t *cb, void *user_data, bool is_hooks_base); idaman void ida_export idapython_unregister_hook( hook_type_t hook_type, hook_cb_t *cb, void *user_data); //------------------------------------------------------------------------- idaman bool ida_export idapython_hook_to_notification_point( hook_type_t hook_type, hook_cb_t *cb, void *user_data, bool is_hooks_base); idaman bool ida_export idapython_unhook_from_notification_point( hook_type_t hook_type, hook_cb_t *cb, void *user_data); #define hook_to_notification_point USE_IDAPYTHON_HOOK_TO_NOTIFICATION_POINT #define unhook_from_notification_point USE_IDAPYTHON_UNHOOK_FROM_NOTIFICATION_POINT //------------------------------------------------------------------------- #define HBF_CALL_WITH_NEW_EXEC 0x00000001 #define HBF_VOLATILE_METHOD_SET 0x00000002 struct hooks_base_t { struct idapython_listener_t : public event_listener_t { hook_cb_t *cb; void *ud; idapython_listener_t(hook_cb_t *_cb, void *_ud) : cb(_cb), ud(_ud) {} virtual ssize_t idaapi on_event(ssize_t code, va_list va) override { return cb(ud, code, va); } }; const char *class_name; qstring identifier; idapython_listener_t listener; hook_type_t type; uint32 flags; uint32 hkcb_flags; typedef std::map has_nondef_map_t; has_nondef_map_t has_nondef; bool hook_added = false; bool hook() { if ( !hook_added && listener.cb != nullptr && hook_event_listener(type, &listener, nullptr /*owner*/, hkcb_flags) ) { idapython_register_hook(type, listener.cb, this, true); hook_added = true; } return hook_added; } bool unhook() { if ( hook_added && listener.cb != nullptr ) { // there is no need to check the return result of unhook_event_listener, // if HKCB_GLOBAL flag is not used // the LISTENER will be freed earlier // than this method will be called. unhook_event_listener(type, &listener); idapython_unregister_hook(type, listener.cb, this); hook_added = false; } return !hook_added; } bool call_requires_new_execution() const { return (flags & HBF_CALL_WITH_NEW_EXEC) != 0; } bool has_fixed_method_set() const { return (flags & HBF_VOLATILE_METHOD_SET) == 0; } hooks_base_t( const char *_class_name, hook_cb_t *_cb, hook_type_t _type, uint32 _flags, uint32 _hkcb_flags) : class_name(_class_name), listener(_cb, this), type(_type), flags(_flags), hkcb_flags(_hkcb_flags) {} virtual ~hooks_base_t() { unhook(); } struct ida_local event_code_to_method_name_t { int code; const char *method_name; }; protected: void init_director_hooks( PyObject *self, const event_code_to_method_name_t *mappings, size_t count) { // identifier { ref_t py_id; if ( PyObject_HasAttrString(self, "id") ) py_id = newref_t(PyObject_GetAttrString(self, "id")); if ( !py_id || !PyUnicode_Check(py_id.o) ) py_id = newref_t(PyObject_Repr(self)); if ( py_id && PyUnicode_Check(py_id.o) ) PyUnicode_as_qstring(&identifier, py_id.o); } // method set QASSERT(30588, has_fixed_method_set()); qstring buf(class_name); QASSERT(30589, !buf.empty()); char *p = qstrchr(buf.begin(), '.'); QASSERT(30590, p != nullptr); *p++ = '\0'; newref_t py_mod(PyImport_ImportModule(buf.c_str())); #ifdef TESTABLE_BUILD QASSERT(30591, py_mod); #endif if ( py_mod ) { newref_t py_def_class(PyObject_GetAttrString(py_mod.o, p)); newref_t py_this_class(PyObject_GetAttrString(self, "__class__")); #ifdef TESTABLE_BUILD QASSERT(30592, py_def_class && py_this_class); #endif if ( py_def_class && py_this_class ) { for ( size_t i = 0; i < count; ++i ) { const event_code_to_method_name_t &cur = mappings[i]; uchar _has_nondef = 0; newref_t py_def_meth(PyObject_GetAttrString(py_def_class.o, cur.method_name)); newref_t py_this_meth(PyObject_GetAttrString(py_this_class.o, cur.method_name)); #ifdef TESTABLE_BUILD QASSERT(30593, py_def_meth && py_this_meth); #endif if ( py_def_meth && py_this_meth ) { if ( PyObject_HasAttrString(py_def_meth.o, "__trampoline") > 0 ) _has_nondef = 2; else _has_nondef = PyObject_RichCompareBool(py_this_meth.o, py_def_meth.o, Py_EQ) == 0 ? 1 : 0; } has_nondef[cur.code] = _has_nondef; } } } } void ensure_no_method( PyObject *self, const char *forbidden_method_name, const char *replacement_method_name) { if ( PyObject_HasAttrString(self, "__class__") ) { newref_t py_this_class(PyObject_GetAttrString(self, "__class__")); if ( py_this_class && PyObject_HasAttrString(py_this_class.o, forbidden_method_name) ) { msg("WARNING: The method \"%s::%s\" won't be called (it has been replaced with \"%s::%s\")\n", class_name, forbidden_method_name, class_name, replacement_method_name); } } } #ifdef TESTABLE_BUILD PyObject *dump_state( const event_code_to_method_name_t *mappings, size_t mappings_size, bool assert_all_reimplemented) const { qstring buf; qstrvec_t missing_reimpls; buf.sprnt("%s(this=%p) \"%s\" {type=%d, cb=%p, flags=%x}", class_name, this, identifier.c_str(), int(type), listener.cb, flags); if ( has_fixed_method_set() ) { for ( size_t i = 0; i < mappings_size; ++i ) { const hooks_base_t::event_code_to_method_name_t &m = mappings[i]; has_nondef_map_t::const_iterator it = has_nondef.find(m.code); if ( it != has_nondef.end() && it->second > 0 ) { const char *how = ""; if ( it->second == 2 ) how = " (as compat trampoline)"; else if ( it->second == 3 ) how = " (as 6.95 bw-compat)"; buf.cat_sprnt("\n\treimplements \"%s\"%s", m.method_name, how); } else { missing_reimpls.push_back(m.method_name); } } const size_t nmr = missing_reimpls.size(); if ( assert_all_reimplemented && nmr > 0 ) { qstring ebuf; for ( size_t i = 0; i < nmr; ++i ) { if ( !ebuf.empty() ) ebuf.append(", ", 2); ebuf.append(missing_reimpls[i]); } PyErr_Format(PyExc_NotImplementedError, "%s(this=%p) \"%s\" {type=%d, cb=%p, flags=%x} is " "missing reimplementations for: \"%s\"", class_name, this, identifier.c_str(), int(type), listener.cb, flags, ebuf.c_str()); return nullptr; } } else { buf.append(" is fully dynamic, and won't use 'has_nondef' lookup"); QASSERT(30594, has_nondef.empty()); } if ( buf.last() != '\n' ) buf.append('\n'); return PyUnicode_from_qstring(buf); } #endif }; //------------------------------------------------------------------------- idaman THREAD_SAFE void ida_export idapython_show_wait_box( bool internal, const char *message); idaman void ida_export idapython_hide_wait_box(); #define show_wait_box USE_IDAPYTHON_SHOW_WAIT_BOX #define hide_wait_box USE_IDAPYTHON_HIDE_WAIT_BOX //------------------------------------------------------------------------- idaman bool ida_export idapython_convert_cli_completions( qstrvec_t *out_completions, qstrvec_t *out_hints, qstrvec_t *out_docs, int *out_match_start, int *out_match_end, ref_t py_res); //------------------------------------------------------------------------- idaman int ida_export pylong_to_byte_array( bytevec_t *out_allocated_buffer, PyObject *in, bool little_endian=true, bool is_signed=true); //------------------------------------------------------------------------- struct module_callbacks_t { module_callbacks_t() { memset(this, 0, sizeof(*this)); } void (*init) (void); void (*term) (void); void (*closebase) (void); }; DECLARE_TYPE_AS_MOVABLE(module_callbacks_t); idaman void register_module_lifecycle_callbacks( const module_callbacks_t &cbs); idaman void ida_export prepare_programmatic_plugin_load(const char *path); #endif // __PYWRAPS_HPP__