C++ SDK examples

This page is currently a stub. We're working on additional examples tailored for beginners, so expect more samples soon.

The IDA SDK, which you can get from our Download Center in My Hex-Rays Portal, provides sample code in addition to necessary libraries and headers. These exemplary plugins, processor modules, or file loaders are designed to help you create your own plugins, modules, and more.

Where can I find all the examples?

The IDA SDK is shipped with plenty of samples (including, for example, sample processor modules or loaders) and exemplary plugins that you can find in the IDA SDK folder. To kickstart your journey with the IDA SDK, we encourage you to check out the included samples, compile them, and run them.

Sample plugins

The plugins folder contains sample plugins written in C++. Usually, the subfolders contains at minimum the cpp file(s) and a makefile.

Below, we present only a selection of samples to familiarize yourself with the possibilities of the C++ SDK. All of these samples and their corresponding makefiles can be found in your plugins folder inside the SDK directory.

hello

Simple hello word plugin ideal to get started with IDA SDK.

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  msg("Hello, world! (cpp)\n");
  return true;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_UNL            // Unload the plugin immediately after calling 'run'
  | PLUGIN_MULTI,       // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  nullptr,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Hello, world",       // the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};

vcsample

Sample plugin, ideal to get familiar with plugins structure.

/*
 *  This is a sample plugin module
 *
 *  It can be compiled by any of the supported compilers:
 *
 *      - Visual C++
 *      - GCC
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <expr.hpp>
#include <bytes.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//#define INSTALL_SAMPLE_CALLBACK
//#define HAS_USER_DEFINED_PREFIX

//--------------------------------------------------------------------------
//lint -e754 struct member not referenced
struct plugin_data_t : public plugmod_t, public event_listener_t
{
  ea_t old_ea = BADADDR;
  int old_lnnum = -1;
  virtual ssize_t idaapi on_event(ssize_t event_id, va_list) override;
  virtual bool idaapi run(size_t arg) override;

  idaapi ~plugin_data_t();
};

//--------------------------------------------------------------------------
// Example of a user-defined IDC function in C++

//#define DEFINE_IDC_FUNC
#ifdef DEFINE_IDC_FUNC
static error_t idaapi myfunc5(idc_value_t *argv, idc_value_t *res)
{
  msg("myfunc is called with arg0=%x and arg1=%s\n", argv[0].num, argv[1].c_str());
  res->num = 5;     // let's return 5
  return eOk;
}
static const char myfunc5_args[] = { VT_LONG, VT_STR, 0 };
static const ext_idcfunc_t myfunc5_desc =
{
  { "MyFunc5", myfunc5, myfunc5_args, nullptr, 0, 0 }
};
#endif // DEFINE_IDC_FUNC

//--------------------------------------------------------------------------
// This callback is called for UI notification events
ssize_t idaapi plugin_data_t::on_event(ssize_t event_id, va_list)
{
  if ( event_id != ui_msg     // avoid recursion
    && event_id != ui_refreshmarked ) // ignore uninteresting events
  {
    msg("ui_callback %" FMT_ZS "\n", event_id);
  }
  return 0; // 0 means "continue processing the event"
            // otherwise the event is considered as processed
}

//--------------------------------------------------------------------------
// A sample how to generate user-defined line prefixes
#ifdef HAS_USER_DEFINED_PREFIX
static const int prefix_width = 8;

struct sample_prefix_t : public user_defined_prefix_t
{
  plugin_data_t *pd;
  sample_prefix_t(plugin_data_t *d) :
    user_defined_prefix_t(prefix_width, d), pd(d) {}
  virtual void idaapi get_user_defined_prefix(
        qstring *out,
        ea_t ea,
        const insn_t & /*insn*/,
        int lnnum,
        int indent,
        const char *line) override
  {
    out->qclear();        // empty prefix by default

    // We want to display the prefix only the lines which
    // contain the instruction itself

    if ( indent != -1 )           // a directive
      return;

    if ( line[0] == '\0' )        // empty line
      return;

    if ( tag_advance(line,1)[-1] == ash.cmnt[0] ) // comment line...
      return;

    // We don't want the prefix to be printed again for other lines of the
    // same instruction/data. For that we remember the line number
    // and compare it before generating the prefix

    if ( pd->old_ea == ea && pd->old_lnnum == lnnum )
      return;

    // Ok, seems that we found an instruction line.

    // Let's display the size of the current item as the user-defined prefix
    asize_t our_size = get_item_size(ea);

    // We don't bother about the width of the prefix
    // because it will be padded with spaces by the kernel

    out->sprnt(" %" FMT_64 "d", our_size);

    // Remember the address and line number we produced the line prefix for:
    pd->old_ea = ea;
    pd->old_lnnum = lnnum;
  }
};
#endif // HAS_USER_DEFINED_PREFIX

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  if ( inf_get_filetype() == f_ELF )
    return nullptr; // we do not want to work with this idb

  plugin_data_t *pd = new plugin_data_t;

  // notifications
#ifdef INSTALL_SAMPLE_CALLBACK
  hook_event_listener(HT_UI, pd, pd);
#endif // INSTALL_SAMPLE_CALLBACK

  // user-defined prefix. will be automatically uninstalled by the kernel
  // when our plugin gets unloaded.
#ifdef HAS_USER_DEFINED_PREFIX
  new sample_prefix_t(pd);
#endif // HAS_USER_DEFINED_PREFIX

  // custom IDC function
#ifdef DEFINE_IDC_FUNC
  add_idc_func(myfunc5_desc);
#endif // DEFINE_IDC_FUNC

  // an example how to retrieve plugin options
  const char *options = get_plugin_options("vcsample");
  if ( options != nullptr )
    warning("command line options: %s", options);

  return pd;
}

//--------------------------------------------------------------------------
plugin_data_t::~plugin_data_t()
{
#ifdef DEFINE_IDC_FUNC
  del_idc_func(myfunc5_desc.name);
#endif
}

//--------------------------------------------------------------------------
bool idaapi plugin_data_t::run(size_t arg)
{
  warning("vcsample plugin has been called with arg %" FMT_Z, arg);
  // msg("just fyi: the current screen address is: %a\n", get_screen_ea());
  return true;
}

//--------------------------------------------------------------------------
static const char comment[] = "This is a sample plugin. It does nothing useful";

static const char help[] =
  "A sample plugin module\n"
  "\n"
  "This module shows you how to create plugin modules.\n"
  "\n"
  "It does nothing useful - just prints a message that is was called\n"
  "and shows the current address.\n";

//--------------------------------------------------------------------------
// This is the preferred name of the plugin module in the menu system
// The preferred name may be overridden in plugins.cfg file

static const char wanted_name[] = "Sample plugin";


// This is the preferred hotkey for the plugin module
// The preferred hotkey may be overridden in plugins.cfg file

static const char wanted_hotkey[] = "";


//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // plugin flags
  init,                 // initialize

  nullptr,              // terminate. this pointer may be nullptr.

  nullptr,              // invoke plugin

  comment,              // long comment about the plugin
                        // it could appear in the status line
                        // or as a hint

  help,                 // multiline help about the plugin

  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

calle

Sample plugin that allows the user to change the address of the called function.

