mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
1403 lines
49 KiB
C++
1403 lines
49 KiB
C++
#ifndef __PYWRAPS_HPP__
|
|
#define __PYWRAPS_HPP__
|
|
|
|
#include <Python.h>
|
|
|
|
//------------------------------------------------------------------------
|
|
// 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<ref_t>
|
|
{
|
|
void to_pyobject_pointers(qvector<PyObject*> *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<tid_t> &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<tid_t> *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<ea64_t> 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 <idd.hpp>
|
|
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 <loader.hpp>
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 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_entry_t> lookup_entries_t;
|
|
|
|
#include <graph.hpp>
|
|
#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<pycim_callback_id_t>
|
|
{
|
|
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 <typeinf.hpp>
|
|
#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<wchar_t> 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<int,uchar> 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__
|