#include #include #include #include "pywraps.hpp" //lint -esym(843,pywraps_initialized) could be declared const //lint -esym(843,g_nw) could be declared const //lint -esym(844,g_nw) could be declared const //lint -esym(843,g_use_local_python) could be declared const //------------------------------------------------------------------------ // String constants used static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; // Constants used by get_idaapi_class_reference() #define PY_CLSID_CVT_INT64 0 #define PY_CLSID_APPCALL_SKEL_OBJ 1 #define PY_CLSID_CVT_BYREF 2 #define PY_CLSID_LAST 3 //--------------------------------------------------------------------------- // Use these macros to define script<->C fields #define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } #define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) #define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) //--------------------------------------------------------------------------- enum scfield_types_t { // Numeric fields FT_FIRST_NUM, FT_INT, FT_SIZET, FT_SSIZET, FT_NUM16, FT_NUM32, FT_LAST_NUM, // String field FT_STR, FT_CHAR, // Object fields FT_ARR, // Allocated array of strings FT_STRARR, // Allocated array of 16bit numbers FT_NUM16ARR, // Fixed size character array. The size must be passed in the definition FT_CHRARR_STATIC, }; //--------------------------------------------------------------------------- struct scfld_t { const char *field_name; uint32 field_type; size_t field_offs; bool is_optional; }; #define FT_VALUE_MASK 0xFFFF0000 // Possible return values of conversion functions #define FT_NOT_FOUND -1 #define FT_BAD_TYPE -2 #define FT_OK 1 static ref_t get_idaapi_attr_by_id(const int class_id); static ref_t get_idaapi_attr(const char *attr); //------------------------------------------------------------------------- static Py_ssize_t pyvar_walk_list( const ref_t &py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), void *ud) { PYW_GIL_CHECK_LOCKED_SCOPE(); Py_ssize_t size = CIP_FAILED; do { PyObject *o = py_list.o; if ( !PyList_CheckExact(o) && !PyW_IsSequenceType(o) ) break; bool is_seq = !PyList_CheckExact(o); size = is_seq ? PySequence_Size(o) : PyList_Size(o); if ( cb == NULL ) break; Py_ssize_t i; for ( i=0; i < size; i++ ) { // Get the item ref_t py_item; if ( is_seq ) py_item = newref_t(PySequence_GetItem(o, i)); else py_item = borref_t(PyList_GetItem(o, i)); if ( py_item == NULL || cb(py_item, i, ud) < CIP_OK ) break; } size = i; } while ( false ); return size; } //------------------------------------------------------------------------- Py_ssize_t ida_export pyvar_walk_list( PyObject *py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), void *ud) { borref_t r(py_list); return pyvar_walk_list(r, cb, ud); } //--------------------------------------------------------------------------- ref_t ida_export PyW_IntVecToPyList(const intvec_t &intvec) { size_t c = intvec.size(); PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t py_list(PyList_New(c)); for ( size_t i=0; i < c; i++ ) PyList_SetItem(py_list.o, i, PyInt_FromLong(intvec[i])); return ref_t(py_list); } //--------------------------------------------------------------------------- static int idaapi pylist_to_intvec_cb( const ref_t &py_item, Py_ssize_t /*index*/, void *ud) { intvec_t &intvec = *(intvec_t *) ud; uint64 num; { PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyW_GetNumber(py_item.o, &num) ) num = 0; } intvec.push_back(int(num)); return CIP_OK; } //--------------------------------------------------------------------------- bool ida_export PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec) { intvec.clear(); return pyvar_walk_list(py_list, pylist_to_intvec_cb, &intvec) != CIP_FAILED; } //------------------------------------------------------------------------- static int idaapi pylist_to_eavec_cb( const ref_t &py_item, Py_ssize_t index, void *ud) { eavec_t &eavec = *(eavec_t *) ud; ea_t ea = BADADDR; { PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyInt_Check(py_item.o) ) { ea = PyInt_AsUnsignedLongMask(py_item.o); } else { if ( PyLong_Check(py_item.o) ) { ea = ea_t(PyLong_AsUnsignedLongLong(py_item.o)); } else { qstring m; m.sprnt("Item #%d cannot be converted to an ea_t", int(index)); PyErr_SetString(PyExc_ValueError, m.c_str()); return CIP_FAILED; } } } eavec.push_back(ea); return CIP_OK; } //--------------------------------------------------------------------------- bool ida_export PyW_PyListToEaVec(PyObject *py_list, eavec_t &eavec) { eavec.clear(); return pyvar_walk_list(py_list, pylist_to_eavec_cb, &eavec) != CIP_FAILED; } //--------------------------------------------------------------------------- static int idaapi pylist_to_strvec_cb( const ref_t &py_item, Py_ssize_t /*index*/, void *ud) { PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t &strvec = *(qstrvec_t *)ud; const char *s; if ( !PyString_Check(py_item.o) ) s = ""; else s = PyString_AsString(py_item.o); strvec.push_back(s); return CIP_OK; } //--------------------------------------------------------------------------- bool ida_export PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec) { strvec.clear(); return pyvar_walk_list(py_list, pylist_to_strvec_cb, &strvec) != CIP_FAILED; } //------------------------------------------------------------------------- bool ida_export PyWStringOrNone_Check(PyObject *tp) { return tp == Py_None || PyString_Check(tp); } //------------------------------------------------------------------------- PyObject *ida_export meminfo_vec_t_to_py(meminfo_vec_t &areas) { PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_list = PyList_New(areas.size()); meminfo_vec_t::const_iterator it, it_end(areas.end()); Py_ssize_t i = 0; for ( it=areas.begin(); it != it_end; ++it, ++i ) { const memory_info_t &mi = *it; // startEA endEA name sclass sbase bitness perm PyList_SetItem(py_list, i, Py_BuildValue("(" PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", pyul_t(mi.startEA), pyul_t(mi.endEA), mi.name.c_str(), mi.sclass.c_str(), pyul_t(mi.sbase), (unsigned int)(mi.bitness), (unsigned int)mi.perm)); } return py_list; } //------------------------------------------------------------------------- static qvector py_compiled_form_vec; //------------------------------------------------------------------------- void ida_export PyW_register_compiled_form(PyObject *py_form) { ref_t ref = borref_t(py_form); if ( !py_compiled_form_vec.has(ref) ) py_compiled_form_vec.push_back(ref); } //------------------------------------------------------------------------- void ida_export PyW_unregister_compiled_form(PyObject *py_form) { ref_t ref = borref_t(py_form); if ( py_compiled_form_vec.has(ref) ) py_compiled_form_vec.del(ref); } //------------------------------------------------------------------------- static void free_compiled_form_instances(void) { while ( !py_compiled_form_vec.empty() ) { const ref_t &ref = py_compiled_form_vec[0]; qstring title; if ( !PyW_GetStringAttr(ref.o, "title", &title) ) title = ""; msg("WARNING: Form \"%s\" was not Free()d. Force-freeing.\n", title.c_str()); // Will call 'py_unregister_compiled_form()', and thus trim the vector down. newref_t unused(PyObject_CallMethod(ref.o, (char *)"Free", "()")); } } //------------------------------------------------------------------------- // Checks if the given py_var is a special PyIdc_cvt_helper object. // It does that by examining the magic attribute and returns its numeric value. // It returns -1 if the object is not a recognized helper object. // Any Python object can be treated as an cvt object if this attribute is created. static int get_pyidc_cvt_type(PyObject *py_var) { PYW_GIL_CHECK_LOCKED_SCOPE(); // Check if this our special by reference object ref_t attr(PyW_TryGetAttrString(py_var, S_PY_IDCCVT_ID_ATTR)); if ( attr == NULL || (!PyInt_Check(attr.o) && !PyLong_Check(attr.o)) ) return -1; return (int)PyInt_AsLong(attr.o); } //------------------------------------------------------------------------- // Utility function to convert a python object to an IDC object // and sets a python exception on failure. bool ida_export pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj) { PYW_GIL_CHECK_LOCKED_SCOPE(); int sn = 0; bool ok = pyvar_to_idcvar(py_obj, idc_obj, &sn) >= CIP_OK; if ( !ok ) PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); return ok; } //------------------------------------------------------------------------ static idc_class_t *get_py_idc_cvt_opaque() { return find_idc_class(S_PY_IDC_OPAQUE_T); } //------------------------------------------------------------------------- // Utility function to create opaque / convertible Python <-> IDC variables // The referred Python variable will have its reference increased static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) { PYW_GIL_CHECK_LOCKED_SCOPE(); // Create an IDC object of this special helper class if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) return false; // Store the CVT id idc_value_t idc_val; idc_val.set_long(PY_ICID_OPAQUE); VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); // Store the value as a PVOID referencing the given Python object py_var.incref(); idc_val.set_pvoid(py_var.o); VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); return true; } //------------------------------------------------------------------------ // IDC Opaque object destructor: when the IDC object dies we kill the // opaque Python object along with it static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; static error_t idaapi py_idc_opaque_dtor( idc_value_t *argv, idc_value_t * /*res*/) { // This can be called at plugin registration time, when a // 'script_plugin_t' instance is ::free()'d. It is // not guaranteed that we have the GIL at that point. PYW_GIL_GET; // Get the value from the object idc_value_t idc_val; VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); // Extract the Python object reference PyObject *py_obj = (PyObject *)idc_val.pvoid; // Decrease its reference (and eventually destroy it) Py_DECREF(py_obj); return eOk; } //------------------------------------------------------------------------- // Converts a Python variable into an IDC variable // This function returns on one CIP_XXXX int ida_export pyvar_to_idcvar( const ref_t &py_var, idc_value_t *idc_var, int *gvar_sn) { PYW_GIL_CHECK_LOCKED_SCOPE(); // None / NULL if ( py_var == NULL || py_var.o == Py_None ) { idc_var->set_long(0); } // Numbers? else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) { return CIP_OK; } // String else if ( PyString_Check(py_var.o) ) { idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); } // Boolean else if ( PyBool_Check(py_var.o) ) { idc_var->set_long(py_var.o == Py_True ? 1 : 0); } // Float else if ( PyFloat_Check(py_var.o) ) { double dresult = PyFloat_AsDouble(py_var.o); ieee_realcvt((void *)&dresult, idc_var->e, 3); idc_var->vtype = VT_FLOAT; } // void* else if ( PyCObject_Check(py_var.o) ) { idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); } // Python list? else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) { // Create the object VarObject(idc_var); // Determine list size and type bool is_seq = !PyList_CheckExact(py_var.o); Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); bool ok = true; qstring attr_name; // Convert each item for ( Py_ssize_t i=0; i < size; i++ ) { // Get the item ref_t py_item; if ( is_seq ) py_item = newref_t(PySequence_GetItem(py_var.o, i)); else py_item = borref_t(PyList_GetItem(py_var.o, i)); // Convert the item into an IDC variable idc_value_t v; ok = pyvar_to_idcvar(py_item, &v, gvar_sn) >= CIP_OK; if ( ok ) { // Form the attribute name newref_t py_int(PyInt_FromSsize_t(i)); ok = PyW_ObjectToString(py_int.o, &attr_name); if ( !ok ) break; // Store the attribute VarSetAttr(idc_var, attr_name.c_str(), &v); } if ( !ok ) break; } return ok ? CIP_OK : CIP_FAILED; } // Dictionary: we convert to an IDC object else if ( PyDict_Check(py_var.o) ) { // Create an empty IDC object VarObject(idc_var); // Get the dict.items() list newref_t py_items(PyDict_Items(py_var.o)); // Get the size of the list qstring key_name; bool ok = true; Py_ssize_t size = PySequence_Size(py_items.o); for ( Py_ssize_t i=0; i < size; i++ ) { // Get item[i] -> (key, value) PyObject *py_item = PyList_GetItem(py_items.o, i); // Extract key/value newref_t key(PySequence_GetItem(py_item, 0)); newref_t val(PySequence_GetItem(py_item, 1)); // Get key's string representation PyW_ObjectToString(key.o, &key_name); // Convert the attribute into an IDC value idc_value_t v; ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; if ( ok ) { // Store the attribute VarSetAttr(idc_var, key_name.c_str(), &v); } if ( !ok ) break; } return ok ? CIP_OK : CIP_FAILED; } // Possible function? else if ( PyCallable_Check(py_var.o) ) { idc_var->clear(); idc_var->vtype = VT_FUNC; idc_var->funcidx = -1; // Does not apply return CIP_OK; } // Objects: // - pyidc_cvt objects: int64, byref, opaque // - other python objects else { // Get the type int cvt_id = get_pyidc_cvt_type(py_var.o); switch ( cvt_id ) { // // INT64 // case PY_ICID_INT64: { // Get the value attribute ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); if ( attr == NULL ) return false; idc_var->set_int64(PyLong_AsLongLong(attr.o)); return CIP_OK; } // // BYREF // case PY_ICID_BYREF: { // BYREF always require this parameter if ( gvar_sn == NULL ) return CIP_FAILED; // Get the value attribute ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); if ( attr == NULL ) return CIP_FAILED; // Create a global variable char buf[MAXSTR]; qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); idc_value_t *gvar = add_idc_gvar(buf); // Convert the python value into the IDC global variable bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; if ( ok ) { (*gvar_sn)++; // Create a reference to this global variable VarRef(idc_var, gvar); } return ok ? CIP_OK : CIP_FAILED; } // // OPAQUE // case PY_ICID_OPAQUE: { if ( !wrap_PyObject_ptr(py_var, idc_var) ) return CIP_FAILED; return CIP_OK_OPAQUE; } // // Other objects // default: // A normal object? newref_t py_dir(PyObject_Dir(py_var.o)); Py_ssize_t size = PyList_Size(py_dir.o); if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) return CIP_FAILED; // Create the IDC object VarObject(idc_var); for ( Py_ssize_t i=0; i < size; i++ ) { borref_t item(PyList_GetItem(py_dir.o, i)); const char *field_name = PyString_AsString(item.o); if ( field_name == NULL ) continue; size_t len = strlen(field_name); // Skip private attributes if ( (len > 2 ) && (strncmp(field_name, "__", 2) == 0 ) && (strncmp(field_name+len-2, "__", 2) == 0) ) { continue; } idc_value_t v; // Get the non-private attribute from the object newref_t attr(PyObject_GetAttrString(py_var.o, field_name)); if ( attr == NULL // Convert the attribute into an IDC value || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK ) { return CIP_FAILED; } // Store the attribute VarSetAttr(idc_var, field_name, &v); } } } return CIP_OK; } //------------------------------------------------------------------------- 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 // If py_var points to an existing object then the object will be updated // If py_var points to an existing immutable object then ZERO is returned // Returns one of CIP_xxxx. Check pywraps.hpp int ida_export idcvar_to_pyvar( const idc_value_t &idc_var, ref_t *py_var) { PYW_GIL_CHECK_LOCKED_SCOPE(); switch ( idc_var.vtype ) { case VT_PVOID: if ( *py_var == NULL ) { newref_t nr(PyCObject_FromVoidPtr(idc_var.pvoid, NULL)); *py_var = nr; } else { return CIP_IMMUTABLE; } break; case VT_INT64: { // Recycle? if ( *py_var != NULL ) { // Recycling an int64 object? int t = get_pyidc_cvt_type(py_var->o); if ( t != PY_ICID_INT64 ) 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)); return CIP_OK; } ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); if ( py_cls == NULL ) return CIP_FAILED; *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; break; } #if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) case VT_STR: *py_var = newref_t(PyString_FromString(idc_var.str)); break; #endif case VT_STR2: if ( *py_var == NULL ) { const qstring &s = idc_var.qstr(); *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); break; } else return CIP_IMMUTABLE; // Cannot recycle immutable object case VT_LONG: // Cannot recycle immutable objects if ( *py_var != NULL ) return CIP_IMMUTABLE; *py_var = newref_t(cvt_to_pylong(idc_var.num)); break; case VT_FLOAT: if ( *py_var == NULL ) { double x; if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) INTERR(30160); *py_var = newref_t(PyFloat_FromDouble(x)); break; } else return CIP_IMMUTABLE; case VT_REF: { if ( *py_var == NULL ) { ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); if ( py_cls == NULL ) return CIP_FAILED; // Create a byref object with None value. We populate it later *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; } int t = get_pyidc_cvt_type(py_var->o); if ( t != PY_ICID_BYREF ) return CIP_FAILED; // Dereference // (Since we are not using VREF_COPY flag, we can safely const_cast) idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); if ( dref_v == NULL ) return CIP_FAILED; // Can we recycle the object? ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); if ( new_py_val != NULL ) { // Recycle t = idcvar_to_pyvar(*dref_v, &new_py_val); // Success? Nothing more to be done if ( t == CIP_OK ) return CIP_OK; // Clear it so we don't recycle it new_py_val = ref_t(); } // Try to convert (not recycle) if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) return CIP_FAILED; // Update the attribute PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); break; } // Can convert back into a Python object or Python dictionary // (Depending if py_var will be recycled and it was a dictionary) case VT_OBJ: { // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields idc_value_t idc_val; if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) { // Extract the object *py_var = borref_t((PyObject *) idc_val.pvoid); return CIP_OK_OPAQUE; } ref_t obj; bool is_dict = false; // Need to create a new object? if ( *py_var == NULL ) { // Get skeleton class reference ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); if ( py_cls == NULL ) return CIP_FAILED; // Call constructor obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); if ( PyW_GetError() || obj == NULL ) return CIP_FAILED; } else { // Recycle existing variable obj = *py_var; if ( PyDict_Check(obj.o) ) is_dict = true; } // Walk the IDC attributes and store into python for ( const char *attr_name = VarFirstAttr(&idc_var); attr_name != NULL; attr_name = VarNextAttr(&idc_var, attr_name) ) { // Get the attribute idc_value_t v; VarGetAttr(&idc_var, attr_name, &v, true); // Convert attribute to a python value (recursively) ref_t py_attr; int cvt = idcvar_to_pyvar(v, &py_attr); if ( cvt <= CIP_IMMUTABLE ) return CIP_FAILED; if ( is_dict ) PyDict_SetItemString(obj.o, attr_name, py_attr.o); else PyObject_SetAttrString(obj.o, attr_name, py_attr.o); } *py_var = obj; break; } // Unhandled type default: *py_var = ref_t(); return CIP_FAILED; } return CIP_OK; } //------------------------------------------------------------------------- // Converts IDC arguments to Python argument list or just one tuple // If 'decref' is NULL then 'pargs' will contain one element which is the tuple bool ida_export pyw_convert_idc_args( const idc_value_t args[], int nargs, ref_vec_t &pargs, bool as_tupple, char *errbuf, size_t errbufsize) { PYW_GIL_CHECK_LOCKED_SCOPE(); ref_t py_tuple; pargs.qclear(); if ( as_tupple ) { py_tuple = newref_t(PyTuple_New(nargs)); if ( py_tuple == NULL ) { if ( errbuf != 0 && errbufsize > 0 ) qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); return false; } } for ( int i=0; i < nargs; i++ ) { ref_t py_obj; int cvt = idcvar_to_pyvar(args[i], &py_obj); if ( cvt < CIP_OK ) { if ( errbuf != 0 && errbufsize > 0 ) qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); return false; } if ( as_tupple ) { // PyTuple_SetItem() steals the reference. py_obj.incref(); QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0); } else { pargs.push_back(py_obj); } } // Add the tuple to the list of args only now. Doing so earlier will // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will // cause 'PyTuple_SetItem()' to fail. if ( as_tupple ) pargs.push_back(py_tuple); return true; } //------------------------------------------------------------------------ static ref_t ida_idaapi_module; static ref_t compat_idaapi_module; static bool pywraps_initialized = false; #define SWIG_RUNTIME_VERSION "4" //------------------------------------------------------------------------ // check if we have a file which is known to be executed automatically // by SWIG or Python runtime static bool pywraps_check_autoscripts(char *buf, size_t bufsize) { static const char *const exts[] = { "py", "pyc", "pyd", "pyo", "pyw", }; static const char *const fns[] = { "swig_runtime_data" SWIG_RUNTIME_VERSION, "sitecustomize", "usercustomize" }; for ( size_t ifn=0; ifn < qnumber(fns); ++ifn ) { // check for a script or module with several possible extensions for ( size_t iext=0; iext < qnumber(exts); ++iext ) { qsnprintf(buf, bufsize, "%s.%s", fns[ifn], exts[iext]); if ( qfileexist(buf) ) return true; } // check for a subdirectory under current directory if ( qfileexist(fns[ifn]) ) { qstrncpy(buf, fns[ifn], bufsize); return true; } } return false; } //------------------------------------------------------------------------ error_t ida_export PyW_CreateIdcException(idc_value_t *res, const char *msg) { // Create exception object VarObject(res, find_idc_class("exception")); // Set the message field idc_value_t v; v.set_string(msg); VarSetAttr(res, "message", &v); // Throw exception return set_qerrno(eExecThrow); } //------------------------------------------------------------------------ // Calls a Python callable encoded in IDC.pvoid member static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; static error_t idaapi idc_py_invoke0( idc_value_t *argv, idc_value_t *res) { PYW_GIL_GET; PyObject *pyfunc = (PyObject *) argv[0].pvoid; newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); // Report Python error as IDC exception qstring err; error_t err_code = eOk; if ( PyW_GetError(&err) ) err_code = PyW_CreateIdcException(res, err.c_str()); return err_code; } //------------------------------------------------------------------------ // This function must be called on initialization static bool init_pywraps() { if ( pywraps_initialized ) return true; // Take a reference to the idaapi python module // (We need it to create instances of certain classes) if ( ida_idaapi_module == NULL ) { // Take a reference to the module so we can create the needed class instances ida_idaapi_module = PyW_TryImportModule(S_PY_IDA_IDAAPI_MODNAME); if ( ida_idaapi_module == NULL ) return false; } // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) return false; // IDC opaque class not registered? if ( get_py_idc_cvt_opaque() == NULL ) { // Add the class idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); if ( idc_cvt_opaque == NULL ) return false; // Form the dtor name char dtor_name[MAXSTR]; qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); // Register the dtor function if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) return false; // Link the dtor function to the class set_idc_dtor(idc_cvt_opaque, dtor_name); } pywraps_initialized = true; return true; } //------------------------------------------------------------------------ // This function must be called on de-initialization static void deinit_pywraps() { if ( !pywraps_initialized ) return; pywraps_initialized = false; { PYW_GIL_CHECK_LOCKED_SCOPE(); ida_idaapi_module = ref_t(); // Deref. } // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); } //------------------------------------------------------------------------ // Utility function to create linked class instances ref_t ida_export create_linked_class_instance( const char *modname, const char *clsname, void *lnk) { PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t py_module(PyImport_ImportModule(modname)); ref_t result; if ( py_module != NULL ) { ref_t py_class = PyW_TryGetAttrString(py_module.o, clsname); if ( py_class != NULL ) { newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_class.o, py_lnk.o, NULL)); if ( !PyW_GetError() && py_obj != NULL ) result = py_obj; } } return result; } //------------------------------------------------------------------------ // Gets a class type reference in idaapi // With the class type reference we can create a new instance of that type // This function takes a reference to the idaapi module and keeps the reference static ref_t get_idaapi_attr_by_id(const int class_id) { if ( class_id >= PY_CLSID_LAST || ida_idaapi_module == NULL ) return ref_t(); // Some class names. The array is parallel with the PY_CLSID_xxx consts static const char *class_names[]= { "PyIdc_cvt_int64__", "object_t", "PyIdc_cvt_refclass__" }; PYW_GIL_CHECK_LOCKED_SCOPE(); return newref_t(PyObject_GetAttrString(ida_idaapi_module.o, class_names[class_id])); } //------------------------------------------------------------------------ // Gets a class reference by name static ref_t get_idaapi_attr(const char *attrname) { PYW_GIL_CHECK_LOCKED_SCOPE(); return ida_idaapi_module == NULL ? ref_t() : PyW_TryGetAttrString(ida_idaapi_module.o, attrname); } //------------------------------------------------------------------------ // Returns a qstring from an object attribute bool ida_export PyW_GetStringAttr( PyObject *py_obj, const char *attr_name, qstring *str) { PYW_GIL_CHECK_LOCKED_SCOPE(); ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); if ( py_attr == NULL ) return false; bool ok = PyString_Check(py_attr.o); if ( ok ) *str = PyString_AsString(py_attr.o); return ok; } //------------------------------------------------------------------------ // Returns an attribute or NULL // No errors will be set if the attribute did not exist ref_t ida_export PyW_TryGetAttrString(PyObject *py_obj, const char *attr) { PYW_GIL_CHECK_LOCKED_SCOPE(); ref_t o; if ( PyObject_HasAttrString(py_obj, attr) ) o = newref_t(PyObject_GetAttrString(py_obj, attr)); return o; } //------------------------------------------------------------------------ // Tries to import a module and clears the exception on failure ref_t ida_export PyW_TryImportModule(const char *name) { PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t result(PyImport_ImportModule(name)); if ( result == NULL && PyErr_Occurred() ) PyErr_Clear(); return result; } //------------------------------------------------------------------------- // Converts a Python number into an IDC value (32 or 64bits) // The function will first try to convert the number into a 32bit value // If the number does not fit then VT_INT64 will be used // NB: This function cannot properly detect if the Python value should be // converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which // can fit in a C long but Python creates a PyLong object for it. // And because of that we are confused as to whether to convert to 32 or 64 bool ida_export PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) { PYW_GIL_CHECK_LOCKED_SCOPE(); bool rc = true; do { if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) { rc = false; break; } // Can we convert to C long? long l = PyInt_AsLong(py_var); if ( !PyErr_Occurred() ) { idc_var->set_long(l); break; } // Clear last error PyErr_Clear(); // Can be fit into a C unsigned long? l = (long) PyLong_AsUnsignedLong(py_var); if ( !PyErr_Occurred() ) { idc_var->set_long(l); break; } PyErr_Clear(); idc_var->set_int64(PyLong_AsLongLong(py_var)); } while ( false ); return rc; } //------------------------------------------------------------------------- // Parses a Python object as a long or long long bool ida_export PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) { PYW_GIL_CHECK_LOCKED_SCOPE(); bool rc = true; #define SETNUM(numexpr, is64_expr) \ do \ { \ if ( num != NULL ) \ *num = numexpr; \ if ( is_64 != NULL ) \ *is_64 = is64_expr; \ } while ( false ) do { if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) { rc = false; break; } // Can we convert to C long? long l = PyInt_AsLong(py_var); if ( !PyErr_Occurred() ) { SETNUM(uint64(l), false); break; } // Clear last error PyErr_Clear(); // Can be fit into a C unsigned long? unsigned long ul = PyLong_AsUnsignedLong(py_var); if ( !PyErr_Occurred() ) { SETNUM(uint64(ul), false); break; } PyErr_Clear(); // Try to parse as int64 PY_LONG_LONG ll = PyLong_AsLongLong(py_var); if ( !PyErr_Occurred() ) { SETNUM(uint64(ll), true); break; } PyErr_Clear(); // Try to parse as uint64 unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); PyObject *err = PyErr_Occurred(); if ( err == NULL ) { SETNUM(uint64(ull), true); break; } // Negative number? _And_ it with uint64(-1) rc = false; if ( err == PyExc_TypeError ) { newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); newref_t py_num(PyNumber_And(py_var, py_mask.o)); if ( py_num != NULL && py_mask != NULL ) { PyErr_Clear(); ull = PyLong_AsUnsignedLongLong(py_num.o); if ( !PyErr_Occurred() ) { SETNUM(uint64(ull), true); rc = true; } } } PyErr_Clear(); } while ( false ); return rc; #undef SETNUM } //------------------------------------------------------------------------- // Checks if a given object is of sequence type bool ida_export PyW_IsSequenceType(PyObject *obj) { PYW_GIL_CHECK_LOCKED_SCOPE(); bool rc = true; do { if ( !PySequence_Check(obj) ) { rc = false; break; } Py_ssize_t sz = PySequence_Size(obj); if ( sz == -1 || PyErr_Occurred() != NULL ) { PyErr_Clear(); rc = false; break; } } while ( false ); return rc; } //------------------------------------------------------------------------- // Returns the string representation of an object bool ida_export PyW_ObjectToString(PyObject *obj, qstring *out) { PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t py_str(PyObject_Str(obj)); bool ok = py_str != NULL; if ( ok ) *out = PyString_AsString(py_str.o); else out->qclear(); return ok; } //-------------------------------------------------------------------------- // Checks if a Python error occured and fills the out parameter with the // exception string bool ida_export PyW_GetError(qstring *out, bool clear_err) { PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyErr_Occurred() == NULL ) return false; // Error occurred but details not needed? if ( out == NULL ) { // Just clear the error if ( clear_err ) PyErr_Clear(); return true; } // Get the exception info PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); PyErr_Fetch(&err_type, &err_value, &err_traceback); if ( !clear_err ) PyErr_Restore(err_type, err_value, err_traceback); // Resolve FormatExc() ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); // Helper there? if ( py_fmtexc != NULL ) { // Call helper py_ret = PyObject_CallFunctionObjArgs( py_fmtexc.o, err_type, err_value, err_traceback, NULL); } // Clear the error if ( clear_err ) PyErr_Clear(); // Helper failed?! if ( py_ret == NULL ) { // Just convert the 'value' part of the original error py_ret = PyObject_Str(err_value); } // No exception text? if ( py_ret == NULL ) { *out = "IDAPython: unknown error!"; } else { *out = PyString_AsString(py_ret); Py_DECREF(py_ret); } if ( clear_err ) { Py_XDECREF(err_traceback); Py_XDECREF(err_value); Py_XDECREF(err_type); } return true; } //------------------------------------------------------------------------- static bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) { PYW_GIL_CHECK_LOCKED_SCOPE(); qstring s; if ( !PyW_GetError(&s, clear_err) ) return false; qstrncpy(buf, s.c_str(), bufsz); return true; } //------------------------------------------------------------------------- // A loud version of PyGetError() which gets the error and displays it // This method is used to display errors that occurred in a callback bool ida_export PyW_ShowCbErr(const char *cb_name) { PYW_GIL_CHECK_LOCKED_SCOPE(); static qstring err_str; if ( !PyW_GetError(&err_str) ) return false; msg("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); return true; } //--------------------------------------------------------------------------- void *ida_export pyobj_get_clink(PyObject *pyobj) { PYW_GIL_CHECK_LOCKED_SCOPE(); // Try to query the link attribute ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; return t; } //------------------------------------------------------------------------ int idaapi pywraps_notify_when_t::idp_callback(void *ud, int event_id, va_list va) { pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; switch ( event_id ) { case processor_t::newfile: case processor_t::oldfile: { // This hook gets called from the kernel. Ensure we hold the GIL. // Note that PYW_GIL_GET appears in each case of the switch, which is to // ensure that the GIL is retrieved ONLY when we need it. If PYW_GIL_GET // appears outside the switch, it will be executed each time this callback // is called, which results in a huge slowdown (at least on mac). PYW_GIL_GET; int old = event_id == processor_t::oldfile ? 1 : 0; char *dbname = va_arg(va, char *); _this->notify(NW_OPENIDB_SLOT, old); } break; case processor_t::closebase: { PYW_GIL_GET; _this->notify(NW_CLOSEIDB_SLOT); } break; } // event not processed, let other plugins or the processor module handle it return 0; } //------------------------------------------------------------------------ bool pywraps_notify_when_t::unnotify_when(int when, PyObject *py_callable) { int cnt = 0; for ( int slot=0; slot < NW_EVENTSCNT; slot++ ) { // convert index to flag and see if ( ((1 << slot) & when) != 0 ) { unregister_callback(slot, py_callable); ++cnt; } } return cnt > 0; } //------------------------------------------------------------------------ void pywraps_notify_when_t::register_callback(int slot, PyObject *py_callable) { borref_t callable_ref(py_callable); ref_vec_t &tbl = table[slot]; ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); // Already added if ( it != it_end ) return; // Insert the element tbl.push_back(callable_ref); } //------------------------------------------------------------------------ void pywraps_notify_when_t::unregister_callback(int slot, PyObject *py_callable) { borref_t callable_ref(py_callable); ref_vec_t &tbl = table[slot]; ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); // Not found? if ( it == it_end ) return; // Delete the element tbl.erase(it); } //------------------------------------------------------------------------ bool pywraps_notify_when_t::init() { return hook_to_notification_point(HT_IDP, idp_callback, this); } //------------------------------------------------------------------------ bool pywraps_notify_when_t::deinit() { // Uninstall all objects ref_vec_t::iterator it, it_end; for ( int slot=0; slot < NW_EVENTSCNT; slot++ ) { for ( it = table[slot].begin(), it_end = table[slot].end(); it != it_end; ++it ) unregister_callback(slot, it->o); } // ...and remove the notification return unhook_from_notification_point(HT_IDP, idp_callback, this); } //------------------------------------------------------------------------ bool pywraps_notify_when_t::notify_when(int when, PyObject *py_callable) { // While in notify() do not allow insertion or deletion to happen on the spot // Instead we will queue them so that notify() will carry the action when it finishes // dispatching the notification handlers if ( in_notify ) { notify_when_args_t &args = delayed_notify_when_list.push_back(); args.when = when; args.py_callable = py_callable; return true; } // Uninstalling the notification? if ( (when & NW_REMOVE) != 0 ) return unnotify_when(when & ~NW_REMOVE, py_callable); int cnt = 0; for ( int slot=0; slot < NW_EVENTSCNT; slot++ ) { // is this flag set? if ( ((1 << slot) & when) != 0 ) { register_callback(slot, py_callable); ++cnt; } } return cnt > 0; } //------------------------------------------------------------------------ bool pywraps_notify_when_t::notify(int slot, ...) { va_list va; va_start(va, slot); bool ok = notify_va(slot, va); va_end(va); return ok; } //------------------------------------------------------------------------ bool pywraps_notify_when_t::notify_va(int slot, va_list va) { PYW_GIL_CHECK_LOCKED_SCOPE(); // Sanity bounds check! if ( slot < 0 || slot >= NW_EVENTSCNT ) return false; bool ok = true; in_notify = true; int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; { for ( ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); it != it_end; ++it ) { // Form the notification code newref_t py_code(PyInt_FromLong(1 << slot)); ref_t py_result; switch ( slot ) { case NW_CLOSEIDB_SLOT: case NW_INITIDA_SLOT: case NW_TERMIDA_SLOT: { py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); break; } case NW_OPENIDB_SLOT: { newref_t py_old(PyInt_FromLong(old)); py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); } break; } if ( PyW_GetError(&err) || py_result == NULL ) { PyErr_Clear(); warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); ok = false; } } } in_notify = false; // Process any delayed notify_when() calls that if ( !delayed_notify_when_list.empty() ) { notify_when_args_vec_t::iterator it, it_end; for ( it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); it != it_end; ++it ) { notify_when(it->when, it->py_callable); } delayed_notify_when_list.qclear(); } return ok; } //------------------------------------------------------------------------- static pywraps_notify_when_t *g_nw = NULL; //------------------------------------------------------------------------- bool ida_export add_notify_when(int when, PyObject *py_callable) { return g_nw != NULL && g_nw->notify_when(when, py_callable); } //------------------------------------------------------------------------ // Initializes the notify_when mechanism // (Normally called by IDAPython plugin.init()) static bool pywraps_nw_init() { if ( g_nw != NULL ) return true; g_nw = new pywraps_notify_when_t(); if ( g_nw->init() ) return true; // Things went bad, undo! delete g_nw; g_nw = NULL; return false; } //------------------------------------------------------------------------ static bool pywraps_nw_notify(int slot, ...) { if ( g_nw == NULL ) return false; // Appears to be called from 'driver_notifywhen.cpp', which // itself is called from possibly non-python code. // I.e., we must acquire the GIL. PYW_GIL_GET; va_list va; va_start(va, slot); bool ok = g_nw->notify_va(slot, va); va_end(va); return ok; } //------------------------------------------------------------------------ // Deinitializes the notify_when mechanism static 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; return true; } //------------------------------------------------------------------------- // lookup_info_t //------------------------------------------------------------------------- lookup_entry_t &ida_export lookup_info_t_new_entry(lookup_info_t *_this, py_customidamemo_t *py_view) { QASSERT(30454, py_view != NULL && !_this->find_by_py_view(NULL, NULL, py_view)); lookup_entry_t &e = _this->entries.push_back(); e.py_view = py_view; return e; } //------------------------------------------------------------------------- void ida_export lookup_info_t_commit(lookup_info_t *_this, lookup_entry_t &e, TForm *form, TCustomControl *view) { QASSERT(30455, &e >= _this->entries.begin() && &e < _this->entries.end()); QASSERT(30456, form != NULL && view != NULL && e.py_view != NULL && !_this->find_by_form(NULL, NULL, form) && !_this->find_by_view(NULL, NULL, view) && _this->find_by_py_view(NULL, NULL, e.py_view)); e.form = form; e.view = view; } //------------------------------------------------------------------------- #define FIND_BY__BODY(self, crit, res1, res2) \ { \ for ( lookup_entries_t::const_iterator it = self->entries.begin(); it != self->entries.end(); ++it ) \ { \ const lookup_entry_t &e = *it; \ if ( e.crit == crit ) \ { \ if ( out_##res1 != NULL ) \ *out_##res1 = e.res1; \ if ( out_##res2 != NULL ) \ *out_##res2 = e.res2; \ return true; \ } \ } \ return false; \ } bool ida_export lookup_info_t_find_by_form( const lookup_info_t *_this, TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) FIND_BY__BODY(_this, form, view, py_view); bool ida_export lookup_info_t_find_by_py_view( const lookup_info_t *_this, TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) FIND_BY__BODY(_this, py_view, view, form); bool lookup_info_t::find_by_view( TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(this, view, form, py_view); #undef FIND_BY__BODY //------------------------------------------------------------------------- bool ida_export lookup_info_t_del_by_py_view( lookup_info_t *_this, const py_customidamemo_t *py_view) { for ( lookup_entries_t::iterator it = _this->entries.begin(); it != _this->entries.end(); ++it ) { if ( it->py_view == py_view ) { _this->entries.erase(it); return true; } } return false; } //------------------------------------------------------------------------- lookup_info_t pycim_lookup_info; //------------------------------------------------------------------------- // py_customidamemo_t //------------------------------------------------------------------------- void py_customidamemo_t::convert_node_info( node_info_t *out, uint32 *out_flags, ref_t py_nodeinfo) { if ( out_flags != NULL ) *out_flags = 0; #define COPY_PROP(checker, converter, pname, flag) \ do \ { \ newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \ if ( pname != NULL && checker(pname.o) ) \ { \ out->pname = converter(pname.o); \ if ( out_flags != NULL ) \ *out_flags |= flag; \ } \ } while ( false ) #define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag) #define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag) COPY_ULONG_PROP(bg_color, NIF_BG_COLOR); COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR); COPY_ULONG_PROP(ea, NIF_EA); COPY_STRING_PROP(text, NIF_TEXT); #undef COPY_STRING_PROP #undef COPY_ULONG_PROP #undef COPY_PROP } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_ensure_view_callbacks_installed() { static bool installed = false; if ( !installed ) { struct ida_local lambda_t { static int idaapi callback(void * /*ud*/, int code, va_list va) { py_customidamemo_t *py_view; if ( pycim_lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) ) { PYW_GIL_GET; switch ( code ) { case view_activated: py_view->on_view_activated(); break; case view_deactivated: py_view->on_view_deactivated(); break; case view_keydown: { int key = va_arg(va, int); int state = va_arg(va, int); py_view->on_view_keydown(key, state); } break; case obsolete_view_popup: py_view->on_view_popup(); break; case view_click: case view_dblclick: { const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); if ( code == view_click ) py_view->on_view_click(event); else py_view->on_view_dblclick(event); } break; case view_curpos: py_view->on_view_curpos(); break; case view_close: py_view->on_view_close(); delete py_view; break; case view_switched: { tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int); py_view->on_view_switched(rt); } break; case view_mouse_over: { const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); py_view->on_view_mouse_over(event); } break; } } return 0; } }; hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL); installed = true; } } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_set_node_info( py_customidamemo_t *_this, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) { if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) ) return; borref_t py_idx(py_node_idx); borref_t py_ni(py_node_info); borref_t py_fl(py_flags); node_info_t ni; _this->convert_node_info(&ni, NULL, py_ni); int idx = PyInt_AsLong(py_idx.o); uint32 flgs = PyLong_AsLong(py_fl.o); viewer_set_node_info(_this->view, idx, ni, flgs); } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_set_nodes_infos( py_customidamemo_t *_this, PyObject *dict) { if ( !PyDict_Check(dict) ) return; Py_ssize_t pos = 0; PyObject *o_key, *o_value; while ( PyDict_Next(dict, &pos, &o_key, &o_value) ) { borref_t key(o_key); borref_t value(o_value); if ( !PyNumber_Check(key.o) ) continue; uint32 flags; node_info_t ni; _this->convert_node_info(&ni, &flags, value); int idx = PyInt_AsLong(key.o); viewer_set_node_info(_this->view, idx, ni, flags); } } //------------------------------------------------------------------------- PyObject *ida_export py_customidamemo_t_get_node_info( py_customidamemo_t *_this, PyObject *py_node_idx) { if ( !PyNumber_Check(py_node_idx) ) Py_RETURN_NONE; node_info_t ni; if ( !viewer_get_node_info(_this->view, &ni, PyInt_AsLong(py_node_idx)) ) Py_RETURN_NONE; return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str()); } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_del_nodes_infos( py_customidamemo_t *_this, PyObject *py_nodes) { if ( !PySequence_Check(py_nodes) ) return; Py_ssize_t sz = PySequence_Size(py_nodes); for ( Py_ssize_t i = 0; i < sz; ++i ) { newref_t item(PySequence_GetItem(py_nodes, i)); if ( !PyNumber_Check(item.o) ) continue; int idx = PyInt_AsLong(item.o); viewer_del_node_info(_this->view, idx); } } //------------------------------------------------------------------------- PyObject *ida_export py_customidamemo_t_get_current_renderer_type( py_customidamemo_t *_this) { tcc_renderer_type_t rt = get_view_renderer_type(_this->view); return PyLong_FromLong(long(rt)); } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_set_current_renderer_type( py_customidamemo_t *_this, PyObject *py_rto) { tcc_renderer_type_t rt = TCCRT_INVALID; borref_t py_rt(py_rto); if ( PyNumber_Check(py_rt.o) ) { rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o)); set_view_renderer_type(_this->view, rt); } } //------------------------------------------------------------------------- PyObject *ida_export py_customidamemo_t_create_groups( py_customidamemo_t *_this, PyObject *_groups_infos) { if ( !PySequence_Check(_groups_infos) ) Py_RETURN_NONE; borref_t groups_infos(_groups_infos); groups_crinfos_t gis; Py_ssize_t sz = PySequence_Size(groups_infos.o); for ( Py_ssize_t i = 0; i < sz; ++i ) { newref_t item(PySequence_GetItem(groups_infos.o, i)); if ( !PyDict_Check(item.o) ) continue; borref_t nodes(PyDict_GetItemString(item.o, "nodes")); if ( nodes.o == NULL || !PySequence_Check(nodes.o) ) continue; borref_t text(PyDict_GetItemString(item.o, "text")); if ( text.o == NULL || !PyString_Check(text.o) ) continue; group_crinfo_t gi; Py_ssize_t nodes_cnt = PySequence_Size(nodes.o); for ( Py_ssize_t k = 0; k < nodes_cnt; ++k ) { newref_t node(PySequence_GetItem(nodes.o, k)); if ( PyInt_Check(node.o) ) gi.nodes.add_unique(PyInt_AsLong(node.o)); } if ( !gi.nodes.empty() ) { gi.text = PyString_AsString(text.o); gis.push_back(gi); } } intvec_t groups; if ( gis.empty() || !viewer_create_groups(_this->view, &groups, gis) || groups.empty() ) Py_RETURN_NONE; PyObject *py_groups = PyList_New(0); for ( intvec_t::const_iterator it = groups.begin(); it != groups.end(); ++it ) PyList_Append(py_groups, PyInt_FromLong(long(*it))); return py_groups; } //------------------------------------------------------------------------- static void pynodes_to_idanodes(intvec_t *idanodes, ref_t pynodes) { Py_ssize_t sz = PySequence_Size(pynodes.o); for ( Py_ssize_t i = 0; i < sz; ++i ) { newref_t item(PySequence_GetItem(pynodes.o, i)); if ( !PyInt_Check(item.o) ) continue; idanodes->add_unique(PyInt_AsLong(item.o)); } } //------------------------------------------------------------------------- PyObject *ida_export py_customidamemo_t_delete_groups( py_customidamemo_t *_this, PyObject *_groups, PyObject *_new_current) { if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) ) Py_RETURN_NONE; borref_t groups(_groups); borref_t new_current(_new_current); intvec_t ida_groups; pynodes_to_idanodes(&ida_groups, groups); if ( ida_groups.empty() ) Py_RETURN_NONE; if ( viewer_delete_groups(_this->view, ida_groups, int(PyInt_AsLong(new_current.o))) ) Py_RETURN_TRUE; else Py_RETURN_FALSE; } //------------------------------------------------------------------------- PyObject *ida_export py_customidamemo_t_set_groups_visibility( py_customidamemo_t *_this, PyObject *_groups, PyObject *_expand, PyObject *_new_current) { if ( !PySequence_Check(_groups) || !PyBool_Check(_expand) || !PyNumber_Check(_new_current) ) Py_RETURN_NONE; borref_t groups(_groups); borref_t expand(_expand); borref_t new_current(_new_current); intvec_t ida_groups; pynodes_to_idanodes(&ida_groups, groups); if ( ida_groups.empty() ) Py_RETURN_NONE; if ( viewer_set_groups_visibility(_this->view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) Py_RETURN_TRUE; else Py_RETURN_FALSE; } //------------------------------------------------------------------------- bool ida_export py_customidamemo_t_bind(py_customidamemo_t *_this, PyObject *self, TCustomControl *view) { if ( _this->self != NULL || _this->view != NULL ) return false; PYGLOG("%p: py_customidamemo_t::bind(self=%p, view=%p)\n", this, _this->_self, _this->view); PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t py_cobj(PyCObject_FromVoidPtr(_this, NULL)); PyObject_SetAttrString(self, S_M_THIS, py_cobj.o); _this->self = borref_t(self); _this->view = view; return true; } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_unbind(py_customidamemo_t *_this, bool clear_view) { if ( _this->self == NULL ) return; PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", _this, _this->self.o, _this->view); PYW_GIL_CHECK_LOCKED_SCOPE(); newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); PyObject_SetAttrString(_this->self.o, S_M_THIS, py_cobj.o); _this->self = newref_t(NULL); if ( clear_view ) _this->view = NULL; } //------------------------------------------------------------------------- void idaapi py_customidamemo_t::s_on_view_mouse_moved( TCustomControl *cv, int shift, view_mouse_event_t *e, void *ud) { PYW_GIL_GET; py_customidamemo_t *_this = (py_customidamemo_t *) ud; _this->on_view_mouse_moved(e); } //------------------------------------------------------------------------- int py_customidamemo_t::get_py_method_arg_count(char *method_name) { newref_t method(PyObject_GetAttrString(self.o, method_name)); if ( method != NULL && PyCallable_Check(method.o) ) { newref_t fc(PyObject_GetAttrString(method.o, "func_code")); if ( fc != NULL ) { newref_t ac(PyObject_GetAttrString(fc.o, "co_argcount")); if ( ac != NULL ) return PyInt_AsLong(ac.o); } } return -1; } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_collect_class_callbacks_ids( py_customidamemo_t *_this, pycim_callbacks_ids_t *out) { out->add(S_ON_VIEW_ACTIVATED, _this->GRBASE_HAVE_VIEW_ACTIVATED); out->add(S_ON_VIEW_DEACTIVATED, _this->GRBASE_HAVE_VIEW_DEACTIVATED); out->add(S_ON_VIEW_KEYDOWN, _this->GRBASE_HAVE_KEYDOWN); out->add(S_ON_POPUP, _this->GRBASE_HAVE_POPUP); out->add(S_ON_VIEW_CLICK, _this->GRBASE_HAVE_VIEW_CLICK); out->add(S_ON_VIEW_DBLCLICK, _this->GRBASE_HAVE_VIEW_DBLCLICK); out->add(S_ON_VIEW_CURPOS, _this->GRBASE_HAVE_VIEW_CURPOS); out->add(S_ON_CLOSE, _this->GRBASE_HAVE_CLOSE); out->add(S_ON_VIEW_SWITCHED, _this->GRBASE_HAVE_VIEW_SWITCHED); out->add(S_ON_VIEW_MOUSE_OVER, _this->GRBASE_HAVE_VIEW_MOUSE_OVER); out->add(S_ON_VIEW_MOUSE_MOVED, _this->GRBASE_HAVE_VIEW_MOUSE_MOVED); } //------------------------------------------------------------------------- bool ida_export py_customidamemo_t_collect_pyobject_callbacks( py_customidamemo_t *_this, PyObject *o) { pycim_callbacks_ids_t cbids; _this->collect_class_callbacks_ids(&cbids); _this->cb_flags = 0; for ( pycim_callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) { const pycim_callback_id_t &cbid = *it; ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str())); int have = cbid.have; // Mandatory fields not present? if ( (attr == NULL && have <= 0) // Mandatory callback fields present but not callable? || (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0) ) { return false; } if ( have > 0 && attr != NULL ) _this->cb_flags |= have; } return true; } //------------------------------------------------------------------------- void ida_export py_customidamemo_t_install_custom_viewer_handlers( py_customidamemo_t *_this) { if ( _this->has_callback(_this->GRBASE_HAVE_VIEW_MOUSE_MOVED) ) { // Set user-data set_custom_viewer_handler(_this->view, CVH_USERDATA, (void *)_this); // set_custom_viewer_handler(_this->view, CVH_MOUSEMOVE, (void *) py_customidamemo_t::s_on_view_mouse_moved); } } //------------------------------------------------------------------------- #define CHK_EVT(flag_needed) \ if ( self == NULL || !has_callback(flag_needed) ) \ return; \ PYW_GIL_CHECK_LOCKED_SCOPE() //------------------------------------------------------------------------- void py_customidamemo_t::on_view_activated() { CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_ACTIVATED, NULL)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_deactivated() { CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DEACTIVATED, NULL)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_keydown(int key, int state) { CHK_EVT(GRBASE_HAVE_KEYDOWN); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_KEYDOWN, "ii", key, state)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_popup() { CHK_EVT(GRBASE_HAVE_POPUP); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_POPUP, NULL)); } //------------------------------------------------------------------------- static PyObject *build_renderer_pos_swig_proxy(const view_mouse_event_t *event) { newref_t py_module(PyImport_ImportModule(S_IDA_KERNWIN_MODNAME)); ref_t py_result; if ( py_module != NULL ) { ref_t py_class = PyW_TryGetAttrString(py_module.o, "renderer_pos_info_t"); if ( py_class != NULL ) { ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_class.o, NULL)); if ( py_obj != NULL ) { newref_t py_node(PyInt_FromLong(event->renderer_pos.node)); PyObject_SetAttrString(py_obj.o, "node", py_node.o); newref_t py_cx(PyInt_FromLong(event->renderer_pos.cx)); PyObject_SetAttrString(py_obj.o, "cx", py_cx.o); newref_t py_cy(PyInt_FromLong(event->renderer_pos.cy)); PyObject_SetAttrString(py_obj.o, "cy", py_cy.o); newref_t py_sx(PyInt_FromLong(event->renderer_pos.sx)); PyObject_SetAttrString(py_obj.o, "sx", py_sx.o); py_result = py_obj; } } } if ( py_result != NULL ) { py_result.incref(); return py_result.o; } else { return NULL; } } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) { CHK_EVT(GRBASE_HAVE_VIEW_CLICK); if ( ovc_num_args < 0 ) ovc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_CLICK); if ( ovc_num_args == 6 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iiiiO", event->x, event->y, event->state, event->button, rpos)); } else if ( ovc_num_args == 5 ) { pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iiii", event->x, event->y, event->state, event->button)); } else { pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CLICK, "iii", event->x, event->y, event->state)); } } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) { CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK); if ( ovdc_num_args < 0 ) ovdc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_DBLCLICK); if ( ovdc_num_args == 5 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DBLCLICK, "iiiO", event->x, event->y, event->state, rpos)); } else { pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_DBLCLICK, "iii", event->x, event->y, event->state)); } } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_curpos() { CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); pycall_res_t result( PyObject_CallMethod( self.o, (char *)S_ON_VIEW_CURPOS, NULL)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_close() { CHK_EVT(GRBASE_HAVE_CLOSE); pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) { CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); pycall_res_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); } //------------------------------------------------------------------------- static ref_t build_current_graph_item_tuple(int *out_icode, const view_mouse_event_t *event) { const selection_item_t *item = event->location.item; ref_t tuple; if ( (event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY) && item != NULL ) { if ( item->is_node ) { *out_icode = 1; tuple = newref_t(Py_BuildValue("(i)", item->node)); } else { *out_icode = 2; tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst)); } } else { *out_icode = 0; tuple = newref_t(Py_BuildValue("()")); } return tuple; } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) { CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER); if ( ovmo_num_args < 0 ) ovmo_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_OVER); if ( event->rtype != TCCRT_GRAPH && event->rtype != TCCRT_PROXIMITY ) return; int icode; ref_t tuple = build_current_graph_item_tuple(&icode, event); if ( ovmo_num_args == 7 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_OVER, "iiiiOO", event->x, event->y, event->state, icode, tuple.o, rpos)); } else { pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_OVER, "iiiiO", event->x, event->y, event->state, icode, tuple.o)); } } //------------------------------------------------------------------------- void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event) { CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_MOVED); if ( ovmm_num_args < 0 ) ovmm_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_MOVED); int icode; ref_t tuple = build_current_graph_item_tuple(&icode, event); if ( ovmm_num_args == 7 ) { PyObject *rpos = build_renderer_pos_swig_proxy(event); pycall_res_t result(PyObject_CallMethod( self.o, (char *)S_ON_VIEW_MOUSE_MOVED, "iiiiOO", event->x, event->y, event->state, icode, tuple.o, rpos)); } } #undef CHK_EVT //------------------------------------------------------------------------- // //------------------------------------------------------------------------- //------------------------------------------------------------------------- // A set of tinfo_t & details objects that were created from IDAPython. // This is necessary in order to clear all the "type details" that are // associated, in the kernel, with the tinfo_t instances. // // Unfortunately the IDAPython plugin has to terminate _after_ the IDB is // closed, but the "type details" must be cleared _before_ the IDB is closed. static qvector py_tinfo_t_vec; static qvector py_ptr_type_data_t_vec; static qvector py_array_type_data_t_vec; static qvector py_func_type_data_t_vec; static qvector py_udt_type_data_t_vec; static void __clear(tinfo_t *inst) { inst->clear(); } static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); } static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); } static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); } static void __clear(udt_type_data_t *inst) { inst->clear(); } static void til_clear_python_tinfo_t_instances(void) { // Pre-emptive strike: clear all the python-exposed tinfo_t // (& related types) instances: if that were not done here, // ~tinfo_t() calls happening as part of the python shutdown // process will try and clear() their details. ..but the kernel's // til-related functions will already have deleted those details // at that point. // // NOTE: Don't clear() the arrays of pointers. All the python-exposed // instances will be deleted through the python shutdown/ref-decrementing // process anyway (which will cause til_deregister_..() calls), and the // entries will be properly pulled out of the vector when that happens. #define BATCH_CLEAR(Type) \ do \ { \ for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \ __clear(py_##Type##_vec[i]); \ } while ( false ) BATCH_CLEAR(tinfo_t); BATCH_CLEAR(ptr_type_data_t); BATCH_CLEAR(array_type_data_t); BATCH_CLEAR(func_type_data_t); BATCH_CLEAR(udt_type_data_t); #undef BATCH_CLEAR } #define DEF_REG_UNREG_REFCOUNTED(Type) \ void ida_export til_register_python_##Type##_instance(Type *inst) \ { \ /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \ /* to an object's tinfo_t property will end up trying to register it. */ \ py_##Type##_vec.add_unique(inst); \ } \ \ void ida_export til_deregister_python_##Type##_instance(Type *inst) \ { \ qvector::iterator found = py_##Type##_vec.find(inst); \ if ( found != py_##Type##_vec.end() ) \ { \ __clear(inst); \ /* tif->clear();*/ \ py_##Type##_vec.erase(found); \ } \ } DEF_REG_UNREG_REFCOUNTED(tinfo_t); DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t); DEF_REG_UNREG_REFCOUNTED(array_type_data_t); DEF_REG_UNREG_REFCOUNTED(func_type_data_t); DEF_REG_UNREG_REFCOUNTED(udt_type_data_t); //------------------------------------------------------------------------- // //--------------------------------------------------------------------------- static qvector live_timers; py_timer_ctx_t *ida_export python_timer_new(PyObject *py_callback) { py_timer_ctx_t *t = new py_timer_ctx_t(); t->pycallback = py_callback; Py_INCREF(t->pycallback); live_timers.push_back(t); return t; } //------------------------------------------------------------------------- void ida_export python_timer_del(py_timer_ctx_t *t) { QASSERT(30491, live_timers.del(t)); Py_DECREF(t->pycallback); delete t; } //------------------------------------------------------------------------- static void clear_python_timer_instances(void) { // Pre-emptive strike: clear all the existing python-exposed // timers, or there's a chance IDA will try and execute some // python code after IDAPython is removed from memory. while ( !live_timers.empty() ) { py_timer_ctx_t *t = live_timers[0]; unregister_timer(t->timer_id); python_timer_del(t); } } #undef DEF_REG_UNREG_REFCOUNTED