/*
 *  Change the callee address for constructions like
 *
 *  call esi    ; LocalFree
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <bytes.hpp>
#include <auto.hpp>
#include <segregs.hpp>
#define T 20

struct callee_vars_t : public plugmod_t
{
  processor_t &ph;
  callee_vars_t(processor_t &_ph) : ph(_ph) {}
  virtual bool idaapi run(size_t arg) override;
};

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  processor_t &ph = PH;
  if ( ph.id != PLFM_386 && ph.id != PLFM_MIPS && ph.id != PLFM_ARM )
    return nullptr; // only for x86, MIPS and ARM
  return new callee_vars_t(ph);
}

//--------------------------------------------------------------------------
static const char comment[] = "Change the callee address";
static const char help[] =
  "This plugin allows the user to change the address of the called function\n"
  "in constructs like\n"
  "\n"
  "       call esi\n"
  "\n"
  "You can enter a function name instead of its address\n";

//--------------------------------------------------------------------------
static const char *const form =
  "HELP\n"
  "%s\n"
  "ENDHELP\n"
  "Enter the callee address\n"
  "\n"
  "  <~C~allee:$::40:::>\n"
  "\n"
  "\n";

bool idaapi callee_vars_t::run(size_t)
{
  const char *nname;
  if ( ph.id == PLFM_MIPS )
    nname = "$ mips";
  else if ( ph.id == PLFM_ARM )
    nname = " $arm";
  else
    nname = "$ vmm functions";
  netnode n(nname);
  ea_t ea = get_screen_ea();    // get current address
  if ( !is_code(get_flags(ea)) )
    return false; // not an instruction
  // get the callee address from the database
  ea_t callee = node2ea(n.altval_ea(ea)-1);
  // remove thumb bit for arm
  if ( ph.id == PLFM_ARM )
    callee &= ~1;
  char buf[MAXSTR];
  qsnprintf(buf, sizeof(buf), form, help);
  if ( ask_form(buf, &callee) )
  {
    if ( callee == BADADDR )
    {
      n.altdel_ea(ea);
    }
    else
    {
      if ( ph.id == PLFM_ARM && (callee & 1) == 0 )
      {
        // if we're calling a thumb function, set bit 0
        sel_t tbit = get_sreg(callee, T);
        if ( tbit != 0 && tbit != BADSEL )
          callee |= 1;
      }
      // save the new address
      n.altset_ea(ea, ea2node(callee)+1);
    }
    gen_idb_event(idb_event::callee_addr_changed, ea, callee);
    plan_ea(ea);                 // reanalyze the current instruction
  }
  return true;
}

//--------------------------------------------------------------------------
static const char wanted_name[] = "Change the callee address";
static const char wanted_hotkey[] = "Alt-F11";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // plugin flags
  init,                 // initialize

  nullptr,              // terminate. this pointer may be nullptr.
  nullptr,              // invoke plugin

  comment,              // long comment about the plugin
                        // it could appear in the status line
                        // or as a hint

  help,                 // multiline help about the plugin

  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

choose

Sample plugin module that demonstrates the use of the choose() function.

/*
 *  This is a sample plugin module
 *
 *  It demonstrates the use of the choose() function
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <bytes.hpp>
#include <kernwin.hpp>

struct plugin_ctx_t : public plugmod_t
{
  virtual bool idaapi run(size_t arg) override;
};

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//-------------------------------------------------------------------------
// non-modal call instruction chooser
struct calls_chooser_t : public chooser_t
{
protected:
  static const int widths_[];
  static const char *const header_[];

public:
  // remember the call instruction addresses in this qvector
  eavec_t list;

  // this object must be allocated using `new`
  calls_chooser_t(const char *title, bool ok, func_item_iterator_t *fii);

  // function that is used to decide whether a new chooser should be opened
  // or we can use the existing one.
  // The contents of the window are completely determined by its title
  virtual const void *get_obj_id(size_t *len) const override
  {
    *len = strlen(title);
    return title;
  }

  // function that returns number of lines in the list
  virtual size_t idaapi get_count() const override { return list.size(); }

  // function that generates the list line
  virtual void idaapi get_row(
        qstrvec_t *cols,
        int *icon_,
        chooser_item_attrs_t *attrs,
        size_t n) const override;

  // function that is called when the user hits Enter
  virtual cbret_t idaapi enter(size_t n) override
  {
    if ( n < list.size() )
      jumpto(list[n]);
    return cbret_t(); // nothing changed
  }

protected:
  void build_list(bool ok, func_item_iterator_t *fii)
  {
    insn_t insn;
    while ( ok )
    {
      ea_t ea = fii->current();
      if ( decode_insn(&insn, ea) > 0 && is_call_insn(insn) ) // a call instruction is found
        list.push_back(ea);
      ok = fii->next_code();
    }
  }
};

// column widths
const int calls_chooser_t::widths_[] =
{
  CHCOL_HEX | 8,  // Address
  32,             // Instruction
};
// column headers
const char *const calls_chooser_t::header_[] =
{
  "Address",      // 0
  "Instruction",  // 1
};

inline calls_chooser_t::calls_chooser_t(
        const char *title_,
        bool ok,
        func_item_iterator_t *fii)
  : chooser_t(0, qnumber(widths_), widths_, header_, title_),
    list()
{
  CASSERT(qnumber(widths_) == qnumber(header_));

  // build the list of calls
  build_list(ok, fii);
}

void idaapi calls_chooser_t::get_row(
        qstrvec_t *cols_,
        int *,
        chooser_item_attrs_t *,
        size_t n) const
{
  // assert: n < list.size()
  ea_t ea = list[n];

  // generate the line
  qstrvec_t &cols = *cols_;
  cols[0].sprnt("%08a", ea);
  generate_disasm_line(&cols[1], ea, GENDSM_REMOVE_TAGS);
  CASSERT(qnumber(header_) == 2);
}


//--------------------------------------------------------------------------
// The plugin method
// This is the main function of the plugin.
bool idaapi plugin_ctx_t::run(size_t)
{
  qstring title;
  // Let's display the functions called from the current one
  // or from the selected area

  // First we determine the working area
  func_item_iterator_t fii;
  bool ok;
  ea_t ea1, ea2;
  if ( read_range_selection(nullptr, &ea1, &ea2) ) // the selection is present?
  {
    callui(ui_unmarksel);                       // unmark selection
    title.sprnt("Functions called from %08a..%08a", ea1, ea2);
    ok = fii.set_range(ea1, ea2);
  }
  else                                          // nothing is selected
  {
    func_t *pfn = get_func(get_screen_ea());    // try the current function
    if ( pfn == nullptr )
    {
      warning("Please position the cursor on a function or select an area");
      return true;
    }
    ok = fii.set(pfn);
    get_func_name(&title, pfn->start_ea);
    title.insert("Functions called from ");
  }

  // now open the window
  calls_chooser_t *ch = new calls_chooser_t(title.c_str(), ok, &fii);
  ch->choose(); // the default cursor position is 0 (first row)
  return true; //-V773
}

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  // plugin flags
  PLUGIN_MULTI,
  // initialize
  init,
  nullptr,
  nullptr,
  // long comment about the plugin
  // it could appear in the status line
  // or as a hint
  "This is a sample plugin. It displays the chooser window",
  // multiline help about the plugin
  "A sample plugin module\n"
  "\n"
  "This module shows you how to use choose() function.\n",

  // the preferred short name of the plugin
  "Called functions",
  // the preferred hotkey to run the plugin
  ""
};

custdata

This sample plugin demonstates how to install a custom data type and a custom data format.

custview

This sample plugin demonstates how to create and manipulate a simple custom viewer, that allows you to create a view which displays colored lines.

cvt64_sample

Plugin with CVT64 examples.

dwarf

The source code of the dwarf plugin

ex_debidc

This sample Debugger IDC Helper executes IDC script when the process is launched and allows to hook IDC scripts to various debugger events.

// Debugger IDC Helper
// Executes IDC script when the process is launched
// In fact, this approach can be used to hook IDC scripts to various debugger
// events.

#include <ida.hpp>
#include <idp.hpp>
#include <dbg.hpp>
#include <expr.hpp>
#include <loader.hpp>

int data_id;

//--------------------------------------------------------------------------
// The plugin stores the IDC file name in the database
// It will create a node for this purpose
static const char node_name[] = "$ debugger idc file";


//--------------------------------------------------------------------------
struct plugin_ctx_t;

DECLARE_LISTENER(dbg_listener_t, plugin_ctx_t, ctx);
DECLARE_LISTENER(idp_listener_t, plugin_ctx_t, ctx);

struct plugin_ctx_t : public plugmod_t
{
  dbg_listener_t dbg_listener = dbg_listener_t(*this);
  idp_listener_t idp_listener = idp_listener_t(*this);
  plugin_ctx_t()
  {
    hook_event_listener(HT_DBG, &dbg_listener);
#ifdef ENABLE_MERGE
    hook_event_listener(HT_IDP, &idp_listener);
#endif
    set_module_data(&data_id, this);
  }
  ~plugin_ctx_t()
  {
    clr_module_data(data_id);
    // listeners are uninstalled automatically
    // when the owner module is unloaded
  }

  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
// Get the IDC file name from the database
static bool get_idc_name(char *buf, size_t bufsize)
{
  // access the node
  netnode mynode(node_name);
  // retrieve the value
  return mynode.valstr(buf, bufsize) > 0;
}

//--------------------------------------------------------------------------
// Store the IDC file name in the database
static void set_idc_name(const char *idc)
{
  // access the node
  netnode mynode;
  // if it doesn't exist yet, create it
  // otherwise get its id
  mynode.create(node_name);
  // store the value
  mynode.set(idc, strlen(idc)+1);
}

//--------------------------------------------------------------------------
ssize_t idaapi idp_listener_t::on_event(ssize_t code, va_list va)
{
  return 0;
}

//--------------------------------------------------------------------------
ssize_t idaapi dbg_listener_t::on_event(ssize_t code, va_list /*va*/)
{
  switch ( code )
  {
    case dbg_process_start:
    case dbg_process_attach:
      // it is time to run the script
      char idc[QMAXPATH];
      if ( get_idc_name(idc, sizeof(idc)) )
      {
        qstring errbuf;
        if ( !exec_idc_script(nullptr, idc, "main", nullptr, 0, &errbuf) )
          warning("%s", errbuf.c_str());
      }
      break;
  }
  return 0;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  // retrieve the old IDC name from the database
  char idc[QMAXPATH];
  if ( !get_idc_name(idc, sizeof(idc)) )
    qstrncpy(idc, "*.idc", sizeof(idc));

  char *newidc = ask_file(false, idc, "Specify the script to run upon debugger launch");
  if ( newidc != nullptr )
  {
    // store it back in the database
    set_idc_name(newidc);
    msg("Script %s will be run when the debugger is launched\n", newidc);
  }
  return true;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  // Our plugin works only for x86 PE executables
  processor_t &ph = PH;
  if ( ph.id != PLFM_386 || inf_get_filetype() != f_PE )
    return nullptr;
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
static const char wanted_name[] = "Specify Debugger IDC Script";
static const char wanted_hotkey[] = "";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  wanted_name,          // long comment about the plugin
  wanted_name,          // multiline help about the plugin
  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

ex_events1

Sample plugin illustrating analysis improvement; it checks branch targets for newly created instructions.

/*
        This is a sample plugin.

        It illustrates how the analysis can be improved

        The plugin checks branch targets for newly created instructions.
        If the target does not exist in the program, the plugin
        forbids the instruction creation.

*/

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <allins.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
  plugin_ctx_t()
  {
    hook_event_listener(HT_IDB, this);
  }
  ~plugin_ctx_t()
  {
    // listeners are uninstalled automatically
    // when the owner module is unloaded
  }

  virtual bool idaapi run(size_t) override;
  virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};

