State-of-the-art binary code analysis tools
* Hex-Rays Decompiler project
* Copyright (c) 2007-2021 by Hex-Rays, [email protected]
* Sample plugin for Hex-Rays Decompiler.
* It shows how to specify a register value at a desired location.
* Such a functionality may be useful when the code to decompile is
* obfuscated and uses opaque predicates.
* The basic idea of this plugin is very simple: we add assertions like
* mov #VALUE, reg
* at the specified addresses in the microcode. The decompiler will use this
* info during the microcode optimization phase. However, the assertion
* will not appear in the output listing.
* Usage: use Edit, Plugins, Specify register value.
#include <hexrays.hpp>
struct fixed_regval_info_t
ea_t ea; // address in the decompiled function
mreg_t reg; // register number
int nbytes; // size of the register in bytes
uint64 value; // user-specified value
fixed_regval_info_t() : ea(BADADDR), reg(mr_none), nbytes(0), value(0) {}
typedef qvector<fixed_regval_info_t> fixed_regvals_t;
struct plugin_ctx_t : public plugmod_t
// Info about the user specified register values. Since this plugin exists
// only for illustration purposes, we keep this info in the memory.
// Ideally this info should saved into idb.
fixed_regvals_t user_regvals;
install_hexrays_callback(hr_callback, this);
remove_hexrays_callback(hr_callback, this);
virtual bool idaapi run(size_t) override;
static ssize_t idaapi hr_callback(
void *ud,
hexrays_event_t event,
va_list va);
void insert_assertions(mba_t *mba) const;
// Code for making debugging easy
// Ensure that the debug helper functions are linked in.
// With them it is possible to print microinstructions like this:
// insn->dstr()
// operand->dstr()
// in your favorite debugger. Having these functions greatly
// simplifies debugging.
//lint -e{413} Likely use of null pointer
void refs_for_linker(void)
#ifdef _DEBUG
#define CALL_DSTR(type) ((type*)0)->dstr()
#undef CALL_DSTR
static minsn_t *create_mov(const fixed_regval_info_t &fri)
minsn_t *m = new minsn_t(fri.ea);
m->opcode = m_mov;
m->l.make_number(fri.value, fri.nbytes, fri.ea);
m->d.make_reg(fri.reg, fri.nbytes);
// declare this 'mov' as an assertion.
// assertions are deleted before generating ctree and don't
// appear in the output
// Just for debugging let us print the constructed assertion:
msg("Created insn: %s\n", m->dstr());
return m;
void plugin_ctx_t::insert_assertions(mba_t *mba) const
func_t *pfn = mba->get_curfunc();
if ( pfn == NULL )
return; // currently only functions are supported, not snippets
// filter out the addresses outside of the decompiled function
fixed_regvals_t regvals;
for ( const auto &rv : user_regvals )
if ( func_contains(pfn, rv.ea) )
if ( regvals.empty() )
return; // no addresses inside our function
struct ida_local assertion_inserter_t : public minsn_visitor_t
fixed_regvals_t &regvals;
virtual int idaapi visit_minsn(void) override
for ( size_t i=0; i < regvals.size(); i++ )
fixed_regval_info_t &fri = regvals[i];
if ( curins->ea == fri.ea )
// create "mov #value, reg"
minsn_t *m = create_mov(fri);
// insert it before the current instruction
blk->insert_into_block(m, curins->prev);
// remove this fixed regval from consideration
return regvals.empty(); // stop if regvals becomes empty
assertion_inserter_t(fixed_regvals_t &fr) : regvals(fr) {}
assertion_inserter_t ai(regvals);
// find the specified addresses in mba and insert assertions.
// note: if the address specified by the user has the 'nop' instruction, it
// won't be translated into mircocode. we may fail to add an assertion because
// of this. the user should not specify the address of a 'nop' instruction
// or the logic in visit_minsn() should be improved to handle the situation
// when the specified address is not present in the microcode.
// This will work if IDA_DUMPDIR envvar points to a directory
// it is a good idea to ensure that we did not break anything
// call the verifier for that
// This callback intercepts control as soon microcode is generated
// and adds necessary assertions to it. These assertions will inform
// the decompiler about the user-specifed register values.
ssize_t idaapi plugin_ctx_t::hr_callback(
void *ud,
hexrays_event_t event,
va_list va)
plugin_ctx_t &ctx = *(plugin_ctx_t *)ud;
if ( event == hxe_microcode )
mba_t *mba = va_arg(va, mba_t *);
return 0;
bool idaapi plugin_ctx_t::run(size_t)
// Currently the user can only add new regvals. Since the main goal of
// this plugin to illustrate how to modify the microcode, deleting or showing
// fixed regvals is left as an exercise to the reader.
static const char form[] =
"Specify known register value\n"
"<~A~ddress :$::16::>\n"
"<~V~alue :L::16::>\n"
static qstring regname;
static fixed_regval_info_t fri;
CASSERT(sizeof(fri.ea) == sizeof(ea_t));
CASSERT(sizeof(fri.value) == sizeof(uint64));
while ( ask_form(form, &fri.ea, &regname, &fri.value) )
reg_info_t ri;
if ( !parse_reg_name(&ri, regname.c_str()) )
warning("Sorry, bad register name: %s", regname.c_str());
fri.nbytes = ri.size;
fri.reg = reg2mreg(ri.reg);
if ( fri.reg == mr_none )
warning("Failed to convert to microregister: %s", regname.c_str());
continue; // apparently this register is not supported by the decompiler
bool found = false;
for ( auto &rv : user_regvals )
if ( rv.ea == fri.ea && rv.reg == fri.reg )
rv.nbytes = fri.nbytes;
rv.value = fri.value;
found = true;
if ( !found )
static const char fmt[] = "Register %s at %a is considered to be equal to 0x%" FMT_64 "X\n";
info(fmt, regname.c_str(), fri.ea, fri.value);
msg(fmt, regname.c_str(), fri.ea, fri.value);
return true;
return false;
static plugmod_t *idaapi init()
return nullptr; // no decompiler
const char *hxver = get_hexrays_version();
msg("Hex-rays version %s has been detected, %s ready to use\n",
hxver, PLUGIN.wanted_name);
return new plugin_ctx_t;
static const char comment[] = "Sample18 plugin for Hex-Rays decompiler";
plugin_t PLUGIN =
PLUGIN_MULTI, // The plugin can work with multiple idbs in parallel
init, // initialize
comment, // long comment about the plugin
nullptr, // multiline help about the plugin
"Specify register value", // the preferred short name of the plugin
nullptr, // the preferred hotkey to run the plugin