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.
\
/*
* Hex-Rays Decompiler project
* Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
* ALL RIGHTS RESERVED.
*
* Sample plugin for Hex-Rays Decompiler.
* It automatically replaces zeroes in pointer contexts with NULLs.
* For example, expression like
*
* funcptr = 0;
*
* will be displayed as
*
* funcptr = NULL;
*
* Due to highly dynamic nature of the decompier output, we must
* use the decompiler events to accomplish the task. The plugin will
* wait for the ctree structure to be ready in the memory and will
* replace zeroes in pointer contexts with NULLs.
*
*/
#include <hexrays.hpp>
// Hex-Rays API pointer
hexdsp_t *hexdsp = NULL;
static bool inited = false;
static const char nodename[] = “$ hexrays NULLs”;
static const char null_type[] = “MACRO_NULL”;
//————————————————————————–
// Is the plugin enabled?
// The user can disable it. The plugin will save the on/off switch in the
// current database.
static bool is_enabled(void)
{
netnode n(nodename); // use a netnode to save the state
return n.altval(0) == 0; // if the long value is positive, then disabled
}
//————————————————————————–
// If the expression is zero, convert it to NULL
static void make_null_if_zero(cexpr_t *e)
{
if ( e->is_zero_const() && !e->type.is_ptr() )
{ // this is plain zero, convert it
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);
}
}
//————————————————————————–
// Convert zeroes of the ctree to NULLs
static void convert_zeroes(cfunc_t *cfunc)
{
// To represent NULLs, we will use the MACRO_NULL enumeration
// Normally it is present in the loaded tils but let’s verify it
if ( !get_named_type(NULL, null_type, NTF_TYPE) )
{
msg(“%s type is missing, cannot convert zeroes to NULLs\n”, null_type);
return;
}
// We derive a helper class from ctree_visitor_t
// The ctree_visitor_t is a base class to derive
// ctree walker classes.
// You have to redefine some virtual functions
// to do the real job. Here we redefine visit_expr() since we want
// to examine and modify expressions.
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)
{
// verify if the current expression has pointer expressions
// we handle the following patterns:
// A. ptr = 0;
// B. func(0); where argument is a pointer
// C. ptr op 0 where op is a comparison
switch ( e->op )
{
case cot_asg: // A
if ( e->x->type.is_ptr() )
make_null_if_zero(e->y);
break;
case cot_call: // B
{
carglist_t &args = *e->a;
for ( int i=0; i < args.size(); i++ ) // check all arguments
{
carg_t &a = args[i];
if ( a.formal_type.is_ptr_or_array() )
make_null_if_zero(&a);
}
}
break;
case cot_eq: // C
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:
// check both sides for zeroes
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; // continue walking the tree
}
};