//--------------------------------------------------------------------------
// This callback is called by the kernel when database related events happen
ssize_t idaapi plugin_ctx_t::on_event(ssize_t event_id, va_list va)
{
  switch ( event_id )
  {
    case idb_event::make_code:  // An instruction is being created
                                // args: insn_t *
                                // returns: 1-ok, <=0-the kernel should stop
      insn_t *insn = va_arg(va, insn_t *);
      // we are interested in the branch instructions
      if ( insn->itype >= NN_ja && insn->itype <= NN_jmpshort )
      {
        // the first operand contains the jump target
        ea_t target = to_ea(insn->cs, insn->Op1.addr);
        if ( !is_mapped(target) )
          return -1;
      }
  }
  return 0; // event not processed
            // let other plugins handle it
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  // since the plugin is fully automatic, there is nothing to do
  warning("Branch checker is fully automatic");
  return true;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_HIDE           // Plugin should not appear in the Edit, Plugins menu
  | PLUGIN_MULTI,       // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  nullptr,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Branch checker",     // the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};

extlang

Sample plugin that illustrates how to register a thid party language interpreter.

/*
        This is a sample plugin. It illustrates

          how to register a thid party language interpreter

*/

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <expr.hpp>
#include <kernwin.hpp>

//--------------------------------------------------------------------------
static bool idaapi compile_expr(// Compile an expression
        const char *name,       // in: name of the function which will
                                //     hold the compiled expression
        ea_t current_ea,        // in: current address. if unknown then BADADDR
        const char *expr,       // in: expression to compile
        qstring *errbuf)        // out: error message if compilation fails
{                               // Returns: success
  qnotused(name);
  qnotused(current_ea);
  qnotused(expr);
  // our toy interpreter doesn't support separate compilation/evaluation
  // some entry fields in ida won't be useable (bpt conditions, for example)
  if ( errbuf != nullptr )
    *errbuf = "compilation error";
  return false;
}

//--------------------------------------------------------------------------
static bool idaapi call_func(   // Evaluate a previously compiled expression
        idc_value_t *result,    // out: function result
        const char *name,       // in: function to call
        const idc_value_t args[], // in: input arguments
        size_t nargs,           // in: number of input arguments
        qstring *errbuf)        // out: error message if compilation fails
{                               // Returns: success
  qnotused(name);
  qnotused(nargs);
  qnotused(args);
  qnotused(result);
  if ( errbuf != nullptr )
    *errbuf = "evaluation error";
  return false;
}

//--------------------------------------------------------------------------
bool idaapi eval_expr(          // Compile and evaluate expression
        idc_value_t *rv,        // out: expression value
        ea_t current_ea,        // in: current address. if unknown then BADADDR
        const char *expr,       // in: expression to evaluation
        qstring *errbuf)        // out: error message if compilation fails
{                               // Returns: success
  qnotused(current_ea);
  // we know to parse and decimal and hexadecimal numbers
  int radix = 10;
  const char *ptr = skip_spaces(expr);
  bool neg = false;
  if ( *ptr == '-' )
  {
    neg = true;
    ptr = skip_spaces(ptr+1);
  }
  if ( *ptr == '0' && *(ptr+1) == 'x' )
  {
    radix = 16;
    ptr += 2;
  }
  sval_t value = 0;
  while ( radix == 10 ? qisdigit(*ptr) : qisxdigit(*ptr) )
  {
    int d = *ptr <= '9' ? *ptr-'0' : qtolower(*ptr)-'a'+10;
    value *= radix;
    value += d;
    ptr++;
  }
  if ( neg )
    value = -value;
  ptr = skip_spaces(ptr);
  if ( *ptr != '\0' )
  {
    msg("EVAL FAILED: %s\n", expr);
    if ( errbuf != nullptr )
      *errbuf = "syntax error";
    return false;
  }

  // we have the result, store it in the return value
  if ( rv != nullptr )
  {
    rv->clear();
    rv->num = value;
  }
  msg("EVAL %" FMT_EA "d: %s\n", value, expr);
  return true;
}

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  extlang_t my_extlang =
  {
    sizeof(extlang_t),            // Size of this structure
    0,                            // Language features, currently 0
    0,                            // refcnt
    "extlang sample",             // Language name
    nullptr,                      // fileext
    nullptr,                      // syntax highlighter
    compile_expr,
    nullptr,                      // compile_file
    call_func,
    eval_expr,
    nullptr,                      // create_object
    nullptr,                      // get_attr
    nullptr,                      // set_attr
    nullptr,                      // call_method
    nullptr,                      // eval_snippet
    nullptr,                      // load_procmod
    nullptr,                      // unload_procmod
  };
  bool installed = false;

  plugin_ctx_t()
  {
    installed = install_extlang(&my_extlang) >= 0;
  }
  ~plugin_ctx_t()
  {
    if ( installed )
      remove_extlang(&my_extlang);
  }

  virtual bool idaapi run(size_t) override { return false; }
};

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  plugin_ctx_t *ctx = new plugin_ctx_t;
  if ( !ctx->installed )
  {
    msg("extlang: install_extlang() failed\n");
    delete ctx;
    ctx = nullptr;
  }
  return ctx;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_HIDE           // Plugin should not appear in the Edit, Plugins menu
  | PLUGIN_FIX          // Load plugin when IDA starts and keep it in the
                        // memory until IDA stops
  | PLUGIN_MULTI,       // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  nullptr,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Sample third party language", // the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};

formsample

This plugin demonstrates how to use complex forms.

