Examples

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
{