Scripting with IDA Pro is very useful to automate tasks, write scripts or do batch analysis, nonetheless one problem is commonly faced by script writers: the lack of a certain function from the scripting language. In the blog post going to demonstrate how to extend both IDC and IDAPython to add new functions.
Every IDC variable is represented by an idc_value_t object in the SDK. The IDC variable type is stored in the idc_value->vtype field and have one of the following values:
Now that idc_value_t is covered, let us explain how to add a new IDC function in two steps:
The callback is defined as:
typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r);
The argv array is initialized by the IDC interpreter and contains the passed arguments and the r argument is set by the callback to return values to the IDC interpreter.
The IDC function callback can be registered with the IDC interpreter using this function:
bool set_idc_func(const char *name, idc_func_t *fp, const char *args);
Where:
When the plugin is about to unload (in its term() callback), it should unregister the IDC function by calling set_idc_func(name, NULL, NULL)
For the sake of demonstration, let us add getenv() to IDC:
sstatic const char idc_getenv_args[] = { VT_STR2, 0 }; static const char idc_getenv_name[] = "getenv"; static error_t idaapi idc_getenv(idc_value_t *argv, idc_value_t *res) { char *env = getenv(argv[0].c_str()); res->set_string(env == NULL ? "" : env); return eOk; } int idaapi init() { set_idc_func(idc_getenv_name, idc_getenv, idc_getenv_args ); return PLUGIN_KEEP; } void idaapi term(void) { // Unregister set_idc_func(idc_getenv_name, NULL, NULL); }
It is possible to extend IDAPython in two ways:
While the former method requires basic knowledge of SWIG, both methods require some understanding of the Python C API. In this article, we will use the second method because it is more practical and does not require modification to IDAPython itself. This process involes three steps:
If you’re new to the Python C API, you could still follow and understand the code used in this blog post without refering to this tutorial.
Let us wrap the following function:
// ints.hpp idaman ssize_t ida_export get_predef_insn_cmt( const insn_t &cmd, char *buf, size_t bufsize);
which is used to retrieve the predifined comment of a given instruction. We want to expose it as the following Python function:
def get_predef_insn_cmt(ea): """Return instruction comments @param ea: Instruction ea @return: None on failure or a string containing the instruction comment """ pass
Here is the callback:
static PyObject *py_getpredef_insn_cmt(PyObject * /*self*/, PyObject *args) { do { // Parse arguments unsigned long ea; if (!PyArg_ParseTuple(args, "k", &ea)) break; // Decode instruction if (decode_insn(get_screen_ea()) == 0) break; // Get comments char buf[MAXSTR], *p = buf; p += qsnprintf(buf, sizeof(buf), "%s: ", ph.instruc[cmd.itype].name); if (get_predef_insn_cmt(cmd, p, sizeof(buf) - (p - buf)) == -1 || *p == '\0') break; // Return comment as a string return PyString_FromString(buf); } while (false); Py_RETURN_NONE; }
Unlike IDC callback registration, Python C API allows you to register more than a function at once. This is done by describing all the callbacks in a PyMethodDef array:
static PyMethodDef py_methods[] = { {"getpredef_insn_cmt", py_getpredef_insn_cmt, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} };
and then calling the registration function:
Py_InitModule("idapyext", py_methods);
Finally, to use this function, make sure you “import idapyext” before calling getpredef_insn_cmt The source code used in the blog post can be downloaded from here
UPD 2014-04-24: please read http://www.hexblog.com/?p=788 for important changes in IDA v6.5