/*
 *  This plugin demonstrates how to use complex forms.
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
static int idaapi btn_cb(int, form_actions_t &)
{
  warning("button pressed");
  return 0;
}

//--------------------------------------------------------------------------
static int idaapi modcb(int fid, form_actions_t &fa)
{
  switch ( fid )
  {
    case CB_INIT:
      msg("initializing\n");
      break;
    case CB_YES:
      msg("terminating\n");
      break;
    case 5:     // operand
      msg("changed operand\n");
      break;
    case 6:     // check
      msg("changed check\n");
      break;
    case 7:     // button
      msg("changed button\n");
      break;
    case 8:     // color button
      msg("changed color button\n");
      break;
    default:
      msg("unknown id %d\n", fid);
      break;
  }

  bool is_gui = is_idaq();

  qstring buf0;
  if ( !fa.get_string_value(5, &buf0) )
    INTERR(30145);

  if ( buf0 == "on" )
    fa.enable_field(12, true);

  if ( buf0 == "off" )
    fa.enable_field(12, false);

  ushort buf1;
  if ( !fa.get_cbgroup_value(12, &buf1) )
    INTERR(30146);

  fa.show_field(7, (buf1 & 1) != 0);
  fa.enable_field(8, (buf1 & 2) != 0);


  ushort c13;
  if ( !fa.get_checkbox_value(13, &c13) )
    INTERR(30147);
  fa.enable_field(10, c13 != 0);

  ushort c14;
  if ( !fa.get_checkbox_value(14, &c14) )
    INTERR(30148);
  fa.enable_field(5, c14 != 0);

  ushort c15;
  if ( !fa.get_checkbox_value(15, &c15) )
    INTERR(30149);

  if ( (buf1 & 8) != 0 )
  {
    sval_t x, y, w, h;
    fa.get_signed_value(4, &x);
    fa.get_signed_value(3, &y);
    fa.get_signed_value(2, &w);
    fa.get_signed_value(1, &h);
    fa.move_field(5, x, y, w, h);
    if ( x != -1 && c15 )
      fa.move_field(-5, x-7, y, w, h);
  }

  // get_field_value() for buttons must return false always
  if ( fa._get_field_value(7, nullptr) )
    INTERR(30150);

  bgcolor_t bgc = -1;
  if ( is_gui && !fa.get_color_value(8, &bgc) )
    INTERR(30151);
  msg("  op=%s change=%x color=%x\n", buf0.c_str(), buf1, bgc);

  fa.set_label_value(9, buf0.c_str());
  return 1;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  static const char form[] =
    "@0:477[]\n"
    "Manual operand\n"
    "\n"
    "%/Enter alternate string for the %9X operand\n"
    "\n"
    "  <~O~perand:q5:100:40::>\n"
    "  <~X~:D4:100:10::>\n"
    "  <~Y~:D3:100:10::>\n"
    "  <~W~:D2:100:10::>\n"
    "  <~H~:D1:100:10::>\n"
    "\n"
    "  <S~h~ow Button:C10>\n"
    "  <~E~nable color Button:C11>\n"
    "  <~E~nable C10:C13>\n"
    "  <~S~et operand bounds:C6>\n"
    "  <Enable operand:C14>\n"
    "  <Move label:C15>12>\n"
    "\n"
    " <~B~utton:B7:0:::> <~C~olor button:K8::::>\n"
    "\n"
    "\n";
  qstring buf("original");
  ushort check = 0x12;
  bgcolor_t bgc = 0x556677;
  uval_t x = -1;
  uval_t y = -1;
  uval_t w = -1;
  uval_t h = -1;
  CASSERT(IS_FORMCHGCB_T(modcb));
  CASSERT(IS_QSTRING(buf));
  if ( ask_form(form, modcb, buf.c_str(), &buf, &x, &y, &w, &h, &check, btn_cb, &bgc) > 0 )
  {
    msg("operand: %s\n", buf.c_str());
    msg("check = %d\n", check);
    msg("dim = %a %a %a %a\n", x, y, w, h);
    msg("bgc = %x\n", bgc);
  }
  return true;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

funclist

This sample plugin demonstrates how to get the the entry point prototypes.

/*
 *  This is a sample plugin module
 *
 *      It demonstrates how to get the the entry point prototypes
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <auto.hpp>
#include <entry.hpp>
#include <bytes.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <typeinf.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  virtual bool idaapi run(size_t) override;
};

//-------------------------------------------------------------------------
// non-modal entry point chooser
struct entry_chooser_t : public chooser_t
{
protected:
  struct item_t
  {
    ea_t ea;
    qstring decl;
    int ord;
    uint32 argsize;
  };
  // remember the information about an entry point in this qvector
  qvector<item_t> list;

  static const int widths_[];
  static const char *const header_[];

public:
  // this object must be allocated using `new`
  entry_chooser_t();

  // function that is used to decide whether a new chooser should be opened
  // or we can use the existing one.
  // There should be the only window as the entry points data are static.
  virtual const void *get_obj_id(size_t *len) const override { *len = 1; return ""; }

  // function that returns number of lines in the list
  virtual size_t idaapi get_count() const override { return list.size(); }

  // function that generates the list line
  virtual void idaapi get_row(
        qstrvec_t *cols,
        int *icon_,
        chooser_item_attrs_t *attrs,
        size_t n) const override;

  // function that is called when the user hits Enter
  virtual cbret_t idaapi enter(size_t n) override
  {
    if ( n < list.size() )
      jumpto(list[n].ea);
    return cbret_t(); // nothing changed
  }

  // function that is called when the chooser is initialized
  virtual bool idaapi init() override
  {
    // rebuild the list
    list.clear();
    size_t n = get_entry_qty();
    // gather information about the entry points
    for ( size_t i = 0; i < n; ++i )
    {
      asize_t ord = get_entry_ordinal(int(i));
      ea_t ea = get_entry(ord);
      if ( ord == ea )
        continue;
      tinfo_t type;
      qstring decl;
      qstring long_name;
      qstring true_name;
      asize_t argsize = 0;
      qstring entry_name;
      get_entry_name(&entry_name, ord);
      if ( get_tinfo(&type, ea) && type.print(&decl, entry_name.c_str()) )
      {
        // found type info, calc the size of arguments
        func_type_data_t fi;
        if ( type.get_func_details(&fi) && !fi.empty() )
        {
          for ( int k=0; k < fi.size(); k++ )
          {
            int s1 = fi[k].type.get_size();
            uchar szi = inf_get_cc_size_i();
            s1 = qmax(s1, szi);
            argsize += s1;
          }
        }
      }
      else if ( get_long_name(&long_name, ea) > 0
             && get_name(&true_name, ea, GN_NOT_DUMMY) > 0
             && long_name != true_name )
      {
        // found mangled name
      }
      else
      {
        // found nothing, just show the name
        if ( get_visible_name(&decl, ea) <= 0 )
          continue;
      }
      if ( argsize == 0 )
      {
        func_t *pfn = get_func(ea);
        if ( pfn != nullptr )
          argsize = pfn->argsize;
      }
      item_t x;
      x.ord = ord;
      x.ea = ea;
      x.decl.swap(decl);
      x.argsize = uint32(argsize);
      list.push_back(x);
    }
    return true;
  }

  // function that is called when the user wants to refresh the chooser
  virtual cbret_t idaapi refresh(ssize_t n) override
  {
    init();
    if ( n < 0 )
      return NO_SELECTION;
    return adjust_last_item(n);  // try to preserve the cursor
  }
};
DECLARE_TYPE_AS_MOVABLE(entry_chooser_t::item_t);

// column widths
const int entry_chooser_t::widths_[] =
{
  CHCOL_DEC | 4,  // Ordinal
  CHCOL_HEX | 8,  // Address
  CHCOL_HEX | 6,  // ArgSize
  70,             // Declaration
};
// column headers
const char *const entry_chooser_t::header_[] =
{
  "Ordinal",      // 0
  "Address",      // 1
  "ArgSize",      // 2
  "Declaration",  // 3
};

inline entry_chooser_t::entry_chooser_t()
  : chooser_t(CH_CAN_REFRESH, // user can refresh the chooser using Ctrl-U
              qnumber(widths_), widths_, header_,
              "Exported functions"),
    list()
{
  CASSERT(qnumber(widths_) == qnumber(header_));
}

void idaapi entry_chooser_t::get_row(
        qstrvec_t *cols_,
        int *,
        chooser_item_attrs_t *,
        size_t n) const
{
  // assert: n < list.size()
  const item_t &item = list[n];

  // generate the line
  qstrvec_t &cols = *cols_;
  cols[0].sprnt("%d", item.ord);
  cols[1].sprnt("%08a", item.ea);
  if ( item.argsize != 0 )
    cols[2].sprnt("%04x", item.argsize);
  cols[3] = item.decl;
  CASSERT(qnumber(header_) == 4);
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  if ( !auto_is_ok()
    && ask_yn(ASKBTN_NO,
              "HIDECANCEL\n"
              "The autoanalysis has not finished yet.\n"
              "The result might be incomplete.\n"
              "Do you want to continue?") < ASKBTN_YES )
  {
    return true;
  }

  // open the window
  entry_chooser_t *ch = new entry_chooser_t();
  ch->choose();
  return true; //-V773
} //lint !e429 'ch' has not been freed or returned

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  if ( get_entry_qty() == 0 )
    return nullptr;
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  "Generate list of exported function prototypes",
  "Generate list of exported function prototypes",
  "List of exported functions",
  "Ctrl-F11",
};

getlines

This sample plugin demonstrates how to get the disassembly lines for one address.

/*
 *  This is a sample plugin module
 *
 *      It demonstrates how to get the disassembly lines for one address
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <bytes.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  ea_t ea = get_screen_ea();
  if ( ask_addr(&ea, "Please enter the disassembly address")
    && is_mapped(ea) )                              // address belongs to disassembly
  {
    int flags = calc_default_idaplace_flags();
    linearray_t ln(&flags);
    idaplace_t pl;
    pl.ea = ea;
    pl.lnnum = 0;
    ln.set_place(&pl);
    msg("printing disassembly lines:\n");
    int n = ln.get_linecnt();                // how many lines for this address?
    for ( int i=0; i < n; i++ )              // process all of them
    {
      qstring buf;
      tag_remove(&buf, *ln.down());          // get line and remove color codes
      msg("%d: %s\n", i, buf.c_str());       // display it on the message window
    }
    msg("total %d lines\n", n);
  }
  return true;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
static const char comment[] = "Generate disassembly lines for one address";
static const char help[] = "Generate disassembly lines for one address\n";


//--------------------------------------------------------------------------
// This is the preferred name of the plugin module in the menu system
// The preferred name may be overridden in plugins.cfg file

static const char wanted_name[] = "Disassembly lines sample";


// This is the preferred hotkey for the plugin module
// The preferred hotkey may be overridden in plugins.cfg file

static const char wanted_hotkey[] = "";


//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  comment,              // long comment about the plugin
  help,                 // multiline help about the plugin
  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

highlighter

This plugin will display a colored box at the executed instructions.

// Highlighter plugin v1.0
// Highlights executed instructions

// This plugin will display a colored box at the executed instructions.
// It will take into account only the instructions where the application
// has been suspended.

// http://www.hexblog.com/2005/11/the_highlighter.html

// Copyright 2005 Ilfak Guilfanov, <ig@hexblog.com>

#include <ida.hpp>
#include <idp.hpp>
#include <dbg.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//--------------------------------------------------------------------------
struct plugin_ctx_t;
struct idd_post_events_t : public post_event_visitor_t
{
  plugin_ctx_t &ctx;
  idd_post_events_t(plugin_ctx_t &_ctx) : ctx(_ctx) {}
  virtual ssize_t idaapi handle_post_event(
        ssize_t code,
        int notification_code,
        va_list va) override;
};

//--------------------------------------------------------------------------
struct exec_prefix_t : public user_defined_prefix_t
{
  static const int prefix_width = 1;

  plugin_ctx_t &ctx;
  exec_prefix_t(plugin_ctx_t &_ctx)
    : user_defined_prefix_t(prefix_width, &_ctx), ctx(_ctx) {}

  virtual void idaapi get_user_defined_prefix(
        qstring *out,
        ea_t ea,
        const insn_t &insn,
        int lnnum,
        int indent,
        const char *line) override;
};

//--------------------------------------------------------------------------
typedef std::set<ea_t> easet_t;
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
  idd_post_events_t idd_post_events = idd_post_events_t(*this);

  exec_prefix_t *exec_prefix = nullptr;

  // List of executed addresses
  easet_t execset;

  ea_t old_ea = BADADDR;
  int old_lnnum = 0;

  plugin_ctx_t()
  {
    hook_event_listener(HT_DBG, this);
  }
  ~plugin_ctx_t()
  {
    // listeners are uninstalled automatically
    // when the owner module is unloaded
    exec_prefix = nullptr; // make lint happy
  }

  virtual bool idaapi run(size_t) override;
  virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};

//--------------------------------------------------------------------------
// A sample how to generate user-defined line prefixes
static const char highlight_prefix[] = { COLOR_INV, ' ', COLOR_INV, 0 };
void idaapi exec_prefix_t::get_user_defined_prefix(
        qstring *buf,
        ea_t ea,
        const insn_t &,
        int lnnum,
        int indent,
        const char *line)
{
  buf->qclear();        // empty prefix by default

  // We want to display the prefix only the lines which
  // contain the instruction itself

  if ( indent != -1 )
    return;           // a directive
  if ( line[0] == '\0' )
    return;        // empty line
  if ( tag_advance(line,1)[-1] == ASH.cmnt[0] )
    return; // comment line...

  // We don't want the prefix to be printed again for other lines of the
  // same instruction/data. For that we remember the line number
  // and compare it before generating the prefix

  if ( ctx.old_ea == ea && ctx.old_lnnum == lnnum )
    return;

  if ( ctx.execset.find(ea) != ctx.execset.end() )
    *buf = highlight_prefix;

  // Remember the address and line number we produced the line prefix for:
  ctx.old_ea = ea;
  ctx.old_lnnum = lnnum;
}

//--------------------------------------------------------------------------
ssize_t idaapi idd_post_events_t::handle_post_event(
        ssize_t retcode,
        int notification_code,
        va_list va)
{
  switch ( notification_code )
  {
    case debugger_t::ev_get_debug_event:
      {
        gdecode_t *code = va_arg(va, gdecode_t *);
        debug_event_t *event = va_arg(va, debug_event_t *);
        if ( *code == GDE_ONE_EVENT )    // got an event?
          ctx.execset.insert(event->ea);
      }
      break;
  }
  return retcode;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  info("AUTOHIDE NONE\n"
       "This is the highlighter plugin.\n"
       "It highlights executed instructions if a debug event occurs at them.\n"
       "The plugins is fully automatic and has no parameters.\n");
  return true;
}

//--------------------------------------------------------------------------
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list /*va*/)
{
  // We set our debug event handler at the beginning and remove it at the end
  // of a debug session
  switch ( code )
  {
    case dbg_process_start:
    case dbg_process_attach:
      exec_prefix = new exec_prefix_t(*this);
      register_post_event_visitor(HT_IDD, &idd_post_events, this);
      break;
    case dbg_process_exit:
      // do not unregister idd_post_events - it should be removed automatically
      delete exec_prefix;
      exec_prefix = nullptr;
      execset.clear();
      break;
  }
  return 0;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
static const char wanted_name[] = "Highlighter";
static const char wanted_hotkey[] = "";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  wanted_name,          // long comment about the plugin
  wanted_name,          // multiline help about the plugin
  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey,        // the preferred hotkey to run the plugin
};

ht_output

This sample plugin demonstrates receiving output window notification callbacks and usage of new output window functions.

/*
 *  This is a sample plugin demonstrating receiving output window notification callbacks
 *  and using of new output window functions: get_output_curline, get_output_cursor,
 *  get_output_selected_text, add_output_popup
 *
 */

#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//-------------------------------------------------------------------------
struct ht_output_plugin_t : public plugmod_t, public event_listener_t
{
  form_actions_t *fa = nullptr;
  qstring selected_data;

  virtual bool idaapi run(size_t arg) override;
  virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;

  void desc_notification(
        const char *notification_name) const;
  ~ht_output_plugin_t();
};

//-------------------------------------------------------------------------
AS_PRINTF(2, 3) static void form_msg(form_actions_t *fa, const char *format, ...)
{
  textctrl_info_t ti;
  fa->get_text_value(1, &ti);
  va_list va;
  va_start(va, format);
  ti.text.cat_vsprnt(format, va);
  va_end(va);
  fa->set_text_value(1, &ti);
}

//---------------------------------------------------------------------------
void ht_output_plugin_t::desc_notification(
        const char *notification_name) const
{
  form_msg(fa, "Received notification from output window: \"%s\"\n",
           notification_name);
}

//-------------------------------------------------------------------------
struct printsel_ah_t : public action_handler_t
{
  ht_output_plugin_t *plugmod;

