Files
idapython-src/pywraps/py_dbg.hpp
T
2019-10-15 15:05:24 +02:00

556 lines
14 KiB
C++

#ifndef __PYDBG__
#define __PYDBG__
//<code(py_dbg)>
// 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;
}
//</code(py_dbg)>
//<inline(py_dbg)>
//-------------------------------------------------------------------------
/*
#<pydoc>
def get_manual_regions():
"""
Returns the manual memory regions
@return: list(start_ea, end_ea, name, sclass, sbase, bitness, perm)
"""
pass
#</pydoc>
*/
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);
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def dbg_is_loaded():
"""
Checks if a debugger is loaded
@return: Boolean
"""
pass
#</pydoc>
*/
static bool dbg_is_loaded()
{
return dbg != NULL;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def refresh_debugger_memory():
"""
Refreshes the debugger memory
@return: Nothing
"""
pass
#</pydoc>
*/
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
}
//------------------------------------------------------------------------
/*
#<pydoc>
def py_list_bptgrps():
"""
Returns list of breakpoint group names
@return: A list of strings or None on failure
"""
pass
#</pydoc>
*/
static PyObject *py_list_bptgrps()
{
PYW_GIL_CHECK_LOCKED_SCOPE();
qstrvec_t args;
if ( list_bptgrps(&args) == 0 )
Py_RETURN_NONE;
return qstrvec2pylist(args);
}
//------------------------------------------------------------------------
/*
#<pydoc>
def move_bpt_to_grp():
"""
Sets new group for the breakpoint
"""
pass
#</pydoc>
*/
static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
set_bpt_group(*bpt, grp_name);
}
/*
#<pydoc>
def internal_get_sreg_base():
"""
Get the sreg base, for the given thread.
@return: The sreg base, or BADADDR on failure.
"""
pass
#</pydoc>
*/
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);
}
/*
#<pydoc>
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
#</pydoc>
*/
//-------------------------------------------------------------------------
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);
}
//</inline(py_dbg)>
#endif