#ifndef __PYDBG__ #define __PYDBG__ // // hookgenDBG:methodsinfo_def //------------------------------------------------------------------------- struct _cvt_status_t { PyObject *def_err_class; const char *def_err_string; qstring err_string; PyObject *err_class; bool ok; _cvt_status_t(PyObject *_def_err_class, const char *_def_err_string) : def_err_class(_def_err_class), def_err_string(_def_err_string), err_class(NULL), ok(true) {} ~_cvt_status_t() { if ( !ok ) { if ( err_class == NULL ) { err_class = def_err_class; err_string = def_err_string; } PyErr_SetString(err_class, err_string.c_str()); } } qstring &failed(PyObject *_err_class) { QASSERT(30587, ok == false); err_class = _err_class; return err_string; } }; //------------------------------------------------------------------------- static bool _to_reg_val(regval_t **out, regval_t *buf, const char *name, PyObject *o) { if ( o == Py_None ) return false; int cvt = SWIG_ConvertPtr(o, (void **) out, SWIGTYPE_p_regval_t, 0); if ( SWIG_IsOK(cvt) && *out != NULL ) return true; register_info_t ri; if ( !get_dbg_reg_info(name, &ri) ) { // we couldn't find the register information. This might // mean that we are accessing another, sub register (e.g., // "eax" while the real register name is "rax".) Let's // assume the dtype is DWORD then ri.dtype = dt_dword; } struct ida_local cvt_t { static bool convert_int(regval_t *lout, PyObject *in, op_dtype_t dt) { uint64 u64 = 0; _cvt_status_t status(PyExc_TypeError, "Expected integer value"); size_t nbits = 0; switch ( dt ) { case dt_byte: nbits = 8; break; case dt_word: nbits = 16; break; default: case dt_dword: nbits = 32; break; case dt_qword: nbits = 64; break; } status.ok = PyW_GetNumber(in, &u64); if ( status.ok ) { if ( nbits < 64 ) { status.ok = u64 < (1ULL << nbits); if ( !status.ok ) status.failed(PyExc_ValueError).sprnt("Integer value too large to fit in %" FMT_Z " bits", nbits); } } if ( status.ok ) lout->set_int(u64); return status.ok; } static bool convert_float(regval_t *lout, PyObject *in, op_dtype_t) { eNE ene; _cvt_status_t status(PyExc_TypeError, "Expected float value"); double dbl = PyFloat_AsDouble(in); status.ok = PyErr_Occurred() == NULL; if ( status.ok ) status.ok = ieee_realcvt(&dbl, ene, 003 /*load double*/) == 0; if ( !status.ok ) status.failed(PyExc_ValueError).sprnt("Float conversion failed"); if ( status.ok ) lout->set_float(ene); return status.ok; } static bool convert_bytes(regval_t *lout, PyObject *in, op_dtype_t dt) { bytevec_t bytes; _cvt_status_t status(PyExc_TypeError, "Unexpected value"); size_t nbytes = 0; switch ( dt ) { case dt_byte16: nbytes = 16; break; case dt_byte32: nbytes = 32; break; case dt_byte64: nbytes = 64; break; default: break; } status.ok = nbytes > 0; Py_ssize_t got; if ( status.ok ) { status.ok = false; if ( IDAPyBytes_Check(in) ) { char *buf; status.ok = IDAPyBytes_AsMemAndSize(in, &buf, &got) >= 0 && got <= nbytes; if ( status.ok ) bytes.append((const uchar *) buf, got); else status.failed(PyExc_ValueError).sprnt( "List of bytes is too long; was expecting at most %d bytes", int(nbytes)); } else if ( IDAPyInt_Check(in) ) { uint64 u64 = 0; status.ok = PyW_GetNumber(in, &u64); if ( status.ok ) { got = sizeof(u64); bytes.resize(got, 0); memcpy(bytes.begin(), &u64, got); } else { if ( PyLong_CheckExact(in) ) goto TRY_RAW_LONG; } } else if ( PyLong_CheckExact(in) ) { TRY_RAW_LONG: // (possibly very long) int or long value. Apparently it's rather // safe to use _PyLong_AsByteArray (it's even present in 3.x) // https://stackoverflow.com/questions/18290507/python-extension-construct-and-inspect-large-integers-efficiently // /* _PyLong_AsByteArray: Convert the least-significant 8*n bits of long // v to a base-256 integer, stored in array bytes. Normally return 0, // return -1 on error. // If little_endian is 1/true, store the MSB at bytes[n-1] and the LSB at // bytes[0]; else (little_endian is 0/false) store the MSB at bytes[0] and // the LSB at bytes[n-1]. // If is_signed is 0/false, it's an error if v < 0; else (v >= 0) n bytes // are filled and there's nothing special about bit 0x80 of the MSB. // If is_signed is 1/true, bytes is filled with the 2's-complement // representation of v's value. Bit 0x80 of the MSB is the sign bit. // Error returns (-1): // + is_signed is 0 and v < 0. TypeError is set in this case, and bytes // isn't altered. // + n isn't big enough to hold the full mathematical value of v. For // example, if is_signed is 0 and there are more digits in the v than // fit in n; or if is_signed is 1, v < 0, and n is just 1 bit shy of // being large enough to hold a sign bit. OverflowError is set in this // case, but bytes holds the least-significant n bytes of the true value. // */ bytes.resize(nbytes, 0); status.ok = pylong_to_byte_array( &bytes, in, /*little_endian=*/ true, /*is_signed=*/ true) >= 0; if ( status.ok ) got = nbytes; else status.failed(PyExc_ValueError).sprnt( "Integer value is too large to fit in %d bytes", int(nbytes)); } } if ( status.ok ) { bytes.growfill(nbytes - got, 0); lout->set_bytes(bytes); } return status.ok; } }; bool ok = false; regval_t &rv = *buf; switch ( ri.dtype ) { case dt_byte: case dt_word: case dt_dword: case dt_qword: default: ok = cvt_t::convert_int(&rv, o, ri.dtype); break; case dt_float: case dt_tbyte: case dt_double: case dt_ldbl: ok = cvt_t::convert_float(&rv, o, ri.dtype); break; case dt_byte16: case dt_byte32: case dt_byte64: ok = cvt_t::convert_bytes(&rv, o, ri.dtype); break; } if ( ok ) *out = &rv; return ok; } //------------------------------------------------------------------------- static PyObject *_from_reg_val( const char *name, const regval_t &rv) { register_info_t ri; if ( !get_dbg_reg_info(name, &ri) ) // see _to_reg_val() ri.dtype = dt_dword; PyObject *res = NULL; _cvt_status_t status(PyExc_ValueError, "Conversion failed"); switch ( ri.dtype ) { default: if ( rv.ival <= uint64(LONG_MAX) ) res = PyInt_FromLong(long(rv.ival)); else res = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) rv.ival); break; case dt_float: case dt_tbyte: case dt_double: case dt_ldbl: { double dbl; status.ok = ieee_realcvt(&dbl, (uint16 *) rv.fval, 013 /*store double*/) == 0; if ( status.ok ) res = PyFloat_FromDouble(dbl); } break; case dt_byte16: case dt_byte32: case dt_byte64: { const bytevec_t &b = rv.bytes(); res = IDAPyBytes_FromMemAndSize((const char *) b.begin(), b.size()); } break; } return res; } // // //------------------------------------------------------------------------- /* # def get_manual_regions(): """ Returns the manual memory regions @return: list(start_ea, end_ea, name, sclass, sbase, bitness, perm) """ pass # */ static PyObject *py_get_manual_regions() { meminfo_vec_t ranges; SWIG_PYTHON_THREAD_BEGIN_ALLOW; get_manual_regions(&ranges); SWIG_PYTHON_THREAD_END_ALLOW; return meminfo_vec_t_to_py(ranges); } //------------------------------------------------------------------------- /* # def dbg_is_loaded(): """ Checks if a debugger is loaded @return: Boolean """ pass # */ static bool dbg_is_loaded() { return dbg != NULL; } //------------------------------------------------------------------------- /* # def refresh_debugger_memory(): """ Refreshes the debugger memory @return: Nothing """ pass # */ static PyObject *refresh_debugger_memory() { SWIG_PYTHON_THREAD_BEGIN_ALLOW; invalidate_dbgmem_config(); invalidate_dbgmem_contents(BADADDR, 0); // Ask the debugger to populate debug names if ( dbg != NULL ) dbg->suspended(true); // Invalidate the cache is_mapped(0); SWIG_PYTHON_THREAD_END_ALLOW; PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; } ssize_t idaapi DBG_Callback(void *ud, int notification_code, va_list va); struct DBG_Hooks : public hooks_base_t { // hookgenDBG:methodsinfo_decl DBG_Hooks(uint32 _flags=0) : hooks_base_t("ida_dbg.DBG_Hooks", DBG_Callback, HT_DBG, _flags) {} bool hook() { return hooks_base_t::hook(); } bool unhook() { return hooks_base_t::unhook(); } #ifdef TESTABLE_BUILD qstring dump_state() { return hooks_base_t::dump_state(mappings, mappings_size); } #endif // hookgenDBG:methods ssize_t dispatch(int code, va_list va) { ssize_t ret = 0; switch ( code ) { // hookgenDBG:notifications } return ret; } private: static ssize_t store_int(int rc, const debug_event_t *, int *warn) { *warn = rc; return 0; } static ssize_t store_int(int rc, thid_t, ea_t, int *warn) { *warn = rc; return 0; } }; //------------------------------------------------------------------------- ssize_t idaapi DBG_Callback(void *ud, int code, va_list va) { // hookgenDBG:safecall=DBG_Hooks } //------------------------------------------------------------------------ /* # def py_list_bptgrps(): """ Returns list of breakpoint group names @return: A list of strings or None on failure """ pass # */ static PyObject *py_list_bptgrps() { PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t args; if ( list_bptgrps(&args) == 0 ) Py_RETURN_NONE; return qstrvec2pylist(args); } //------------------------------------------------------------------------ /* # def move_bpt_to_grp(): """ Sets new group for the breakpoint """ pass # */ static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name) { PYW_GIL_CHECK_LOCKED_SCOPE(); set_bpt_group(*bpt, grp_name); } /* # def internal_get_sreg_base(): """ Get the sreg base, for the given thread. @return: The sreg base, or BADADDR on failure. """ pass # */ static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value) { PYW_GIL_CHECK_LOCKED_SCOPE(); ea_t answer; return internal_get_sreg_base(&answer, tid, sreg_value) <= DRC_NONE ? BADADDR : answer; } //------------------------------------------------------------------------- static ssize_t py_write_dbg_memory(ea_t ea, PyObject *py_buf, size_t size=size_t(-1)) { PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !dbg_can_query() || !IDAPyBytes_Check(py_buf) ) return -1; char *buf = NULL; Py_ssize_t sz; if ( IDAPyBytes_AsMemAndSize(py_buf, &buf, &sz) < 0 ) return -1; if ( size == size_t(-1) ) size = size_t(sz); return write_dbg_memory(ea, buf, size); } /* # def dbg_can_query(): """ This function can be used to check if the debugger can be queried: - debugger is loaded - process is suspended - process is not suspended but can take requests. In this case some requests like memory read/write, bpt management succeed and register querying will fail. Check if idaapi.get_process_state() < 0 to tell if the process is suspended @return: Boolean """ pass # */ //------------------------------------------------------------------------- static PyObject *py_set_reg_val(const char *regname, PyObject *o) { regval_t buf; regval_t *ptr; if ( !_to_reg_val(&ptr, &buf, regname, o) ) return NULL; SWIG_PYTHON_THREAD_BEGIN_ALLOW; bool ok = set_reg_val(regname, ptr); SWIG_PYTHON_THREAD_END_ALLOW; if ( !ok ) { PyErr_SetString(PyExc_Exception, "Failed to set register value"); return NULL; } Py_RETURN_TRUE; } //------------------------------------------------------------------------- static PyObject *py_set_reg_val(thid_t tid, int regidx, PyObject *o) { if ( dbg == NULL ) { PyErr_SetString(PyExc_Exception, "No debugger loaded"); return NULL; } if ( regidx < 0 || regidx >= dbg->nregs ) { qstring buf; buf.sprnt("Bad register index: %d", regidx); PyErr_SetString(PyExc_Exception, buf.c_str()); return NULL; } const register_info_t &ri = dbg->regs(regidx); regval_t buf; regval_t *ptr; if ( !_to_reg_val(&ptr, &buf, ri.name, o) ) return NULL; SWIG_PYTHON_THREAD_BEGIN_ALLOW; bool ok = set_reg_val(tid, regidx, ptr) > 0; SWIG_PYTHON_THREAD_END_ALLOW; return PyInt_FromLong(ok); } //------------------------------------------------------------------------- static PyObject *py_request_set_reg_val(const char *regname, PyObject *o) { regval_t buf; regval_t *ptr; if ( !_to_reg_val(&ptr, &buf, regname, o) ) return NULL; SWIG_PYTHON_THREAD_BEGIN_ALLOW; bool ok = request_set_reg_val(regname, ptr); SWIG_PYTHON_THREAD_END_ALLOW; if ( !ok ) { PyErr_SetString(PyExc_Exception, "Failed to request set register value"); return NULL; } Py_RETURN_TRUE; } //------------------------------------------------------------------------- static PyObject *py_get_reg_val(const char *regname) { regval_t buf; SWIG_PYTHON_THREAD_BEGIN_ALLOW; bool ok = get_reg_val(regname, &buf); SWIG_PYTHON_THREAD_END_ALLOW; if ( !ok ) { PyErr_SetString(PyExc_Exception, "Failed to retrieve register value"); return NULL; } return _from_reg_val(regname, buf); } // #endif