  printsel_ah_t(ht_output_plugin_t *_plgmod) : plugmod(_plgmod) {}

  virtual int idaapi activate(action_activation_ctx_t *) override
  {
    form_msg(plugmod->fa,
             "User menu item is called for selection: \"%s\"\n",
             plugmod->selected_data.c_str());
    return 1;
  }

  virtual action_state_t idaapi update(action_update_ctx_t *) override
  {
    return AST_ENABLE_ALWAYS;
  }
};

//---------------------------------------------------------------------------
// Callback for ui notifications
static ssize_t idaapi ui_callback(void *ud, int notification_code, va_list va)
{
  switch ( notification_code )
  {
    // called when IDA is preparing a context menu for a view
    // Here dynamic context-depending user menu items can be added.
    case ui_populating_widget_popup:
      {
        TWidget *f = va_arg(va, TWidget *);
        if ( get_widget_type(f) == BWN_OUTPUT )
        {
          TPopupMenu *p = va_arg(va, TPopupMenu *);
          ht_output_plugin_t *plgmod = (ht_output_plugin_t *) ud;
          plgmod->selected_data.qclear();
          if ( get_output_selected_text(&plgmod->selected_data) )
          {
            action_desc_t desc = DYNACTION_DESC_LITERAL(
                    "Print selection",
                    new printsel_ah_t(plgmod),
                    nullptr, nullptr, -1);
            attach_dynamic_action_to_popup(nullptr, p, desc);
          }
          plgmod->desc_notification("msg_popup");
        }
      }
      break;
  }
  return 0;
}

//---------------------------------------------------------------------------
ht_output_plugin_t::~ht_output_plugin_t()
{
  unhook_from_notification_point(HT_UI, ui_callback, this);
}

//---------------------------------------------------------------------------
// Callback for view notifications
ssize_t idaapi ht_output_plugin_t::on_event(
        ssize_t notification_code,
        va_list va)
{
  switch ( notification_code )
  {
    case msg_activated:
      desc_notification("msg_activated");
      break;
    case msg_deactivated:
      desc_notification("msg_deactivated");
      break;
    case msg_keydown:
      {
        desc_notification("msg_keydown");
        int key = va_arg(va, int);
        int state = va_arg(va, int);
        form_msg(fa, "Parameters: Key:%d(\'%c\') State:%d\n", key, key, state);
      }
      break;
    case msg_click:
    case msg_dblclick:
      {
        desc_notification(notification_code == msg_click ? "msg_click" : "msg_dblclick");
        int px = va_arg(va, int);
        int py = va_arg(va, int);
        int state = va_arg(va, int);
        qstring buf;
        if ( get_output_curline(&buf, false) )
          form_msg(fa, "Clicked string: %s\n", buf.c_str());
        int cx,cy;
        get_output_cursor(&cx, &cy);
        msg("Parameters: x:%d, y:%d, state:%d\n", px, py, state);
        msg("Cursor position:(%d, %d)\n", cx, cy);
      }
      break;
    case msg_closed:
      desc_notification("msg_closed");
  }
  return 0;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new ht_output_plugin_t;
}

//--------------------------------------------------------------------------
// this callback is called when something happens in our editor form
static int idaapi editor_modcb(int fid, form_actions_t &f_actions)
{
  ht_output_plugin_t *plgmod = (ht_output_plugin_t *) f_actions.get_ud();
  if ( fid == CB_INIT ) // Initialization
  {
    /* set callback for output window notifications */
    hook_to_notification_point(HT_UI, ui_callback, plgmod);
    hook_event_listener(HT_OUTPUT, plgmod, plgmod);
    plgmod->fa = &f_actions;
  }
  else if ( fid == CB_CLOSE )
  {
    unhook_event_listener(HT_OUTPUT, plgmod);
    unhook_from_notification_point(HT_UI, ui_callback, plgmod);
  }
  return 1;
}

//--------------------------------------------------------------------------
bool idaapi ht_output_plugin_t::run(size_t)
{
  static const char formdef[] =
    "BUTTON NO NONE\n"        // we do not want the standard buttons on the form
    "BUTTON YES NONE\n"
    "BUTTON CANCEL NONE\n"
    "Editor form\n"           // the form title. it is also used to refer to the form later
    "\n"
    "%/%*"                    // placeholder for the 'editor_modcb' callback, and its userdata
    "<Text:t1::40:::>\n"      // text edit control
    "\n";

  // structure for text edit control
  textctrl_info_t ti;
  ti.cb = sizeof(textctrl_info_t);
  ti.text = "";

  open_form(formdef, 0, editor_modcb, this, &ti);
  return true;
}

static const char wanted_name[] = "HT_OUTPUT notifications handling example";
static const char wanted_hotkey[] = "Ctrl-Alt-F11";
//--------------------------------------------------------------------------
static const char comment[] = "HT_OUTPUT notifications handling";
static const char help[] =
        "This pluging demonstrates handling of output window\n"
        "notifications: Activation/Desactivation, adding\n"
        "popup menus, keyboard and mouse events, changing of current\n"
        "cursor position and closing of view\n";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  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

  help,                 // multiline help about the plugin

  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

ht_view

This sample plugin demonstrates usage of the view callbacks and adding custom menu items to popup menus.

/*
 *  This is a sample plugin demonstrating usage of the view callbacks
 *  and adding custom menu items to popup menus
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <bytes.hpp>
#include <graph.hpp>

#define ACTION1_NAME "ht_view:Act1"
#define ACTION2_NAME "ht_view:Act2"

//-------------------------------------------------------------------------
struct ht_view_plugin_t : public plugmod_t, public event_listener_t
{
  bool hooked = false;

  ht_view_plugin_t();
  virtual ~ht_view_plugin_t();
  virtual bool idaapi run(size_t arg) override;
  virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;

  void desc_notification(
        const char *notification_name,
        TWidget *view) const;
  void desc_mouse_event(
        const view_mouse_event_t *event) const;
};

//---------------------------------------------------------------------------
// Callback for ui notifications
static ssize_t idaapi ui_callback(void *ud, int notification_code, va_list va)
{
  switch ( notification_code )
  {
    // called when IDA is preparing a context menu for a view
    // Here dynamic context-depending user menu items can be added.
    case ui_populating_widget_popup:
      {
        TWidget *view = va_arg(va, TWidget *);
        if ( get_widget_type(view) == BWN_DISASM )
        {
          TPopupMenu *p = va_arg(va, TPopupMenu *);
          ht_view_plugin_t *plgmod = (ht_view_plugin_t *) ud;
          plgmod->desc_notification("view_popup", view);
          attach_action_to_popup(view, p, ACTION1_NAME);
          attach_action_to_popup(view, p, ACTION2_NAME);
        }
      }
      break;
  }
  return 0;
}

//-------------------------------------------------------------------------
struct ahandler_t : public action_handler_t
{
  bool first;
  ahandler_t(bool _first) : first(_first) {}
  virtual int idaapi activate(action_activation_ctx_t *) override
  {
    msg("User %s menu item is called\n", first ? "first" : "second");
    return true;
  }

  virtual action_state_t idaapi update(action_update_ctx_t *) override
  {
    return AST_ENABLE_ALWAYS;
  }
};
static ahandler_t ah1(true);
static ahandler_t ah2(false);

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new ht_view_plugin_t;
}

//-------------------------------------------------------------------------
ht_view_plugin_t::ht_view_plugin_t()
{
  // Register actions
  const action_desc_t actions[] =
  {
#define ROW(name, label, handler) ACTION_DESC_LITERAL_PLUGMOD(name, label, handler, this, nullptr, nullptr, -1)
    ROW(ACTION1_NAME, "First ht_view's popup menu item", &ah1),
    ROW(ACTION2_NAME, "Second ht_view's popup menu item", &ah2),
#undef ROW
  };

  for ( size_t i = 0, n = qnumber(actions); i < n; ++i )
    register_action(actions[i]);
}

//-------------------------------------------------------------------------
ht_view_plugin_t::~ht_view_plugin_t()
{
  unhook_from_notification_point(HT_UI, ui_callback, this);
}

//-------------------------------------------------------------------------
bool idaapi ht_view_plugin_t::run(size_t)
{
  /* set callback for view notifications */
  if ( !hooked )
  {
    hook_event_listener(HT_VIEW, this);
    hook_to_notification_point(HT_UI, ui_callback, this);
    hooked = true;
    msg("HT_VIEW: installed view notification hook.\n");
  }

  return true;
}

//---------------------------------------------------------------------------
ssize_t idaapi ht_view_plugin_t::on_event(
        ssize_t notification_code,
        va_list va)
{
  TWidget *view = va_arg(va, TWidget *);
  switch ( notification_code )
  {
    case view_activated:
      desc_notification("view_activated", view);
      break;
    case view_deactivated:
      desc_notification("view_deactivated", view);
      break;
    case view_keydown:
      {
        desc_notification("view_keydown", view);
        int key = va_arg(va, int);
        int state = va_arg(va, int);
        msg("Parameters: Key:%d(\'%c\') State:%d\n", key, key, state);
      }
      break;
    case view_click:
    case view_dblclick:
      {
        desc_notification(notification_code == view_click ? "view_click" : "view_dblclick", view);
        desc_mouse_event(va_arg(va, view_mouse_event_t*));
        int cx,cy;
        get_cursor(&cx, &cy);
        msg("Cursor position:(%d, %d)\n", cx, cy);
      }
      break;
    case view_curpos:
      {
        desc_notification("view_curpos", view);
        if ( is_idaview(view) )
        {
          char buf[MAXSTR];
          ea2str(buf, sizeof(buf), get_screen_ea());
          msg("New address: %s\n", buf);
        }
      }
      break;
    case view_mouse_over:
      {
        desc_notification("view_mouse_over", view);
        desc_mouse_event(va_arg(va, view_mouse_event_t*));
      }
      break;
    case view_close:
      desc_notification("view_close", view);
  }
  return 0;
}

//-------------------------------------------------------------------------
void ht_view_plugin_t::desc_notification(
        const char *notification_name,
        TWidget *view) const
{
  qstring buffer;
  get_widget_title(&buffer, view);
  msg("Received notification from view %s: \"%s\"\n",
      buffer.c_str(),
      notification_name);
}

