IDAPython 1.5.3

- IDA Pro 6.2 support
- added set_idc_func_ex(): it is now possible to add new IDC functions using Python
- added visit_patched_bytes() (see ex_patch.py)
- added support for the multiline text input control in the Form class
- added support for the editable/readonly dropdown list control in the Form class
- added execute_sync() to register a function call into the UI message queue
- added execute_ui_requests() / check ex_uirequests.py
- added add_hotkey() / del_hotkey() to bind Python methods to hotkeys
- added register_timer()/unregister_timer(). Check ex_timer.py
- added the IDC (Arrays) netnode manipulation layer into idc.py
- added idautils.Structs() and StructMembers() generator functions
- removed the "Run Python Statement" menu item. IDA now has a unified dialog. 
  Use RunPlugin("python", 0) to invoke it manually.
- better error messages for script plugins, loaders and processor modules
- bugfix: Dbg_Hooks.dbg_run_to() was receiving wrong input
- bugfix: A few Enum related functions were not properly working in idc.py
- bugfix: GetIdaDirectory() and GetProcessName() were broken in idc.py
- bugfix: idaapi.get_item_head() / idc.ItemHead() were not working
This commit is contained in:
skochinsky@gmail.com
2011-10-14 14:24:38 +00:00
parent 96cd02db6c
commit 1258fab948
36 changed files with 3455 additions and 658 deletions

View File

