#ifndef __PY_IDA_BYTES_CUSTDATA__ #define __PY_IDA_BYTES_CUSTDATA__ // //------------------------------------------------------------------------- class py_custom_data_type_t; static qvector py_custom_data_types; //------------------------------------------------------------------------- class py_custom_data_type_t : public data_type_t { qstring dt_name, dt_menu_name, dt_hotkey, dt_asm_keyword; int dtid; // The data format id PyObject *py_self; // may create data? nullptr means always may static bool idaapi s_may_create_at( void *ud, // user-defined data ea_t ea, // address of the future item size_t nbytes) // size of the future item { py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; PYW_GIL_GET; newref_t py_result( PyObject_CallMethod( _this->py_self, (char *)S_MAY_CREATE_AT, PY_BV_EA PY_BV_SZ, bvea_t(ea), bvsz_t(nbytes))); PyW_ShowCbErr(S_MAY_CREATE_AT); return py_result && PyObject_IsTrue(py_result.o); } // !=nullptr means variable size datatype static asize_t idaapi s_calc_item_size( // This function is used to determine // size of the (possible) item at 'ea' void *ud, // user-defined data ea_t ea, // address of the item asize_t maxsize) // maximal size of the item { PYW_GIL_GET; // Returns: 0-no such item can be created/displayed // this callback is required only for varsize datatypes py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; newref_t py_result( PyObject_CallMethod( _this->py_self, (char *)S_CALC_ITEM_SIZE, PY_BV_EA PY_BV_ASIZE, bvea_t(ea), bvasize_t(maxsize))); if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || !py_result ) return 0; uint64 num = 0; PyW_GetNumber(py_result.o, &num); return asize_t(num); } public: py_custom_data_type_t( PyObject *py_dt, const char *name, asize_t value_size, const char *menu_name, const char *hotkey, const char *asm_keyword, int props) { memset(this, 0, sizeof(data_type_t)); cbsize = sizeof(data_type_t); dt_name = name; dt_menu_name = menu_name; dt_hotkey = hotkey; dt_asm_keyword = asm_keyword; this->name = dt_name.begin(); this->menu_name = dt_menu_name.begin(); this->hotkey = dt_hotkey.begin(); this->asm_keyword = dt_asm_keyword.begin(); this->value_size = value_size; this->props = props; dtid = -1; py_custom_data_types.add_unique(this); py_self = py_dt; } ~py_custom_data_type_t() { do_unregister(); py_custom_data_types.del(this); } int get_dtid() const { return dtid; } int do_register() { PYW_GIL_CHECK_LOCKED_SCOPE(); // Already registered? if ( dtid >= 0 ) return -1; ud = this; ref_t py_attr; // may_create_at py_attr = PyW_TryGetAttrString(py_self, S_MAY_CREATE_AT); if ( py_attr != nullptr && PyCallable_Check(py_attr.o) ) may_create_at = s_may_create_at; // calc_item_size py_attr = PyW_TryGetAttrString(py_self, S_CALC_ITEM_SIZE); if ( py_attr != nullptr && PyCallable_Check(py_attr.o) ) calc_item_size = s_calc_item_size; // Now try to register dtid = register_custom_data_type(this); if ( dtid >= 0 ) Py_INCREF(py_self); return dtid; } bool do_unregister() { PYW_GIL_CHECK_LOCKED_SCOPE(); bool ok = unregister_custom_data_type(dtid); if ( ok ) { // when this is called at IDAPython-shutdown-time, this will cause the // dtor to be called, which in turn will call this 'do_unregister' a // second time, but this is no problem since the dtid has already been // unregistered and thus we won't end up in this Py_XDECREF block. dtid = -1; // modify the object now, otherwise it may get deleted Py_XDECREF(py_self); } return ok; } }; //------------------------------------------------------------------------- py_custom_data_type_t *py_custom_data_type_cast(data_type_t *inst) { // The following code seems to not work with gcc. Compiler bug? Not sure, but no time ATM // py_custom_data_type_t *py_inst = (py_custom_data_type_t *) inst; // py_custom_data_types.has(py_inst) ? py_inst : nullptr; if ( py_custom_data_types.has((py_custom_data_type_t *) inst) ) return (py_custom_data_type_t *) inst; else return 0; } //------------------------------------------------------------------------- static int py_custom_data_type_t_get_id(data_type_t *_dt) { py_custom_data_type_t *dt = py_custom_data_type_cast(_dt); return dt != nullptr ? dt->get_dtid() : -1; } //------------------------------------------------------------------------- // data_format_t //------------------------------------------------------------------------- class py_custom_data_format_t; static qvector py_custom_data_formats; //------------------------------------------------------------------------- class py_custom_data_format_t : public data_format_t { private: int dfid; PyObject *py_self; qstring df_name, df_menu_name, df_hotkey; static bool idaapi s_print( // convert to colored string void *ud, // user-defined data qstring *out, // output buffer. may be nullptr const void *value, // value to print. may not be nullptr asize_t size, // size of value in bytes ea_t current_ea, // current address (BADADDR if unknown) int operand_num, // current operand number int dtid) // custom data type id { PYW_GIL_GET; // Build a string from the buffer newref_t py_value(PyBytes_FromStringAndSize( (const char *)value, Py_ssize_t(size))); if ( !py_value ) return false; py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; newref_t py_result(PyObject_CallMethod( _this->py_self, (char *)S_PRINTF, "O" PY_BV_EA "ii", py_value.o, bvea_t(current_ea), operand_num, dtid)); // Error while calling the function? if ( PyW_ShowCbErr(S_PRINTF) || !py_result ) return false; bool ok = false; if ( PyUnicode_Check(py_result.o) ) { if ( out != nullptr ) PyUnicode_as_qstring(out, py_result.o); ok = true; } return ok; } static bool idaapi s_scan( // convert from uncolored string void *ud, // user-defined data bytevec_t *value, // output buffer. may be nullptr const char *input, // input string. may not be nullptr ea_t current_ea, // current address (BADADDR if unknown) int operand_num, // current operand number (-1 if unknown) qstring *errstr) // buffer for error message { PYW_GIL_GET; py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; newref_t py_result( PyObject_CallMethod( _this->py_self, (char *)S_SCAN, "s" PY_BV_EA "i", input, bvea_t(current_ea), operand_num)); // Error while calling the function? if ( PyW_ShowCbErr(S_SCAN) || !py_result ) return false; bool ok = false; do { // We expect a tuple(bool, string|None) if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) break; borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); borref_t py_val(PyTuple_GetItem(py_result.o, 1)); // Get return code from Python ok = PyObject_IsTrue(py_bool.o) != 0; // We expect None or the value (depending on probe) if ( ok ) { // Probe-only? Then okay, no need to extract the 'value' if ( value == nullptr ) break; Py_ssize_t len; char *buf; if ( PyBytes_AsStringAndSize(py_val.o, &buf, &len) != -1 ) { value->qclear(); value->append(buf, len); } } // An error occurred? else { // Make sure the user returned (False, String) if ( py_bool.o != Py_False || !PyUnicode_Check(py_val.o) ) { *errstr = "Invalid return value returned from the Python callback!"; break; } // Get the error message PyUnicode_as_qstring(errstr, py_val.o); } } while ( false ); return ok; } static void idaapi s_analyze( // analyze custom data format occurrence void *ud, // user-defined data ea_t current_ea, // current address (BADADDR if unknown) int operand_num) // current operand number // this callback can be used to create // xrefs from the current item. // this callback may be missing. { PYW_GIL_GET; py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; newref_t py_result( PyObject_CallMethod( _this->py_self, (char *)S_ANALYZE, PY_BV_EA "i", bvea_t(current_ea), operand_num)); PyW_ShowCbErr(S_ANALYZE); } public: py_custom_data_format_t( PyObject *py_df, const char *name, asize_t value_size, const char *menu_name, int props, const char *hotkey, int32 text_width) { memset(this, 0, sizeof(data_format_t)); cbsize = sizeof(data_format_t); df_name = name; df_menu_name = menu_name; df_hotkey = hotkey; this->name = df_name.begin(); this->menu_name = df_menu_name.begin(); this->hotkey = df_hotkey.begin(); this->value_size = value_size; this->props = props; this->text_width = text_width; dfid = -1; py_custom_data_formats.add_unique(this); py_self = py_df; } ~py_custom_data_format_t() { do_unregister(); py_custom_data_formats.del(this); } int get_dfid() const { return dfid; } int do_register() { // Already registered? if ( dfid >= 0 ) return -1; ud = this; PYW_GIL_CHECK_LOCKED_SCOPE(); ref_t py_attr; // print cb py_attr = PyW_TryGetAttrString(py_self, S_PRINTF); if ( py_attr != nullptr && PyCallable_Check(py_attr.o) ) print = s_print; // scan cb py_attr = PyW_TryGetAttrString(py_self, S_SCAN); if ( py_attr != nullptr && PyCallable_Check(py_attr.o) ) scan = s_scan; // analyze cb py_attr = PyW_TryGetAttrString(py_self, S_ANALYZE); if ( py_attr != nullptr && PyCallable_Check(py_attr.o) ) analyze = s_analyze; // Now try to register dfid = register_custom_data_format(this); if ( dfid >= 0 ) Py_INCREF(py_self); return dfid; } bool do_unregister() { PYW_GIL_CHECK_LOCKED_SCOPE(); bool ok = unregister_custom_data_format(dfid); if ( ok ) { // see comment in py_custom_data_type_t::do_unregister() dfid = -1; Py_XDECREF(py_self); } return ok; } }; //------------------------------------------------------------------------- py_custom_data_format_t *py_custom_data_format_cast(data_format_t *inst) { if ( py_custom_data_formats.has((py_custom_data_format_t *) inst) ) return (py_custom_data_format_t *) inst; else return 0; } //------------------------------------------------------------------------- static int py_custom_data_format_t_get_id(data_format_t *_df) { py_custom_data_format_t *df = py_custom_data_format_cast(_df); return df != nullptr ? df->get_dfid() : -1; } //------------------------------------------------------------------------- static void clear_custom_data_types_and_formats() { PYW_GIL_GET; for ( ssize_t i=py_custom_data_types.size()-1; i >= 0; --i ) py_custom_data_types[i]->do_unregister(); for ( ssize_t i=py_custom_data_formats.size()-1; i >= 0; --i ) py_custom_data_formats[i]->do_unregister(); } // //------------------------------------------------------------------------ // //------------------------------------------------------------------------ // Given a py_data_type_t object, this function will register a datatype static int py_register_custom_data_type(PyObject *dt) { ref_t py_attr = PyW_TryGetAttrString(dt, "this"); if ( py_attr == nullptr ) return -1; py_custom_data_type_t *inst = nullptr; int cvt = SWIG_ConvertPtr(py_attr.o, (void **) &inst, SWIGTYPE_p_data_type_t, 0); if ( !SWIG_IsOK(cvt) || py_custom_data_type_cast(inst) == nullptr ) return -1; return inst->do_register(); } //------------------------------------------------------------------------ static bool py_unregister_custom_data_type(int dtid) { const data_type_t *_dt = get_custom_data_type(dtid); if ( _dt == nullptr ) return false; py_custom_data_type_t *dt = py_custom_data_type_cast((data_type_t *) _dt); bool ok = dt != nullptr; if ( ok ) ok = dt->do_unregister(); else ok = unregister_custom_data_type(dtid); // C API dt return ok; } //------------------------------------------------------------------------ static int py_register_custom_data_format(PyObject *df) { ref_t py_attr = PyW_TryGetAttrString(df, "this"); if ( py_attr == nullptr ) return -1; py_custom_data_format_t *inst = nullptr; int cvt = SWIG_ConvertPtr(py_attr.o, (void **) &inst, SWIGTYPE_p_data_format_t, 0); if ( !SWIG_IsOK(cvt) || py_custom_data_format_cast(inst) == nullptr ) return -1; return inst->do_register(); } //------------------------------------------------------------------------ static bool py_unregister_custom_data_format(int dfid) { const data_format_t *_df = get_custom_data_format(dfid); if ( _df == nullptr ) return false; py_custom_data_format_t *df = py_custom_data_format_cast((data_format_t *) _df); bool ok = df != nullptr; if ( ok ) ok = df->do_unregister(); else ok = unregister_custom_data_format(dfid); // C API df return ok; } // #endif // __PY_IDA_BYTES_CUSTDATA__