//-------------------------------------------------------------------------
void ht_view_plugin_t::desc_mouse_event(
        const view_mouse_event_t *event) const
{
  int px = event->x;
  int py = event->y;
  int state = event->state;
  qstring over_txt;
  const selection_item_t *item = event->location.item;
  if ( event->rtype != TCCRT_FLAT && item != nullptr )
  {
    if ( item->is_node )
      over_txt.sprnt("node %d", item->node);
    else
      over_txt.sprnt("edge %d -> %d", item->elp.e.src, item->elp.e.dst);
  }
  else
  {
    over_txt = "(nothing)";
  }
  msg("Parameters: x:%d, y:%d, state:%d, over:%s\n", px, py, state, over_txt.c_str());
}


//-------------------------------------------------------------------------
static const char wanted_name[] = "HT_VIEW notification handling example";
static const char wanted_hotkey[] = "";
//--------------------------------------------------------------------------
static const char comment[] = "HT_VIEW notification Handling";
static const char help[] =
        "This pluging demonstrates handling of custom and IdaView\n"
        "notifications: Activation/Desactivation of views, adding\n"
        "popup menus, keyboard and mouse events, changing of current\n"
        "address and closing of view\n";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  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

  help,                 // multiline help about the plugin

  wanted_name,          // the preferred short name of the plugin
  wanted_hotkey         // the preferred hotkey to run the plugin
};

mtsample

Sample multi-threaded plugin module.

/*
 *  This is a sample multi-threaded plugin module
 *
 *  It creates 3 new threads. Each threads sleeps and prints a message in a loop
 *
 */

#ifdef __NT__
#include <windows.h>
#endif

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

#ifdef __NT__
#include <windows.h>
#endif


//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  qthread_t children[10] = { nullptr };
  int nchilds = 0;

  ~plugin_ctx_t() { term(); }
  void term()
  {
    if ( nchilds > 0 )
    {
      msg("Killing all threads\n");
      for ( int i=0; i < nchilds; i++ )
      {
        qthread_kill(children[i]);
        qthread_join(children[i]);
        // cancel all pending requests from the killed thread
        cancel_thread_exec_requests(children[i]);
        qthread_free(children[i]);
      }
      msg("Killed all threads\n");
      nchilds = 0;
    }
  }
  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
static void say_hello(size_t id, qthread_t tid, int cnt)
{
  struct ida_local hello_t : public exec_request_t
  {
    uint64 nsecs;
    size_t id;
    qthread_t tid;
    int cnt;
    int idaapi execute(void) override
    {
      uint64 now = get_nsec_stamp();
      int64 delay = now - nsecs;
      msg("Hello %d from thread %" FMT_Z ". tid=%p. current tid=%p (delay=%" FMT_64 "d)\n",
          cnt, id, tid, qthread_self(), delay);
      return 0;
    }
    hello_t(size_t _id, qthread_t _tid, int _cnt) : id(_id), tid(_tid), cnt(_cnt)
    {
      nsecs = get_nsec_stamp();
    }
  };
  hello_t hi(id, tid, cnt);

  int mff;
  switch ( id % 3 )
  {
    case 0: mff = MFF_FAST;  break;
    case 1: mff = MFF_READ;  break;
    default:
    case 2: mff = MFF_WRITE; break;
  }
  execute_sync(hi, mff);
}

//--------------------------------------------------------------------------
static int idaapi thread_func(void *ud)
{
  size_t id = (size_t)ud;
  qthread_t tid = qthread_self();
  int cnt = 0;
  srand(id ^ (size_t)tid);
  while ( true )
  {
    say_hello(id, tid, cnt++);
    int r = rand() % 1000;
    qsleep(r);
  }
  return 0;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  if ( nchilds == 0 )
  {
    children[nchilds] = qthread_create(thread_func, (void *)(ssize_t)nchilds); nchilds++;
    children[nchilds] = qthread_create(thread_func, (void *)(ssize_t)nchilds); nchilds++;
    children[nchilds] = qthread_create(thread_func, (void *)(ssize_t)nchilds); nchilds++;
    msg("Three new threads have been created. Main thread id %p\n", qthread_self());
    for ( int i=0; i < 5; i++ )
      say_hello(-1, 0, 0);
  }
  else
  {
    term();
  }
  return true;
}

//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  nullptr,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Multi-threaded sample", // the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};

This sample plugin demonstrates how to customize navigation band colors.

/*
 *  This plugin demonstrates how to customize navigation band colors.
 *  Launch the plugin like so:
 *    - to install: ida_loader.load_and_run_plugin("navcolor", 1)
 *    - to uninstall: ida_loader.load_and_run_plugin("navcolor", 0)
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>


//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  nav_colorizer_t *old_col_fun = nullptr;
  void *old_col_ud = nullptr;
  bool installed = false;

  //lint -esym(1540, plugin_ctx_t::old_col_fun, plugin_ctx_t::old_col_ud)
  ~plugin_ctx_t()
  {
    // uninstall our callback for navigation band, otherwise ida will crash
    maybe_uninstall();
  }
  virtual bool idaapi run(size_t) override;

  bool maybe_install();
  bool maybe_uninstall();
};

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t code)
{
  return code == 1 ? maybe_install() : maybe_uninstall();
}

//--------------------------------------------------------------------------
// Callback that calculates the pixel color given the address and the number of bytes
static uint32 idaapi my_colorizer(ea_t ea, asize_t nbytes, void *ud)
{
  plugin_ctx_t &ctx = *(plugin_ctx_t *)ud;
  // you are at your own here. just for the sake of illustrating how things work
  // we will invert all colors
  uint32 color = ctx.old_col_fun(ea, nbytes, ctx.old_col_ud);
  return ~color;
}

//-------------------------------------------------------------------------
bool plugin_ctx_t::maybe_install()
{
  bool ok = !installed;
  if ( ok )
  {
    set_nav_colorizer(&old_col_fun, &old_col_ud, my_colorizer, this);
    installed = true;
  }
  return ok;
}

//-------------------------------------------------------------------------
bool plugin_ctx_t::maybe_uninstall()
{
  bool ok = installed;
  if ( ok )
  {
    set_nav_colorizer(nullptr, nullptr, old_col_fun, old_col_ud);
    installed = false;
  }
  return ok;
}

//--------------------------------------------------------------------------
// initialize the plugin
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  nullptr,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Modify navigation band colors",// the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};

openform

This plugin demonstrates how to use non modal forms.

/*
 *  This plugin demonstrates how to use non modal forms.
 *  It creates 2 windows on the screen:
 *      - a window with 4 buttons: dock, undock, show, hide      (CONTROL FORM)
 *      - a window with a text edit control and a list control   (EDITOR FORM)
 *  The buttons of the first window can be used to manage the second window.
 *  We will call the first window 'CONTROL FORM' and the second window 'EDITOR
 *  FORM', just to be able to reference them easily.
 */

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

//---------------------------------------------------------------------------
// chooser (list view) items
static const char *const names[] =
{
  "Item one",
  "Item two",
  "Item three"
};

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  // the editor form
  TWidget *editor_widget = nullptr;

  // contents of the text field for each item
  qstring txts[qnumber(names)] =
  {
    "Text one:\n This is text for item one",
    "Text two:\n And this is text for item two",
    "Text three:\n And finally text for the last item"
  };

  // Current index for chooser list view
  size_t curidx = 0;
  // Form actions for control dialog
  form_actions_t *control_fa = nullptr;
  // Defines where to place new/existing editor window
  bool dock = false;

  virtual bool idaapi run(size_t) override;

  int editor_modcb(int fid, form_actions_t &fa);
  void open_editor_form(int options = 0);
  void close_editor_form();
  int control_modcb(int fid, form_actions_t &fa);
  void update_buttons(form_actions_t &fa);
};

//---------------------------------------------------------------------------
struct of_chooser_t : public chooser_t
{
public:
  // this object must be allocated using `new`
  // as it used in the non-modal form
  of_chooser_t() : chooser_t()
  {
    columns = 1;
    static const int widths_[] = { 12 };
    static const char *const header_[] = { "Name" };
    widths = widths_;
    header = header_;
  }

  virtual size_t idaapi get_count() const override { return qnumber(names); }
  virtual void idaapi get_row(
        qstrvec_t *cols,
        int *,
        chooser_item_attrs_t *,
        size_t n) const override
  {
    (*cols)[0] = names[n];
  }
};

// Form actions for editor window
enum editor_form_actions
{
  TEXT_CHANGED  = 1,
  ITEM_SELECTED = 2,
};

// Form actions for control window
enum control_form_actions
{
  BTN_DOCK   = 10,
  BTN_UNDOCK = 11,
  BTN_OPEN   = 12,
  BTN_CLOSE  = 13,
};

//--------------------------------------------------------------------------
inline void enable_button(form_actions_t &fa, int fid, bool enabled)
{
  fa.enable_field(fid, enabled);
}

//--------------------------------------------------------------------------
// Update control window buttons state
void plugin_ctx_t::update_buttons(form_actions_t &fa)
{
  bool visible = editor_widget != nullptr;
  enable_button(fa, 10, !dock && visible);
  enable_button(fa, 11, dock && visible);
  enable_button(fa, 12, !visible);
  enable_button(fa, 13, visible);
}

//--------------------------------------------------------------------------
// this callback is called when the user clicks on a button
static int idaapi btn_cb(int, form_actions_t &)
{
  msg("button has been pressed -> \n");
  return 0;
}