@@ -1,5 +1,5 @@
//---------------------------------------------------------------------
// IDAPython - Python plugin for Interactive Disassembler Pro
// IDAPython - Python plugin for Interactive Disassembler
//
// Copyright (c) The IDAPython Team <idapython@googlegroups.com>
//
@@ -42,7 +42,7 @@
// Python-style version tuple comes from the makefile
// Only the serial and status is set here
#define VER_SERIAL 3
#define VER_SERIAL 0
#define VER_STATUS "final"
#define IDAPYTHON_RUNSTATEMENT 0
#define IDAPYTHON_ENABLE_EXTLANG 3
@@ -75,11 +75,9 @@ enum script_run_when
//-------------------------------------------------------------------------
// Global variables
static bool g_initialized = false;
static bool g_menu_installed = false;
static int g_run_when = -1;
static char g_run_script[QMAXPATH];
static char g_idapython_dir[QMAXPATH];
static char g_runstmt_hotkey[30] = "Ctrl-F3";
//-------------------------------------------------------------------------
// Prototypes and forward declarations
@@ -223,123 +221,23 @@ int set_script_timeout(int timeout)
return timeout;
}
//-------------------------------------------------------------------------
// 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
static bool convert_args(
const idc_value_t args[],
int nargs,
ppyobject_vec_t &pargs,
boolvec_t *decref,
char *errbuf,
size_t errbufsize)
{
bool as_tupple = decref == NULL;
PyObject *py_tuple(NULL);
pargs.qclear();
if ( as_tupple )
{
py_tuple = PyTuple_New(nargs);
if ( py_tuple == NULL )
{
qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize);
return false;
}
// Add the tuple
pargs.push_back(py_tuple);
}
else
{
decref->qclear();
}
for ( int i=0; i<nargs; i++ )
{
PyObject *py_obj(NULL);
int cvt = idcvar_to_pyvar(args[i], &py_obj);
if ( cvt < CIP_OK )
{
qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype);
return false;
}
if ( as_tupple )
{
// Opaque object?
if ( cvt == CIP_OK_NODECREF )
{
// Increment reference for opaque objects.
// (A tupple will steal references of its set items,
// and for an opaque object we want it to still exist
// even if the tuple is gone)
Py_INCREF(py_obj);
}
// Save argument
PyTuple_SetItem(py_tuple, i, py_obj);
}
else
{
pargs.push_back(py_obj);
// do not decrement reference of opaque objects
decref->push_back(cvt == CIP_OK);
}
}
return true;
}
//-------------------------------------------------------------------------
// Frees arguments returned by convert_args()
static void free_args(
ppyobject_vec_t &pargs,
boolvec_t *decref = NULL)
{
if ( decref == NULL )
{
if ( !pargs.empty() )
Py_XDECREF(pargs[0]);
}
else
{
// free argument objects
for ( int i=(int)pargs.size()-1; i>=0; i-- )
{
if ( decref->at(i) )
Py_DECREF(pargs[i]);
}
decref->clear();
}
pargs.clear();
}
//------------------------------------------------------------------------
// Return a formatted error or just print it to the console
static void handle_python_error(char *errbuf, size_t errbufsize)
static void handle_python_error(
char *errbuf,
size_t errbufsize,
bool clear_error = true)
{
if ( errbufsize > 0 )
errbuf[0] = '\0';
// No exception?
if ( !PyErr_Occurred() )
return;
PyObject *result;
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
result = PyObject_Str(pvalue);
if ( result != NULL )
{
qsnprintf(errbuf, errbufsize, "ERROR: %s", PyString_AsString(result));
PyErr_Clear();
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
Py_DECREF(result);
}
else
{
PyErr_Print();
}
qstring s;
if ( PyW_GetError(&s, clear_error) )
qstrncpy(errbuf, s.c_str(), errbufsize);
}
//------------------------------------------------------------------------
@@ -391,23 +289,26 @@ static void PythonEvalOrExec(
}
//------------------------------------------------------------------------
// Simple Python statement runner function for IDC
static const char idc_runpythonstatement_args[] = { VT_STR2, 0 };
static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res)
// Executes a simple string
static bool idaapi IDAPython_extlang_run_statements(
const char *str,
char *errbuf,
size_t errbufsize)
{
PyObject *globals = GetMainGlobals();
bool ok;
if ( globals == NULL )
{
res->set_string("internal error");
ok = false;
}
else
{
errbuf[0] = '\0';
PyErr_Clear();
begin_execution();
PYW_GIL_ENSURE;
PyObject *result = PyRun_String(
argv[0].c_str(),
str,
Py_file_input,
globals,
globals);
@@ -415,21 +316,31 @@ static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res
Py_XDECREF(result);
end_execution();
if ( result == NULL || PyErr_Occurred() )
{
char errbuf[MAXSTR];
handle_python_error(errbuf, sizeof(errbuf));
if ( errbuf[0] == '\0' )
res->set_string("internal error");
else
res->set_string(errbuf);
}
else
{
// success
res->set_long(0);
}
ok = result != NULL && !PyErr_Occurred();
if ( !ok )
handle_python_error(errbuf, errbufsize);
}
if ( !ok && errbuf[0] == '\0' )
qstrncpy(errbuf, "internal error", errbufsize);
return ok;
}
//------------------------------------------------------------------------
// Simple Python statement runner function for IDC
static const char idc_runpythonstatement_args[] = { VT_STR2, 0 };
static error_t idaapi idc_runpythonstatement(
idc_value_t *argv,
idc_value_t *res)
{
char errbuf[MAXSTR];
bool ok = IDAPython_extlang_run_statements(argv[0].c_str(), errbuf, sizeof(errbuf));
if ( ok )
res->set_long(0);
else
res->set_string(errbuf);
return eOk;
}
@@ -441,15 +352,7 @@ const char *idaapi set_python_options(
{
do
{
if ( value_type == IDPOPT_STR )
{
if ( qstrcmp(keyword, "EXEC_STATEMENT_HOTKEY" ) == 0 )
{
qstrncpy(g_runstmt_hotkey, (const char *)value, sizeof(g_runstmt_hotkey));
break;
}
}
else if ( value_type == IDPOPT_NUM )
if ( value_type == IDPOPT_NUM )
{
if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 )
{
@@ -567,7 +470,7 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf
char script[MAXSTR];
qstrncpy(script, FileName, sizeof(script));
strrpl(script, '\\', '//');
strrpl(script, '\\', '/');
PyObject *py_script = PyString_FromString(script);
PYW_GIL_ENSURE;
@@ -754,7 +657,7 @@ bool idaapi IDAPython_extlang_run(
do
{
// Convert arguments to python
ok = convert_args(args, nargs, pargs, &decref, errbuf, errbufsize);
ok = pyw_convert_idc_args(args, nargs, pargs, &decref, errbuf, errbufsize);
if ( !ok )
break;
@@ -792,7 +695,7 @@ bool idaapi IDAPython_extlang_run(
ok = return_python_result(result, pres, errbuf, errbufsize);
} while ( false );
free_args(pargs, &decref);
pyw_free_idc_args(pargs, &decref);
if ( imported_module )
Py_XDECREF(module);
@@ -851,7 +754,7 @@ bool idaapi IDAPython_extlang_create_object(
}
// Error during conversion?
ok = convert_args(args, nargs, pargs, NULL, errbuf, errbufsize);
ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize);
if ( !ok )
break;
ok = false;
@@ -867,7 +770,7 @@ bool idaapi IDAPython_extlang_create_object(
Py_XDECREF(py_cls);
// Free the arguments tuple
free_args(pargs);
pyw_free_idc_args(pargs);
return ok;
}
@@ -918,9 +821,41 @@ bool idaapi IDAPython_extlang_get_attr(
// No object specified:
else
{
// then work with main module
// ...then work with main module
py_obj = py_mod;
}
// Special case: if attribute not passed then retrieve the class
// name associated associated with the passed object
if ( attr == NULL || attr[0] == '\0' )
{
cvt = CIP_FAILED;
// Get the class
PyObject *cls = PyObject_GetAttrString(py_obj, "__class__");
if ( cls == NULL )
break;
// Get its name
PyObject *name = PyObject_GetAttrString(cls, "__name__");
Py_DECREF(cls);
if ( name == NULL )
break;
// Convert name object to string object
PyObject *string = PyObject_Str(name);
Py_DECREF(name);
if ( string == NULL )
break;
// Convert name python string to a C string
const char *clsname = PyString_AsString(name);
if ( clsname == NULL )
break;
result->set_string(clsname);
cvt = CIP_OK;
break;
}
PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr);
// No attribute?
if ( py_attr == NULL )
@@ -1089,7 +1024,7 @@ bool idaapi IDAPython_extlang_call_method(
}
// Convert arguments to python objects
ok = convert_args(args, nargs, pargs, NULL, errbuf, errbufsize);
ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize);
if ( !ok )
break;
@@ -1100,7 +1035,7 @@ bool idaapi IDAPython_extlang_call_method(
} while ( false );
// Free converted args
free_args(pargs);
pyw_free_idc_args(pargs);
// Release reference of object if needed
if ( obj_cvt != CIP_OK_NODECREF )
@@ -1124,7 +1059,8 @@ extlang_t extlang_python =
IDAPython_extlang_create_object,
IDAPython_extlang_get_attr,
IDAPython_extlang_set_attr,
IDAPython_extlang_call_method
IDAPython_extlang_call_method,
IDAPython_extlang_run_statements,
};
//-------------------------------------------------------------------------
@@ -1247,26 +1183,6 @@ void py_print_banner()
PYW_GIL_RELEASE;
}
//-------------------------------------------------------------------------
// Install python menu items
static void install_python_menus()
{
if ( g_menu_installed )
return;
// Add menu items for all the functions
// Note: Different paths are used for the GUI version
add_menu_item(
"File/IDC command...",
"P~y~thon command...",
g_runstmt_hotkey,
SETMENU_APP,
IDAPython_Menu_Callback,
(void *)IDAPYTHON_RUNSTATEMENT);
g_menu_installed = true;
}
//------------------------------------------------------------------------
// Parse plugin options
void parse_plugin_options()
@@ -1331,7 +1247,6 @@ static int idaapi menu_installer_cb(void *, int code, va_list)
case ui_ready_to_run:
g_ui_ready = true;
py_print_banner();
install_python_menus();
if ( g_run_when == run_on_ui_ready )
RunScript(g_run_script);
@@ -1429,7 +1344,8 @@ bool IDAPython_Init(void)
init_idaapi();
// Set IDAPYTHON_VERSION in Python
qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n"
qsnprintf(tmp, sizeof(tmp),
"IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n"
"IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n",
VER_MAJOR,
VER_MINOR,
@@ -1447,8 +1363,17 @@ bool IDAPython_Init(void)
qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL);
if ( !PyRunFile(tmp) )
{
handle_python_error(tmp, sizeof(tmp));
warning("IDAPython: error executing " S_INIT_PY ":\n%s", tmp);
// Try to fetch a one line error string. We must do it before printing
// the traceback information. Make sure that the exception is not cleared
handle_python_error(tmp, sizeof(tmp), false);
// Print the exception traceback
PyRun_SimpleString("import traceback;traceback.print_exc();");
warning("IDAPython: error executing " S_INIT_PY ":\n"
"%s\n"
"\n"
"Refer to the message window to see the full error log.", tmp);
return false;
}
@@ -1497,7 +1422,6 @@ void IDAPython_Term(void)
#endif
/* Remove the menu items before termination */
del_menu_item("File/Python command...");
g_menu_installed = false;
// Notify about IDA closing
pywraps_nw_notify(NW_TERMIDA_SLOT);