hexrays_sample14.cpp
/*
* Hex-Rays Decompiler project
* Copyright (c) 2007-2019 by Hex-Rays, [email protected]
* ALL RIGHTS RESERVED.
*
* Sample plugin for Hex-Rays Decompiler.
* It shows xrefs to the called function as the decompiler output.
* All calls are displayed with the call arguments.
* Usage: Shift-X or Jump, Jump to call xrefs...
*/
#include <hexrays.hpp>
// Hex-Rays API pointer
hexdsp_t *hexdsp = NULL;
struct xref_info_t
{
qstring text;
ea_t ea;
char xref_type;
};
DECLARE_TYPE_AS_MOVABLE(xref_info_t);
typedef qvector<xref_info_t> xrefvec_t;
//-------------------------------------------------------------------------
// go backwards until the beginning of the basic block
static ea_t find_bb_start(ea_t call_ea)
{
ea_t ea = call_ea;
while ( true )
{
flags_t F = get_flags(ea);
if ( !is_flow(F) || has_xref(F) )
break;
insn_t tmp;
ea_t prev = decode_prev_insn(&tmp, ea);
if ( prev == BADADDR )
break;
ea = prev;
}
return ea;
}
//-------------------------------------------------------------------------
static bool determine_decompilation_range(
ea_t call_ea,
const tinfo_t &tif)
{
func_t *pfn = get_func(call_ea);
if ( pfn != NULL && calc_func_size(pfn) <= 1024 )
{ // a small function, decompile it entirely
mbr->pfn = pfn;
return true;
}
ea_t minea = call_ea;
ea_t maxea = call_ea;
eavec_t addrs;
if ( !get_arg_addrs(&addrs, call_ea) )
{
apply_callee_tinfo(call_ea, tif);
if ( !get_arg_addrs(&addrs, call_ea) )
minea = find_bb_start(call_ea);
}
for ( size_t i=0; i < addrs.size(); i++ )
{
if ( minea > addrs[i] )
minea = addrs[i];
if ( maxea < addrs[i] )
maxea = addrs[i];
}
range_t &r = mbr->ranges.push_back();
r.start_ea = minea;
r.end_ea = get_item_end(maxea);
return true;
}
//-------------------------------------------------------------------------
// decompile the snippet
static bool generate_call_line(
qstring *out,
bool *canceled,
ea_t call_ea,
const tinfo_t &tif)
{
if ( !determine_decompilation_range(&mbr, call_ea, tif) )
return false;
cfuncptr_t func = decompile(mbr, &hf, DECOMP_NO_WAIT);
if ( func == NULL )
{
if ( hf.code == MERR_CANCELED )
*canceled = true;
return false;
}
citem_t *call = func->body.find_closest_addr(call_ea);
if ( call == NULL || call->ea != call_ea )
return false;
const strvec_t &sv = func->get_pseudocode();
int y;
if ( !func->find_item_coords(call, NULL, &y) )
return false;
*out = sv[y].line;
tag_remove(out);
// indentation does not convey much info, so remove the leading spaces
out->ltrim();
return true;
}
//-------------------------------------------------------------------------
struct xref_chooser_t : public chooser_t
{
protected:
ea_t func_ea;
const xrefvec_t &list;
static const int widths_[];
static const char *const header_[];
enum { ICON = 55 };
public:
xref_chooser_t(uint32 flags, ea_t func_ea, const xrefvec_t &list, const char *title);
ea_t choose_modal(ea_t xrefpos_ea);
virtual size_t idaapi get_count() const { return list.size(); }
virtual void idaapi get_row(
qstrvec_t *cols,
int *icon_,
chooser_item_attrs_t *attrs,
size_t n) const;
// calculate the location of the item,
// item_data is a pointer to a xref position
virtual ssize_t idaapi get_item_index(const void *item_data) const;
protected:
static const char *direction_str(ea_t ea, ea_t refea)
{
return ea > refea ? "Up" : ea < refea ? "Down" : "";
}
static void get_xrefed_name(qstring *buf, ea_t ref);
xrefvec_t::const_iterator find(const xrefpos_t &pos) const
{
xrefvec_t::const_iterator it = list.begin();
xrefvec_t::const_iterator end = list.end();
for ( ; it != end; ++it )
{
const xref_info_t &cur = *it;
if ( cur.ea == pos.ea && cur.xref_type == pos.type )
break;
}
return it;
}
};
//-------------------------------------------------------------------------
const int xref_chooser_t::widths_[] =
{
6, // Direction
1, // Type
15, // Address
50, // Text
};
const char *const xref_chooser_t::header_[] =
{
"Direction", // 0
"Type", // 1
"Address", // 2
"Text", // 3
};
//-------------------------------------------------------------------------
inline xref_chooser_t::xref_chooser_t(
uint32 flags_,
ea_t func_ea_,
const xrefvec_t &list_,
const char *title_)
: chooser_t(flags_,
qnumber(widths_), widths_, header_,
title_),
func_ea(func_ea_),
list(list_)
{
CASSERT(qnumber(widths_) == qnumber(header_));
icon = ICON;
deflt_col = 2;
}
//-------------------------------------------------------------------------
inline ea_t xref_chooser_t::choose_modal(ea_t xrefpos_ea)
{
if ( list.empty() )
{
warning("There are no %s", title);
return BADADDR;
}
xrefpos_t defpos;
get_xrefpos(&defpos, xrefpos_ea);
ssize_t n = ::choose(this, &defpos);
if ( n < 0 || n >= list.size() )
return BADADDR;
const xref_info_t &entry = list[n];
if ( n == 0 )
{
del_xrefpos(xrefpos_ea);
}
else
{
xrefpos_t xp(entry.ea, entry.xref_type);
set_xrefpos(xrefpos_ea, &xp);
}
return entry.ea;
}
//-------------------------------------------------------------------------
void idaapi xref_chooser_t::get_row(
qstrvec_t *cols_,
int *,
chooser_item_attrs_t *,
size_t n) const
{
const xref_info_t &entry = list[n];
qstrvec_t &cols = *cols_;
cols[0] = direction_str(func_ea, entry.ea);
cols[1].sprnt("%c", xrefchar(entry.xref_type));
get_xrefed_name(&cols[2], entry.ea);
cols[3] = entry.text;
}
//------------------------------------------------------------------------
ssize_t idaapi xref_chooser_t::get_item_index(const void *item_data) const
{
if ( list.empty() )
return NO_SELECTION;
// `item_data` is a pointer to a xref position
xrefpos_t item_pos = *(const xrefpos_t *)item_data;
if ( !item_pos.is_valid() )
return 0; // first item by default
xrefvec_t::const_iterator it = find(item_pos);
if ( it == list.end() )
return 0; // first item by default
return it - list.begin();
}
//-------------------------------------------------------------------------
void xref_chooser_t::get_xrefed_name(qstring *buf, ea_t ref)
{
int f2 = GNCN_NOCOLOR; //-V688
if ( !inf_show_xref_fncoff() )
f2 |= GNCN_NOFUNC;
if ( !inf_show_xref_seg() )
f2 |= GNCN_NOSEG;
get_nice_colored_name(buf, ref, f2);
}
//-------------------------------------------------------------------------
static bool jump_to_call(ea_t func_ea)
{
// Retrieve all xrefs to the function
xrefblk_t xb;
xrefvec_t list;
for ( bool ok = xb.first_to(func_ea, 0); ok; ok=xb.next_to() )
{
xref_info_t &entry = list.push_back();
entry.ea = xb.from;
entry.xref_type = xb.type;
}
// Generate decompiler output or disassembly output for each xref
tinfo_t tif;
if ( get_tinfo(&tif, func_ea) )
guess_tinfo(&tif, func_ea);
show_wait_box("Decompiling...");
bool canceled = false;
size_t n = list.size();
for ( size_t i=0; i < n; i++ )
{
xref_info_t &entry = list[i];
bool success = false;
if ( entry.xref_type >= fl_CF && !canceled )
{
replace_wait_box("Decompiling %a (%" FMT_Z "/%" FMT_Z ")...", entry.ea, i, n);
success = generate_call_line(&entry.text, &canceled, entry.ea, tif);
}
if ( !success )
generate_disasm_line(&entry.text, entry.ea, GENDSM_REMOVE_TAGS);
}
hide_wait_box();
// Display the xref chooser
qstring title;
get_short_name(&title, func_ea);
title.insert("xrefs to ");
xref_chooser_t xrefch(CH_MODAL | CH_KEEP, func_ea, list, title.c_str());
ea_t target = xrefch.choose_modal(func_ea);
if ( target == BADADDR )
return false;
// Jump to the seleected target
return jumpto(target);
}
//-------------------------------------------------------------------------
struct func_xrefs_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *ctx)
{
ea_t ea = ctx->cur_value;
flags_t F = get_flags(ea);
if ( !is_func(F) )
{ // we are not staying on a call.
// check if we stay on the entry point of a function
ea = ctx->cur_ea;
F = get_flags(ea);
if ( !is_func(F) )
ea = BADADDR;
}
if ( ea != BADADDR )
return jump_to_call(ea);
return process_ui_action("JumpOpXref"); // fallback to the built-in action
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx)
{
switch ( ctx->widget_type )
{
case BWN_EXPORTS: // exports
case BWN_IMPORTS: // imports
case BWN_NAMES: // names
case BWN_FUNCS: // functions
case BWN_DISASM: // disassembly views
case BWN_DUMP: // hex dumps
case BWN_PSEUDOCODE: // decompiler view
return AST_ENABLE_FOR_WIDGET;
}
return AST_DISABLE_FOR_WIDGET;
}
};
static func_xrefs_t func_xrefs_ah;
static const char ah_name[] = "vds14:call_xrefs";
static const action_desc_t xref_desc = ACTION_DESC_LITERAL(
ah_name,
"Jump to call x~r~efs",
&func_xrefs_ah,
"Shift-X",
NULL,
-1);
//--------------------------------------------------------------------------
int idaapi init(void)
{
return PLUGIN_SKIP; // 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);
register_action(xref_desc);
attach_action_to_menu("Jump/Jump to func", ah_name, SETMENU_APP);
return PLUGIN_KEEP;
}
//--------------------------------------------------------------------------
void idaapi term(void)
{
if ( hexdsp != NULL )
{
unregister_action(ah_name);
}
}
//--------------------------------------------------------------------------
bool idaapi run(size_t)
{
return false;
}
//--------------------------------------------------------------------------
static const char comment[] = "Sample14 plugin for Hex-Rays decompiler";
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_HIDE, // plugin flags
init, // initialize
term, // terminate. this pointer may be NULL.
run, // invoke plugin
comment, // long comment about the plugin
// it could appear in the status line
// or as a hint
"", // multiline help about the plugin
"Call xrefs", // the preferred short name of the plugin
// (not visible because of PLUGIN_HIDE)
"" // the preferred hotkey to run the plugin
};