//--------------------------------------------------------------------------
// this callback is called when something happens in our non-modal editor form
static int idaapi editor_modcb_(int fid, form_actions_t &fa)
{
  plugin_ctx_t &ctx = *(plugin_ctx_t *)fa.get_ud();
  return ctx.editor_modcb(fid, fa);
}
int plugin_ctx_t::editor_modcb(int fid, form_actions_t &fa)
{
  switch ( fid )
  {
    case CB_INIT:     // Initialization
      msg("init editor form\n");
      break;
    case CB_CLOSE:    // Closing the form
      msg("closing editor form\n");
      // mark the form as closed
      editor_widget = nullptr;
      // If control form exists then update buttons
      if ( control_fa != nullptr )
        update_buttons(*control_fa);
      break;
    case TEXT_CHANGED:     // Text changed
      {
        textctrl_info_t ti;
        fa.get_text_value(1, &ti);
        txts[curidx] = ti.text;
      }
      msg("text has been changed\n");
      break;
    case ITEM_SELECTED:    // list item selected
      {
        sizevec_t sel;
        if ( fa.get_chooser_value(2, &sel) )
        {
          curidx = sel[0];
          textctrl_info_t ti;
          ti.cb = sizeof(textctrl_info_t);
          ti.text = txts[curidx];
          fa.set_text_value(1, &ti);
        }
      }
      msg("selection has been changed\n");
      break;
    default:
      msg("unknown id %d\n", fid);
      break;
  }
  return 1;
}
//---------------------------------------------------------------------------
// create and open the editor form
void plugin_ctx_t::open_editor_form(int options)
{
  static const char formdef[] =
    "BUTTON NO NONE\n"        // we do not want the standard buttons on the form
    "BUTTON YES NONE\n"
    "BUTTON CANCEL NONE\n"
    "Editor form\n"           // the form title. it is also used to refer to the form later
    "\n"
    "%/%*"                    // placeholder for the 'editor_modcb' callback
    "\n"
    "<List:E2::30:1::><|><Text:t1::60:::>\n" // text edit control and chooser control separated by splitter
    "\n";
  // structure for text edit control
  textctrl_info_t ti;
  ti.cb = sizeof(textctrl_info_t);
  ti.text = txts[0];
  // structure for chooser list view
  of_chooser_t *ofch = new of_chooser_t();
  // selection for chooser list view
  sizevec_t selected;
  selected.push_back(0);  // first item by default
  editor_widget = open_form(formdef,
                            options,
                            editor_modcb_, this,
                            ofch, &selected,
                            &ti);
} //lint !e429 custodial pointer 'ofch' likely not freed nor returned


//---------------------------------------------------------------------------
void plugin_ctx_t::close_editor_form()
{
  msg("closing editor widget\n");
  close_widget(editor_widget, WCLS_CLOSE_LATER);
  editor_widget = nullptr;
}
//--------------------------------------------------------------------------
inline void dock_form(bool _dock)
{
  set_dock_pos("Editor form",
               "IDA View-A",
               _dock ? DP_TAB : DP_FLOATING);
}

//--------------------------------------------------------------------------
// this callback is called when something happens in our non-modal control form
static int idaapi control_modcb_(int fid, form_actions_t &fa)
{
  plugin_ctx_t &ctx = *(plugin_ctx_t *)fa.get_ud();
  return ctx.control_modcb(fid, fa);
}
int plugin_ctx_t::control_modcb(int fid, form_actions_t &fa)
{
  switch ( fid )
  {
    case CB_INIT:   // Initialization
      msg("init control form\n");
      dock = false;
      control_fa = &fa;   // remember the 'fa' for the future
      break;
    case CB_CLOSE:  // Closing
      msg("closing control form\n");
      control_fa = nullptr;
      return 1;
    case BTN_DOCK:
      msg("dock editor form\n");
      dock = true;
      dock_form(dock);
      break;
    case BTN_UNDOCK:
      msg("undock editor form\n");
      dock = false;
      dock_form(dock);
      break;
    case BTN_OPEN:
      msg("open editor form\n");
      open_editor_form(WOPN_DP_TAB|WOPN_RESTORE);
      dock_form(dock);
      break;
    case BTN_CLOSE:
      close_editor_form();
      break;
    default:
      msg("unknown id %d\n", fid);
      return 1;
  }
  update_buttons(fa);
  return 1;
}

//--------------------------------------------------------------------------
// the main function of the plugin
bool idaapi plugin_ctx_t::run(size_t)
{
  // first open the editor form
  open_editor_form(WOPN_RESTORE);

  static const char control_form[] =
    "BUTTON NO NONE\n"          // do not display standard buttons at the bottom
    "BUTTON YES NONE\n"
    "BUTTON CANCEL NONE\n"
    "Control form\n"            // the title. it is used to refer to the form later
    "%/%*"                      // placeholder for control_modcb
    "<Dock:B10:30:::><Undock:B11:30:::><Show:B12:30:::><Hide:B13:30:::>\n"; // Create control buttons

  open_form(control_form,
            WOPN_RESTORE,
            control_modcb_, this,
            btn_cb, btn_cb, btn_cb, btn_cb);
  set_dock_pos("Control form", nullptr, DP_FLOATING, 0, 0, 300, 100);
  return true;
}

//--------------------------------------------------------------------------
// initialize the plugin
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  "Open non-modal form sample",// the preferred short name of the plugin
  nullptr,
};

procext

Sample plugin that extends the IBM PC processor module to disassemble some NEC V20 instructions.

/*
 *  This is a sample plugin module
 *  It extends the IBM PC processor module to disassemble some NEC V20 instructions
 *  This is a sample file, it supports just two instructions!
 *
 */

#include <ida.hpp>
#include <idp.hpp>
#include <bytes.hpp>
#include <loader.hpp>
#include <kernwin.hpp>

int data_id;

//--------------------------------------------------------------------------
// Context data for the plugin. This object is created by the init()
// function and hold all local data.
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
  ea_t ea = 0; // current address within the instruction

  netnode nec_node;
  bool hooked = false;

  plugin_ctx_t();
  ~plugin_ctx_t();

  // This function is called when the user invokes the plugin.
  virtual bool idaapi run(size_t) override;
  // This function is called upon some events.
  virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;

  size_t ana(insn_t &insn);
  void process_rm(insn_t &insn, op_t &x, uchar postbyte);
};

static const char node_name[] = "$ sample NEC processor extender parameters";


// Some definitions from IBM PC:

#define segrg specval_shorts.high  // IBM PC expects the segment address
                                   // to be here
#define aux_short       0x0020  // short (byte) displacement used
#define aux_basess      0x0200  // SS based instruction

#define R_ss  18
#define R_ds  19
//--------------------------------------------------------------------------
// This plugin supports just 2 instructions:
// Feel free to add more...

// 0FH 20H                      ADD4S       ; Addition for packed BCD strings
// 0FH 12H Postbyte     CLEAR1  reg/mem8,CL ; Clear one bit

enum nec_insn_type_t
{
  NEC_add4s = CUSTOM_INSN_ITYPE,
  NEC_clear1,
};

//----------------------------------------------------------------------
static int get_dataseg(insn_t &insn, int defseg)
{
  if ( defseg == R_ss )
    insn.auxpref |= aux_basess;
  return defseg;
}

//--------------------------------------------------------------------------
//
//              process r/m byte of the instruction
//
void plugin_ctx_t::process_rm(insn_t &insn, op_t &x, uchar postbyte)
{
  int Mod = (postbyte >> 6) & 3;
  x.reg = postbyte & 7;
  if ( Mod == 3 )               // register
  {
    if ( x.dtype == dt_byte )
      x.reg += 8;
    x.type = o_reg;
  }
  else                          // memory
  {
    if ( Mod == 0 && x.reg == 6 )
    {
      x.type = o_mem;
      x.offb = uchar(ea-insn.ea);
      x.addr = get_word(ea); ea+=2;
      x.segrg = (uint16)get_dataseg(insn, R_ds);
    }
    else
    {
      x.type = o_phrase;        // x.phrase contains the base register
      x.addr = 0;
      int reg = (x.phrase == 2 || x.phrase == 3 || x.phrase == 6) ? R_ss : R_ds;
      x.segrg = (uint16)get_dataseg(insn, reg);
                                // [bp+si],[bp+di],[bp] by SS
      if ( Mod != 0 )
      {
        x.type = o_displ;       // i.e. phrase + offset
        x.offb = uchar(ea-insn.ea);
        if ( Mod == 1 )
        {
          x.addr = char(get_byte(ea++));
          insn.auxpref |= aux_short;
        }
        else
        {
          x.addr = get_word(ea); ea+=2;
        }
      }
    }
  }
}

//--------------------------------------------------------------------------
// Analyze an instruction and fill the 'insn' structure
size_t plugin_ctx_t::ana(insn_t &insn)
{
  int code = get_byte(ea++);
  if ( code != 0x0F )
    return 0;
  code = get_byte(ea++);
  switch ( code )
  {
    case 0x20:
      insn.itype = NEC_add4s;
      return 2;
    case 0x12:
      insn.itype = NEC_clear1;
      {
        uchar postbyte = get_byte(ea++);
        process_rm(insn, insn.Op1, postbyte);
        insn.Op2.type = o_reg;
        insn.Op2.reg  = 9; // 9 is CL for IBM PC
        return size_t(ea - insn.ea);
      }
    default:
      return 0;
  }
}

//--------------------------------------------------------------------------
// Return the instruction mnemonics
const char *get_insn_mnem(const insn_t &insn)
{
  if ( insn.itype == NEC_add4s )
    return "add4s";
  return "clear1";
}

//--------------------------------------------------------------------------
// This function can be hooked to various kernel events.
// In this particular plugin we hook to the HT_IDP group.
// As soon the kernel needs to decode and print an instruction, it will
// generate some events that we intercept and provide our own response.
//
// We extend the processor module to disassemble opcode 0x0F
// (This is a hypothetical example)
// There are 2 different possible approaches for the processor extensions:
//  A. Quick & dirty
//       Implement reaction to ev_ana_insn and ev_out_insn.
//       The first checks if the instruction is valid.
//       The second generates its text.
//  B. Thourough and clean
//       Implement all relevant callbacks.
//       ev_ana_insn fills the 'insn' structure.
//       ev_emu_insn creates all xrefs using ua_add_[cd]ref functions.
//       ev_out_insn generates the textual representation of the instruction.
//          It is required only if the instruction requires special processing
//          or the processor module cannot handle the custom instruction for
//          any reason.
//       ev_out_operand generates the operand representation (only if the
//          operand requires special processing).
//       ev_out_mnem generates the instruction mnemonics.
// The main difference between these 2 approaches is in the creation of
// cross-references and the amount of special processing required by the
// new instructions.

// The quick & dirty approach.
// We just produce the instruction mnemonics along with its operands.
// No cross-references are created. No special processing.
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list va)
{
  switch ( code )
  {
    case processor_t::ev_ana_insn:
      {
        insn_t *insn = va_arg(va, insn_t *);
        ea = insn->ea;
        size_t length = ana(*insn);
        if ( length )
        {
          insn->size = (uint16)length;
          return insn->size;       // event processed
        }
      }
      break;
    case processor_t::ev_out_mnem:
      {
        outctx_t *ctx = va_arg(va, outctx_t *);
        const insn_t &insn = ctx->insn;
        if ( insn.itype >= CUSTOM_INSN_ITYPE )
        {
          ctx->out_line(get_insn_mnem(insn), COLOR_INSN);
          return 1;
        }
      }
      break;
#ifdef ENABLE_MERGE
#endif
    case processor_t::ev_privrange_changed:
      // recreate node as it was migrated
      if ( nec_node != BADNODE )
        nec_node.create(node_name);
      break;
  }
  return 0;                     // event is not processed
}

