hexrays_sample3.cpp
/*
* Hex-Rays Decompiler project
* Copyright (c) 2007-2023 by Hex-Rays, support@hex-rays.com
* ALL RIGHTS RESERVED.
*
* Sample plugin for Hex-Rays Decompiler.
* It introduces a new command for the user: invert if-statement
* For example, a statement like
*
* if ( cond )
* {
* statements1;
* }
* else
* {
* statements2;
* }
*
* will be displayed as
*
* if ( !cond )
* {
* statements2;
* }
* else
* {
* statements1;
* }
*
* Please note that the plugin cannot directly modify the current ctree.
* If the ctree is recreated, the changes will be lost.
* To make them persistent, we need to save information about the inverted
* if statements in the database and automatically reapply them
* for each new build. This approach makes all modifications
* persistent. The user can quit IDA and restart the session:
* his changes will be intact.
*
*/
#include <hexrays.hpp>
// The node to keep inverted-if information.
static const char nodename[] = "$ hexrays inverted-if";
//-------------------------------------------------------------------------
struct vds3_t;
struct invert_if_ah_t : public action_handler_t
{
vds3_t *plugmod;
invert_if_ah_t(vds3_t *_plugmod) : plugmod(_plugmod) {}
virtual int idaapi activate(action_activation_ctx_t *ctx) override;
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override;
};
//-------------------------------------------------------------------------
struct vds3_t : public plugmod_t
{
invert_if_ah_t invert_ah;
eavec_t inverted_ifs; // Cached copy of inverted if-statement addresses
vds3_t();
virtual ~vds3_t();
virtual bool idaapi run(size_t) override { return false; }
void add_inverted_if(ea_t ea);
};
#define ACTION_NAME "sample3:invertif"
//--------------------------------------------------------------------------
// This callback handles various hexrays events.
static ssize_t idaapi callback(void *ud, hexrays_event_t event, va_list va)
{
vds3_t *plugmod = (vds3_t *) ud;
switch ( event )
{
case hxe_populating_popup:
{ // If the current item is an if-statement, then add the menu item
TPopupMenu *popup = va_arg(va, TPopupMenu *);
if ( plugmod->find_if_statement(vu) != nullptr )
attach_action_to_popup(widget, popup, ACTION_NAME);
}
break;
case hxe_maturity:
if ( !plugmod->inverted_ifs.empty() )
{ // If the ctree is ready, invert marked ifs
ctree_maturity_t new_maturity = va_argi(va, ctree_maturity_t);
plugmod->convert_marked_ifs(cfunc);
}
break;
default:
break;
}
return 0;
}
//-------------------------------------------------------------------------
// vds3_t
//-------------------------------------------------------------------------
vds3_t::vds3_t()
: invert_ah(this)
{
netnode node;
if ( !node.create(nodename) ) // create failed -> node existed
{
size_t n;
void *blob = node.getblob(nullptr, &n, 0, 'I');
if ( blob != nullptr )
{
inverted_ifs.inject((ea_t *)blob, n / sizeof(ea_t));
for ( int i=0; i < inverted_ifs.size(); i++ )
inverted_ifs[i] = node2ea(inverted_ifs[i]);
}
}
register_action(ACTION_DESC_LITERAL_PLUGMOD(
ACTION_NAME,
"Invert then/else",
&invert_ah,
this,
nullptr,
nullptr,
-1));
msg("Hex-rays version %s has been detected, %s ready to use\n",
PLUGIN.wanted_name);
}
//-------------------------------------------------------------------------
vds3_t::~vds3_t()
{
}
//--------------------------------------------------------------------------
// The user has selected to invert the if statement. Update ctree
// and refresh the view.
{
// create an inverted condition and swap it with the if-condition
notcond->swap(cif.expr);
delete notcond;
// swap if branches
}
//--------------------------------------------------------------------------
void vds3_t::add_inverted_if(ea_t ea)
{
eavec_t::iterator p = inverted_ifs.find(ea);
if ( p != inverted_ifs.end() ) // already present?
inverted_ifs.erase(p); // delete the mark
else
inverted_ifs.push_back(ea); // remember if-statement address
// immediately save data into the database
eavec_t copy = inverted_ifs;
for ( int i=0; i < copy.size(); i++ )
copy[i] = ea2node(copy[i]);
netnode node(nodename);
node.setblob(copy.begin(), copy.size()*sizeof(ea_t), 0, 'I');
}
//--------------------------------------------------------------------------
// Check if the item under the cursor is 'if' or 'else' keyword
// If yes, return pointer to the corresponding ctree item
{
// 'if' keyword: straightforward check
{
// we can handle only if-then-else statements, so check that the 'else'
// clause exists
return i;
}
// check for 'else' line. The else lines do not correspond
// to any ctree item. That's why we have to check for them separately.
// we could extract the corresponding text line but this would be a bad approach
// a line with single 'else' would not give us enough information to locate
// the corresponding 'if'. That's why we use the line tail marks.
// All 'else' line will have the ITP_ELSE mark
{
// for tail marks, we know only the corresponding ea,
// not the pointer to if-statement
// find it by walking the whole ctree
{
ea_t ea;
cinsn_t *found;
if_finder_t(ea_t e)
{
{
found = i;
return 1; // stop enumeration
}
return 0;
}
};
return iff.found;
}
return nullptr;
}
//--------------------------------------------------------------------------
void vds3_t::convert_marked_ifs(cfunc_t *cfunc)
{
// we walk the ctree and for each if-statement check if has to be inverted
{
vds3_t *self;
if_inverter_t(vds3_t *_self)
self(_self) {}
{
self->do_invert_if(i);
return 0; // continue enumeration
}
};
if_inverter_t ifi(this);
}
//-------------------------------------------------------------------------
// invert_if_ah_t
//-------------------------------------------------------------------------
int idaapi invert_if_ah_t::activate(action_activation_ctx_t *ctx)
{
cinsn_t *i = plugmod->find_if_statement(vu);
plugmod->add_inverted_if(i->ea);
// we manually invert this if and recreate text.
// this is faster than rebuilding ctree from scratch.
plugmod->do_invert_if(i);
vu.refresh_ctext();
return 1;
}
//-------------------------------------------------------------------------
action_state_t idaapi invert_if_ah_t::update(action_update_ctx_t *ctx)
{
vdui_t *vu = get_widget_vdui(ctx->widget);
if ( vu == nullptr )
return AST_DISABLE_FOR_WIDGET;
return plugmod->find_if_statement(*vu) == nullptr ? AST_DISABLE : AST_ENABLE;
}
//--------------------------------------------------------------------------
// Initialize the plugin.
static plugmod_t *idaapi init()
{
}
//--------------------------------------------------------------------------
static char comment[] = "Sample3 plugin for Hex-Rays decompiler";
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_HIDE | PLUGIN_MULTI, // plugin flags
init, // initialize
nullptr,
nullptr,
comment, // long comment about the plugin
// it could appear in the status line
// or as a hint
"", // multiline help about the plugin
"Hex-Rays if-inverter", // the preferred short name of the plugin
"" // the preferred hotkey to run the plugin
};
HexRays SDK header file.
bool init_hexrays_plugin(int flags=0)
Check that your plugin is compatible with hex-rays decompiler.
Definition: hexrays.hpp:8548
bool install_hexrays_callback(hexrays_cb_t *callback, void *ud)
Install handler for decompiler events.
Definition: hexrays.hpp:12298
int remove_hexrays_callback(hexrays_cb_t *callback, void *ud)
Uninstall handler for decompiler events.
Definition: hexrays.hpp:12304
vdui_t * get_widget_vdui(TWidget *f)
Get the vdui_t instance associated to the TWidget.
Definition: hexrays.hpp:11482
#define CV_INSNS
visit only statements, prune all expressions do not use before the final ctree maturity because expre...
Definition: hexrays.hpp:5796
ctree_item_t tail
Tail ctree item on the current line (for indented comments)
Definition: hexrays.hpp:7607