Below is the full source code of a sample plugin. It performs a quite useful
transformation of the pseudocode: replaces zeroes in pointer contexts with NULLs.
A NULL immediately conveys the idea that the current expression is pointer-related.
This is especially useful for unknown function arguments.
The plugin is fully automatic. It hooks to the decompiler events and waits for the
pseudocode to be ready. At that moment it takes control and modifies the ctree.
The conversion is performed by the convert_zeroes() function. It visits all
expressions of the ctree and checks for pointer contexts. If a expression has
a pointer type, then the make_null_if_zero() function is called for it.
This function checks if the expression is a zero constant and converts it if necessary.
The plugin can be turned on or off by its menu item in the Plugins submenu.
The code is short and straightforward. Use it as a template for your plugins.
#include <hexrays.hpp>
hexdsp_t *hexdsp = NULL;
static bool inited = false;
static const char nodename[] = “$ hexrays NULLs”;
static const char null_type[] = “MACRO_NULL”;static bool is_enabled(void)
{
netnode n(nodename); return n.altval(0) == 0;}static void make_null_if_zero(cexpr_t *e)
{
if ( e->is_zero_const() && !e->type.is_ptr() )
{ number_format_t &nf = e->n->nf;
nf.flags = enum_flag();
nf.serial = 0;
nf.type_name = null_type;
e->type = tinfo_t::get_stock(STI_PVOID);
}
}static void convert_zeroes(cfunc_t *cfunc)
{ if ( !get_named_type(NULL, null_type, NTF_TYPE) )
{
msg(“%s type is missing, cannot convert zeroes to NULLs\n”, null_type);
return;
} struct ida_local zero_converter_t : public ctree_visitor_t
{
zero_converter_t(void) : ctree_visitor_t(CV_FAST) {}
int idaapi visit_expr(cexpr_t *e)
{ switch ( e->op )
{
case cot_asg: if ( e->x->type.is_ptr() )
make_null_if_zero(e->y);
break;
case cot_call: {
carglist_t &args = *e->a;
for ( int i=0; i < args.size(); i++ ) {
carg_t &a = args[i];
if ( a.formal_type.is_ptr_or_array() )
make_null_if_zero(&a);
}
}
break;
case cot_eq: case cot_ne:
case cot_sge:
case cot_uge:
case cot_sle:
case cot_ule:
case cot_sgt:
case cot_ugt:
case cot_slt:
case cot_ult: if ( e->y->type.is_ptr() )
make_null_if_zero(e->x);
if ( e->x->type.is_ptr() )
make_null_if_zero(e->y);
break;
default:
break;
}
return 0; }
};
zero_converter_t zc; zc.apply_to(&cfunc->body, NULL);
}static ssize_t idaapi callback(void *, hexrays_event_t event, va_list va)
{
if ( event == hxe_maturity )
{
cfunc_t *cfunc = va_arg(va, cfunc_t*);
ctree_maturity_t mat = va_argi(va, ctree_maturity_t);
if ( mat == CMAT_FINAL ) convert_zeroes(cfunc);
}
return 0;
}int idaapi init(void)
{
if ( !init_hexrays_plugin() )
return PLUGIN_SKIP; if ( is_enabled() ) {
install_hexrays_callback(callback, NULL);
const char *hxver = get_hexrays_version();
msg(“Hex-rays version %s has been detected, %s ready to use\n”, hxver, PLUGIN.wanted_name);
}
inited = true;
return PLUGIN_KEEP;
}void idaapi term(void)
{
if ( inited )
{ remove_hexrays_callback(callback, NULL);
term_hexrays_plugin();
}
}bool idaapi run(size_t)
{ int code = askbuttons(
“~E~nable”,
“~D~isable”,
“~C~lose”,
–1,
“AUTOHIDE NONE\n”
“Sample plugin for Hex-Rays decompiler.\n”
“\n”
“This plugin is fully automatic.\n”
“It detects zeroes in pointer contexts and converts them into NULLs.\n”
“\n”
“The current state of the plugin is: %s\n”,
is_enabled() ? “ENABLED” : “DISABLED”);
switch ( code )
{
case –1: break;
case 0: case 1: netnode n;
n.create(nodename);
n.altset(0, code == 0);
if ( code )
install_hexrays_callback(callback, NULL);
else
remove_hexrays_callback(callback, NULL);
info(“The %s plugin has been %s.”, PLUGIN.wanted_name, code ? “ENABLED” : “DISABLED”);
break;
}
return true;
}static char comment[] = “Sample2 plugin for Hex-Rays decompiler”;plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
0, init, term, run, comment, “”, “Hex-Rays NULL converter”, “”};