//--------------------------------------------------------------------------
// Initialize the plugin.
// IDA will call this function only once.
// If this function returns nullptr, IDA will unload the plugin.
// Otherwise the plugin returns a pointer to a newly created context structure.
//
// In this example we check the processor type and make the decision.
// You may or may not check any other conditions to decide what you do:
// whether your plugin wants to work with the database or not.

static plugmod_t *idaapi init()
{
  processor_t &ph = PH;
  if ( ph.id != PLFM_386 )
    return nullptr;
  auto plugmod = new plugin_ctx_t;
  set_module_data(&data_id, plugmod);
  return plugmod;
}

//-------------------------------------------------------------------------
plugin_ctx_t::plugin_ctx_t()
{
  nec_node.create(node_name);
  hooked = nec_node.altval(0) != 0;
  if ( hooked )
  {
    hook_event_listener(HT_IDP, this);
    msg("NEC V20 processor extender is enabled\n");
  }
}

//--------------------------------------------------------------------------
// Terminate the plugin.
// This destructor will be called before unloading the plugin.
plugin_ctx_t::~plugin_ctx_t()
{
  clr_module_data(data_id);
  // listeners are uninstalled automatically
  // when the owner module is unloaded
}

//--------------------------------------------------------------------------
// The plugin method
// This is the main function of plugin.
// It will be called when the user selects the plugin from the menu.
// The input argument is usually zero. Non-zero values can be specified
// by using load_and_run_plugin() or through plugins.cfg file (discouraged).
bool idaapi plugin_ctx_t::run(size_t)
{
  if ( hooked )
    unhook_event_listener(HT_IDP, this);
  else
    hook_event_listener(HT_IDP, this);
  hooked = !hooked;
  nec_node.create(node_name);
  nec_node.altset(0, hooked);
  info("AUTOHIDE NONE\n"
       "NEC V20 processor extender now is %s", hooked ? "enabled" : "disabled");
  return true;
}

//--------------------------------------------------------------------------
static const char comment[] = "NEC V20 processor extender";
static const char help[] =
  "A sample plugin module\n"
  "\n"
  "This module shows you how to create plugin modules.\n"
  "\n"
  "It supports some NEC V20 instructions\n"
  "and shows the current address.\n";

//--------------------------------------------------------------------------
// This is the preferred name of the plugin module in the menu system
// The preferred name may be overridden in plugins.cfg file

static const char desired_name[] = "NEC V20 processor extender";

// This is the preferred hotkey for the plugin module
// The preferred hotkey may be overridden in plugins.cfg file

static const char desired_hotkey[] = "";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_PROC           // this is a processor extension plugin
| PLUGIN_MULTI,         // this plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  comment,              // long comment about the plugin. not used.
  help,                 // multiline help about the plugin. not used.
  desired_name,         // the preferred short name of the plugin
  desired_hotkey        // the preferred hotkey to run the plugin
};

ui_requests

This plugin demonstrates the UI requests and the process_ui_action()

/*
* This is a sample plugin to demonstrate the UI requests and the process_ui_action()
* One process_ui_action() can be processed during an UI request.
* The UI request is a nice example to show how to schedule UI actions for sequential execution
*/

#include <ida.hpp>
#include <idp.hpp>
#include <graph.hpp>
#include <loader.hpp>
#include <kernwin.hpp>


//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
  int req_id = 0;

  ~plugin_ctx_t()
  {
    if ( req_id != 0 && cancel_exec_request(req_id) )
      msg("Cancelled unexecuted ui_request\n");
  }

  virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
  class msg_req_t: public ui_request_t
  {
    const char *_msg;
  public:
    msg_req_t(const char *mesg): _msg(qstrdup(mesg)) {}
    ~msg_req_t() { qfree((void *)_msg); }
    virtual bool idaapi run() override
    {
      msg("%s", _msg);
      return false;
    }
  };

  class msgs_req_t: public ui_request_t
  {
    int count;
  public:
    msgs_req_t(int cnt): count(cnt) {}
    virtual bool idaapi run() override
    {
      msg("%d\n", count);
      return --count != 0;
    }
  };

  req_id = execute_ui_requests(
    new msg_req_t("print "),
    new msg_req_t("3 countdown "),
    new msg_req_t("mesages:\n"),
    new msgs_req_t(3),
    nullptr);
  return true;
}


//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
  return new plugin_ctx_t;
}

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  "This is a sample ui_requests plugin.",
                        // long comment about the plugin
  "A sample ui_requests and process_ui_commands plugin",
                        // multiline help about the plugin
  "UI requests demo",   // the preferred short name of the plugin
  "Shift-F8"            // the preferred hotkey to run the plugin
};

More plugin samples

We encourage you to browse the plugins folder inside the SDK directory to check out all of examples, including more complex and advances ones, like FindCrypt program, that finds constants used in crypto algorithms, or qproject, that demonstrates how to fully use the Qt environment in IDA.

Modules samples

The modules folder contains sample processor modules, that are used to add support for new instruction sets or architectures to IDA. Inside the README file you will find instructions on how to compile and run the modules.

If you are going to write a new processor module, it's recommended to follow the below steps:

  1. copy the sample module files to a new directory

  2. edit ins.cpp and ins.hpp files

  3. write the analyser ana.cpp

  4. then outputter

  5. and emulator (you can start with an almost empty emulator)

  6. describe the processor & assembler, write the notify() function

Loaders samples

The ldr folder includes source code for new file format loaders:

  • aif ARM Image File

  • amiga Amige Hunk File

  • aof ARM Object File

  • aout a.out

  • dos MS DOS File

  • dump Memory Dump File

  • geos GEOS File

  • hex Intel/Motorola HEX File

  • hpsom HP SOM

  • intelomf Intel Object File

  • javaldr Java Class Loader

  • mas Macro Assembler

  • nlm Netware Loader Module

  • os9 FLEX/9

  • pef Portable Executable Format (MAC)

  • pilot Palm Pilot

  • qnx Qnx

  • rt11 RT/11

  • w32run Watcom RUN32

ready to be compiled, similarly as processor module samples.

Decompiler SDK sample plugins

The exemplary decompiler plugins are shipped with the C++ SDK and can be found in the plugins folder (vds1-vds20) alongside other plugins.

Below are descriptions of sample decompiler plugins.

Sample 1

This plugin decompiles the current function and prints the result in the message window. It is useful to learn how to initialize a decompiler plugin. Please note that all decompiler sample plugins have the "hexrays_" prefix in their names. This is done to make sure that the decompiler plugins are loaded after the hexrays plugin. Otherwise they would see that the decompiler is missing and immediately terminate.

We recommend you to keep the same naming scheme: please use the "hexrays_" prefix for your decompiler plugins.

N.B.: if you're writing a plugin for non-x86 version of the decompiler, you should use another prefix. For example, the x64 decompiler is named "hexx64", ARM is "hexarm" and so on. To be certain, check IDA's "plugins" directory. To debug plugin loading issues, you can use -z20 switch when running IDA.

Sample 2

This plugin shows how to hook to decompiler events and react to them. It also shows how to visit all ctree elements and modify them.

This plugin waits for the decompilation result to be ready and replaces zeroes in pointer contexts with NULLs. One might say that this is just cosmetic change, but it makes the output more readable.

Since the plugin hooks to events, it is fully automatic. The user can disable it by selecting it from the Edit, Plugins menu.

Sample 3

This plugin shows

- how to add a new popup menu item
- how to map the cursor position to ctree element
- how to modify ctree
- how to make the changes persistent

This is a quite complex plugin but it is thoroughly commented.

Sample 4

This plugin dumps all user-defined information to the message window. Read the source code to learn how to access various user-defined data from your plugins:

- label names
- indented comments
- number formats
- local variable names, types, comments

Sample 5

This plugin generates a graph from the current pseudocode and displays it with wingraph32.

The source code can be used to learn ctree details.

Sample 6

This plugin modifies the decompilation output: removes some space characters.

The source code can be used to learn the output text.

Sample 7

This plugin demonstrates how to use the cblock_t::iterator class. It enumerates all instructions of a block statement.

Sample 8

This plugin demonstrates how to use the udc_filter_t (User-Defined Call generator) class, which allows replacing cryptic function calls, with a simpler/more-readable counterpart.

Sample 9

This plugin demonstrates how to generate microcode for a given function and print it into the output window. It displays fully optimized microcode but it is also possible to retrieve microcode from earlier stages of decompilation. Generating the microcode text should be used only for debugging purposes. Printing microcode in production code may lead to crashes or wrong info.

Sample 10

This plugin installs a custom microcode optimization rule: call !DbgRaiseAssertionFailure fast:.0 => call !DbgRaiseAssertionFailure <fast:"char *" "assertion text">.0

See also sample19 for another example.

Sample 11

This plugin installs a custom inter-block optimization rule:

goto L1     =>        goto L@
...

L1: goto L2

In other words we fix a goto target if it points to a chain of gotos. This improves the decompiler output is some cases.

Sample 12

This plugin displays list of direct references to a register from the current instruction.

Sample 13

This plugin generates microcode for selection and dumps it to the output window.

Sample 14

This plugin shows xrefs to the called function as the decompiler output. All calls are displayed with the call arguments.

Sample 15

This plugin shows list of possible values of a register using the value range analysis.

Sample 16

This plugin installs a custom instruction optimization rule:

mov #N, var.4                  mov #N, var.4
xor var@1.1, #M, var@1.1    => mov #NM, var@1.1
                                 where NM == (N>>8)^M

We need this rule because the decompiler cannot propagate the second byte of VAR into the xor instruction.

The XOR opcode can be replaced by any other, we do not rely on it. Also operand sizes can vary.

Sample 17

This plugin shows how to use "Select offsets" widget (select_udt_by_offset() API). This plugin repeats the Alt-Y functionality.

Sample 18

This plugin 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.

Sample 19

This plugin shows how to install a custom microcode optimization rule. Custom rules are useful to handle obfuscated code. See also sample10 for another example.

Sample 20

This plugin shows how to modify the decompiler output on the fly by adding dynamic comments.

Last updated