update to ida 7.6, add builds

This commit is contained in:
2021-10-31 21:20:46 +02:00
parent e0e0f2be99
commit b1809fe2d9
1408 changed files with 279193 additions and 302468 deletions

View File

@@ -0,0 +1,124 @@
/*
* 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 NULL.
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
};

View File

@@ -0,0 +1,20 @@
PROC=callee
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)callee$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)dbg.hpp $(I)fixup.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp \
$(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
\
../../module/arm/../idaidp.hpp \
\
\
../../module/arm/arm.hpp \
callee.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
#ifndef __CALLGRAPH__06192009__
#define __CALLGRAPH__06192009__
#include <deque>
#include <ida.hpp>
#include <idp.hpp>
#include <name.hpp>
#include <bytes.hpp>
#include <graph.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <demangle.hpp>
#define MAX_CALLERS_LEVEL 10
#define FIELD_ID_STRINGS 1
#define FIELD_ID_LIBS 2
#define FIELD_ID_FATHERS 3
#define FIELD_ID_CHILDS 4
#define FIELD_ID_CHILDS_LEVEL 6
#define VERTEX_HIDDEN_NODES -1
struct plugin_ctx_t;
typedef std::deque<int> int_queue_t;
typedef std::map<ea_t, int> ea_int_map_t;
//--------------------------------------------------------------------------
struct funcs_walk_options_t
{
int32 version;
#define FWO_VERSION 1 // current version of options block
int32 flags;
#define FWO_SHOWSTRING 0x0001 // show string references
#define FWO_SKIPLIB 0x0002 // skip library functions
#define FWO_CALLEE_RECURSE_UNLIM 0x0004 // unlimited callees recursion
int32 callees_recurse_limit; // how deep to recurse callees (0 = unlimited)
int32 callers_recurse_limit; // how deep to recurse callers (0 = unlimited)
int32 max_nodes; // maximum number of nodes per level
};
class graph_info_t;
//--------------------------------------------------------------------------
// function call graph creator class
class callgraph_t
{
public:
plugin_ctx_t &ctx;
private:
int node_count = 0;
// node id to func addr and reverse lookup
typedef std::map<int, ea_t> int_ea_map_t;
int_ea_map_t node2ea;
// current node search ptr
int cur_node = 0;
char cur_text[MAXSTR];
bool visited(ea_t func_ea, int *nid);
int add(ea_t func_ea);
public:
ea_int_map_t ea2node;
// edge structure
struct edge_t
{
int id1;
int id2;
edge_t(int i1, int i2): id1(i1), id2(i2) {}
edge_t(): id1(0), id2(0) {}
};
typedef qlist<edge_t> edges_t;
// edge manipulation
typedef edges_t::iterator edge_iterator;
void create_edge(int id1, int id2);
edge_iterator begin_edges() { return edges.begin(); }
edge_iterator end_edges() { return edges.end(); }
void clear_edges();
// find nodes by text
int find_first(const char *text);
int find_next();
const char *get_findtext() { return cur_text; }
callgraph_t(plugin_ctx_t &ctx);
int count() const { return node_count; }
void reset();
// node / func info
struct funcinfo_t
{
qstring name;
bgcolor_t color;
ea_t ea;
qstring strings;
};
typedef std::map<int, funcinfo_t> int_funcinfo_map_t;
int_funcinfo_map_t cached_funcs;
funcinfo_t *get_info(int nid);
// function name manipulation
ea_t get_addr(int nid) const;
const char *get_name(int nid);
int walk_func(eavec_t *hide_nodes, func_t *func, funcs_walk_options_t *o=NULL, int level=1);
void add_fathers(func_t *func, ea_t func_start, int id, funcs_walk_options_t *opt, int level);
bool navigate(graph_info_t *gi, ea_t addr) const;
void go_back(graph_info_t *gi) const;
void go_forward(graph_info_t *gi) const;
bool options(graph_info_t *gi) const;
bool refresh(graph_info_t *gi) const;
bool jumpxref(graph_info_t *gi) const;
bool jumpaddr(graph_info_t *gi) const;
bool jump(const graph_info_t *gi) const;
bool back(graph_info_t *gi) const;
bool forward(graph_info_t *gi) const;
bool center(graph_info_t *gi) const;
bool select(const graph_info_t *gi) const;
bool home(const graph_info_t *gi) const;
bool searchfirst(graph_info_t *gi);
bool searchnext(graph_info_t *gi);
bool hidenode(graph_info_t *gi) const;
bool showhidden(graph_info_t *gi) const;
bool showall(graph_info_t *gi) const;
static ssize_t idaapi gr_callback(void *ud, int code, va_list va);
static void idaapi user_refresh(void *ud, int code, va_list va, int current_node);
private:
edges_t edges;
};
//--------------------------------------------------------------------------
DECLARE_LISTENER(view_listener_t, plugin_ctx_t, ctx);
DECLARE_LISTENER(idp_gi_listener_t, graph_info_t, gi);
DECLARE_LISTENER(idb_gi_listener_t, graph_info_t, gi);
struct idp_merge_listener_t : public event_listener_t
{
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};
//--------------------------------------------------------------------------
// Per function call graph context
typedef qlist<class graph_info_t *> graphinfo_list_t;
class graph_info_t
{
plugin_ctx_t &ctx;
idp_gi_listener_t idp_gi_listener = idp_gi_listener_t(*this);
idb_gi_listener_t idb_gi_listener = idb_gi_listener_t(*this);
public:
typedef graphinfo_list_t::iterator iterator;
callgraph_t fg; // associated call graph maker
graph_viewer_t *gv = nullptr; // associated graph_view
TWidget *widget = nullptr; // associated widget
ea_t func_ea = BADADDR; // function ea in question
qstring title; // the title
int_queue_t queue;
int_queue_t forward_queue;
eavec_t hide_nodes;
private:
bool refresh_needed = true; // schedule a refresh
graph_info_t(plugin_ctx_t &_ctx) : ctx(_ctx), fg(_ctx) {}
static bool find(plugin_ctx_t &ctx, ea_t func_ea, iterator *out);
public:
static graph_info_t *find(plugin_ctx_t &ctx, ea_t func_ea);
static graph_info_t *find(plugin_ctx_t &ctx, const char *title);
static graph_info_t *find(plugin_ctx_t &ctx, const graph_viewer_t *v);
static graph_info_t *create(plugin_ctx_t &ctx, ea_t func_ea);
static void destroy_graph(plugin_ctx_t &ctx, graph_info_t *gi);
void install_hooks();
void remove_hooks();
void mark_for_refresh(void);
void mark_as_refreshed(void);
void refresh(void);
bool is_refresh_needed(void) const { return refresh_needed; }
};
//-------------------------------------------------------------------------
// The main action to invoke the plugin
struct show_callgraph_ah_t : public action_handler_t
{
plugin_ctx_t &ctx;
show_callgraph_ah_t(plugin_ctx_t &_ctx) : ctx(_ctx) {}
virtual int idaapi activate(action_activation_ctx_t *) override;
virtual action_state_t idaapi update(action_update_ctx_t *) override
{
return AST_ENABLE_ALWAYS;
}
};
//-------------------------------------------------------------------------
// Base class for additional actions in the graph view
struct cg_ah_t : public action_handler_t
{
plugin_ctx_t &plg;
cg_ah_t(plugin_ctx_t &p) : plg(p) {}
virtual int act(graph_info_t *gi) = 0;
virtual int idaapi activate(action_activation_ctx_t *ctx) override
{
graph_info_t *gi = graph_info_t::find(plg, (graph_viewer_t *) ctx->widget);
return gi != nullptr ? act(gi) : 0;
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override
{
return graph_info_t::find(plg, (graph_viewer_t *) ctx->widget) != nullptr
? AST_ENABLE_FOR_WIDGET
: AST_DISABLE_FOR_WIDGET;
}
};
//-------------------------------------------------------------------------
#define DECLARE_ACTION_HANDLER(Method) \
struct Method##_ah_t : public cg_ah_t \
{ \
Method##_ah_t(plugin_ctx_t &p) : cg_ah_t(p) {} \
virtual int act(graph_info_t *gi) override \
{ \
return int(gi->fg.Method(gi)); \
} \
}
DECLARE_ACTION_HANDLER(options);
DECLARE_ACTION_HANDLER(refresh);
DECLARE_ACTION_HANDLER(jumpxref);
DECLARE_ACTION_HANDLER(jumpaddr);
DECLARE_ACTION_HANDLER(jump);
DECLARE_ACTION_HANDLER(back);
DECLARE_ACTION_HANDLER(forward);
DECLARE_ACTION_HANDLER(center);
DECLARE_ACTION_HANDLER(select);
DECLARE_ACTION_HANDLER(home);
DECLARE_ACTION_HANDLER(searchfirst);
DECLARE_ACTION_HANDLER(searchnext);
DECLARE_ACTION_HANDLER(hidenode);
DECLARE_ACTION_HANDLER(showhidden);
DECLARE_ACTION_HANDLER(showall);
#undef DECLARE_ACTION_HANDLER
//--------------------------------------------------------------------------
#define PROCMOD_NODE_NAME "$ proximity browser"
struct plugin_ctx_t : public plugmod_t
{
view_listener_t view_listener = view_listener_t(*this);
idp_merge_listener_t idp_merge_listener;
show_callgraph_ah_t show_callgraph_ah = show_callgraph_ah_t(*this);
const action_desc_t main_action;
#define DEFINE_ACTION_HANDLER(Method) Method##_ah_t Method##_ah = Method##_ah_t(*this) //lint !e773 macro not parenthized
DEFINE_ACTION_HANDLER(options);
DEFINE_ACTION_HANDLER(refresh);
DEFINE_ACTION_HANDLER(jumpxref);
DEFINE_ACTION_HANDLER(jumpaddr);
DEFINE_ACTION_HANDLER(jump);
DEFINE_ACTION_HANDLER(back);
DEFINE_ACTION_HANDLER(forward);
DEFINE_ACTION_HANDLER(center);
DEFINE_ACTION_HANDLER(select);
DEFINE_ACTION_HANDLER(home);
DEFINE_ACTION_HANDLER(searchfirst);
DEFINE_ACTION_HANDLER(searchnext);
DEFINE_ACTION_HANDLER(hidenode);
DEFINE_ACTION_HANDLER(showhidden);
DEFINE_ACTION_HANDLER(showall);
#undef DEFINE_ACTION_HANDLER
const action_desc_t actions[15] =
{
#define ROW(Method, Label, Shortcut) \
ACTION_DESC_LITERAL_PLUGMOD("callgraph:" #Method, Label, &Method##_ah, this, Shortcut, nullptr, -1)
ROW(options, "Options", "O"), // 1
ROW(refresh, "Refresh", "R"), // 2
ROW(jumpxref, "Jump to xref", "X"), // 3
ROW(jumpaddr, "Jump to address", "G"), // 4
ROW(jump, "Jump to function", "SPACE"), // 5
ROW(back, "Jump to previous node", "ESC"), // 6
ROW(forward, "Jump to next node", "Ctrl+Enter"), // 7
ROW(center, "Center node", "Enter"), // 8
ROW(select, "Select node", "Ctrl+L"), // 9
ROW(home, "Goto to first node", "H"), // 0
ROW(searchfirst, "Search first", "S"), // 1
ROW(searchnext, "Search next", "N"), // 2
ROW(hidenode, "Hide selected node", nullptr), // 3
ROW(showhidden, "Show hidden node", nullptr), // 4
ROW(showall, "Show all nodes", nullptr), // 5
#undef ROW
};
graphinfo_list_t instances;
funcs_walk_options_t fg_opts =
{
FWO_VERSION, // version
FWO_CALLEE_RECURSE_UNLIM, // flags
2, // max callees recursion
1, // max callers recursion
255 // max nodes per level
};
bool actions_registered = false;
qstring last_text; //lint !e958 padding // see findfirst_node()
virtual bool idaapi run(size_t arg) override;
plugin_ctx_t();
bool register_main_action();
~plugin_ctx_t();
qstring gen_graph_title(ea_t ea);
bool load_options();
void save_options();
bool show_options();
void ensure_actions_registered();
};
extern int data_id;
#endif

View File

@@ -0,0 +1,15 @@
PROC=callgraph
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)callgraph$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)demangle.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)gdl.hpp $(I)graph.hpp $(I)ida.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp \
$(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
callgraph.cpp callgraph.h

View File

@@ -0,0 +1,189 @@
/*
* 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(NULL, &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 == NULL )
{
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
""
};

View File

@@ -0,0 +1,11 @@
PROC=choose
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)choose$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
choose.cpp

View File

@@ -0,0 +1,177 @@
/* Custom data type sample plugin.
* Copyright (c) 2010-2021 Hex-Rays, support@hex-rays.com
* Feel free to do whatever you want with this code.
*
* This sample plugin demonstates how to install a custom data type
* and a custom data format in IDA v5.7
*
* Custom data types can be used to create your own data types.
* A custom data type basically defines the data size. It can be fixed
* or variable. This plugin defines a variable size data type: a pascal
* string. Pascal strings start with a count byte:
* db len, '....'
*
* Custom data formats are used to render data values on the screen.
* Multiple data formats can be registered for a custom data type.
* The data formats with non-NULL menu_names will be listed in the 'Operand type'
* menu and the user will be able to select them.
*
*/
#include <ida.hpp>
#include <idp.hpp>
#include <struct.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
struct plugin_ctx_t : public plugmod_t
{
int psid = 0; // id of the 'pascal string' data type
int psfid = 0; // id of the 'pascal string' data format
plugin_ctx_t();
~plugin_ctx_t();
virtual bool idaapi run(size_t) override { return false; }
};
//---------------------------------------------------------------------------
// We define a variable size data type. For fixed size types this function
// must be omitted.
static asize_t idaapi calc_pascal_string_length(void *, ea_t ea, asize_t maxsize)
{
if ( is_member_id(ea) )
{ // Custom data types may be used in structure definitions. If this case
// ea is a member id. Check for this situation and return 1
return 1;
}
ushort n = get_byte(ea);
if ( n+1 > maxsize )
return 0; // string would be too big
return n+1; // ok, we accept any pascal string
}
//---------------------------------------------------------------------------
// Definition of the data type
static const data_type_t pascal_string_type =
{
sizeof(data_type_t), // size of this structure
NULL, // user defined data
0, // properties
"pascal_string", // internal name of the data type
// must be unique for the current database
"Pascal string", // Menu name. If NULL, the type won't be visible in the Edit menu.
NULL, // Hotkey
"pstr", // Keyword to use in the assembly listing
2, // value size. For varsize types, specify the
// minimal size of the value
NULL, // may_create_at? NULL means the type can be created anywhere
calc_pascal_string_length, // for varsize types: calculate the exact size of an item
};
//---------------------------------------------------------------------------
// Print contents of a pascal string
static bool idaapi print_pascal_string(
void *, // user defined data, not used here
qstring *out, // output buffer. may be NULL
const void *value, // value to print. may not be NULL
asize_t size, // value size in bytes
ea_t, // current ea
int, // operand number
int) // data type id
{
const char *vptr = (const char *)value;
int n = *vptr++;
if ( n+1 > size )
return false;
if ( out != NULL )
{
*out = "\"";
for ( int i=0; i < n; i++ )
{
if ( qisprint(*vptr) )
out->append(*vptr++);
else
out->cat_sprnt("\\x%02X", uchar(*vptr++));
}
out->append('"');
}
return true;
}
//---------------------------------------------------------------------------
// Definition of the data format
static const data_format_t pascal_string_format =
{
sizeof(data_format_t), // size of this structure
NULL, // user defined data
0, // properties
"pascal_string", // internal name of the data format
NULL, // Menu name of the format. NULL means 'do not create menu item'
NULL, // Hotkey
0, // value size. 0 means that this format accepts any value size
0, // Text width of the value. Unknown, specify 0
print_pascal_string, // callback to render colored text for the data
NULL, // scan
NULL, // analyze
};
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
processor_t &ph = PH;
if ( ph.id != PLFM_386 )
return nullptr;
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
plugin_ctx_t::plugin_ctx_t()
{
// Register custom data type
psid = register_custom_data_type(&pascal_string_type);
// Register custom data format for it
psfid = register_custom_data_format(&pascal_string_format);
attach_custom_data_format(psid, psfid);
}
//--------------------------------------------------------------------------
plugin_ctx_t::~plugin_ctx_t()
{
// unregister data type
if ( psid > 0 )
{
// IDA would detach the data format automatically.
unregister_custom_data_type(psid);
}
// unregister data format
if ( psfid > 0 )
unregister_custom_data_format(psfid);
}
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_PROC // plugin flags
// we want the plugin to load as soon as possible
// immediately after the processor module
|PLUGIN_HIDE // we want to hide the plugin because it there will
// be a menu item in the Edit submenu
|PLUGIN_MULTI, // this plugin can work with multiple idbs in parallel
init, // initialize
nullptr,
nullptr,
"", // long comment about the plugin
// it could appear in the status line
// or as a hint
"", // multiline help about the plugin
"Sample custdata", // the preferred short name of the plugin
"" // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,11 @@
PROC=custdata
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)custdata$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)struct.hpp $(I)ua.hpp \
$(I)xref.hpp custdata.cpp

View File

@@ -0,0 +1,282 @@
/* Custom viewer sample plugin.
* Copyright (c) 2007 by Ilfak Guilfanov, ig@hexblog.com
* Feel free to do whatever you want with this code.
*
* This sample plugin demonstates how to create and manipulate a simple
* custom viewer in IDA v5.1
*
* Custom viewers allow you to create a view which displays colored lines.
* These colored lines are dynamically created by callback functions.
*
* Custom viewers are used in IDA itself to display
* the disassembly listng, structure, and enumeration windows.
*
* This sample plugin just displays several sample lines on the screen.
* It displays a hint with the current line number.
* The right-click menu contains one sample command.
* It reacts to one hotkey.
*
* This plugin uses the simpleline_place_t class for the locations.
* Custom viewers can use any decendant of the place_t class.
* The place_t is responsible for supplying data to the viewer.
*/
//---------------------------------------------------------------------------
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#define ACTION_NAME "custview:SampleMenuItem"
struct plugin_ctx_t;
struct sample_action_t : public action_handler_t
{
plugin_ctx_t &plg;
sample_action_t(plugin_ctx_t &p) : plg(p) {}
virtual int idaapi activate(action_activation_ctx_t *) override;
virtual action_state_t idaapi update(action_update_ctx_t *) override
{
return AST_ENABLE_ALWAYS;
}
};
//-------------------------------------------------------------------------
static struct
{
const char *text;
bgcolor_t color;
} const sample_text[] =
{
{ "This is a sample text", 0xFFFFFF },
{ "It will be displayed in the custom view", 0xFFC0C0 },
{ COLSTR("This line will be colored as erroneous", SCOLOR_ERROR), 0xC0FFC0 },
{ COLSTR("Every", SCOLOR_AUTOCMT) " "
COLSTR("word", SCOLOR_DNAME) " "
COLSTR("can", SCOLOR_IMPNAME) " "
COLSTR("be", SCOLOR_NUMBER) " "
COLSTR("colored!", SCOLOR_EXTRA), 0xC0C0FF },
{ " No limit on the number of lines.", 0xC0FFFF },
};
// Structure to keep all information about the our sample view
struct sample_info_t
{
TWidget *cv;
strvec_t sv;
sample_info_t() : cv(NULL) {}
};
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
sample_info_t *si = NULL;
const sample_info_t *last_si = NULL;
sample_action_t sample_ah = sample_action_t(*this);
const action_desc_t sample_action = ACTION_DESC_LITERAL_PLUGMOD(
ACTION_NAME,
"Sample menu item",
&sample_ah,
this,
"N",
NULL,
-1);
virtual bool idaapi run(size_t) override;
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};
//---------------------------------------------------------------------------
// get the word under the (keyboard or mouse) cursor
static bool get_current_word(TWidget *v, bool mouse, qstring &word)
{
// query the cursor position
int x, y;
if ( get_custom_viewer_place(v, mouse, &x, &y) == NULL )
return false;
// query the line at the cursor
qstring buf;
tag_remove(&buf, get_custom_viewer_curline(v, mouse));
if ( x >= buf.length() )
return false;
// find the beginning of the word
char *ptr = buf.begin() + x;
while ( ptr > buf.begin() && !qisspace(ptr[-1]) )
ptr--;
// find the end of the word
char *begin = ptr;
ptr = buf.begin() + x;
while ( !qisspace(*ptr) && *ptr != '\0' )
ptr++;
word = qstring(begin, ptr-begin);
return true;
}
//---------------------------------------------------------------------------
int idaapi sample_action_t::activate(action_activation_ctx_t *)
{
qstring word;
if ( !get_current_word(plg.last_si->cv, false, word) )
return 0;
info("The current word is: %s", word.c_str());
return 1;
}
//---------------------------------------------------------------------------
// Keyboard callback
static bool idaapi ct_keyboard(TWidget * /*v*/, int key, int shift, void *ud)
{
if ( shift == 0 )
{
sample_info_t *si = (sample_info_t *)ud;
switch ( key )
{
case 'N':
warning("The hotkey 'N' has been pressed");
return true;
case IK_ESCAPE:
close_widget(si->cv, WCLS_SAVE | WCLS_CLOSE_LATER);
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
// This callback will be called each time the keyboard cursor position
// is changed
static void idaapi ct_curpos(TWidget *v, void *)
{
qstring word;
if ( get_current_word(v, false, word) )
msg("Current word is: %s\n", word.c_str());
}
//--------------------------------------------------------------------------
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list va)
{
switch ( code )
{
// how to implement a simple hint callback
case ui_get_custom_viewer_hint:
{
qstring &hint = *va_arg(va, qstring *);
TWidget *viewer = va_arg(va, TWidget *);
place_t *place = va_arg(va, place_t *);
int *important_lines = va_arg(va, int *);
if ( si->cv == viewer ) // our viewer
{
if ( place == NULL )
return 0;
simpleline_place_t *spl = (simpleline_place_t *)place;
hint.cat_sprnt("Hint for line %u\n", spl->n);
*important_lines += 1;
}
break;
}
case ui_widget_invisible:
{
TWidget *f = va_arg(va, TWidget *);
if ( f == si->cv )
{
delete si;
si = nullptr;
unhook_event_listener(HT_UI, this);
}
}
break;
case ui_populating_widget_popup:
{
TWidget *f = va_arg(va, TWidget *);
if ( f == si->cv )
{
TPopupMenu *p = va_arg(va, TPopupMenu *);
// Create right-click menu on the fly
attach_action_to_popup(f, p, ACTION_NAME);
}
}
break;
}
return 0;
}
//-------------------------------------------------------------------------
static const custom_viewer_handlers_t handlers(
ct_keyboard,
NULL, // popup
NULL, // mouse_moved
NULL, // click
NULL, // dblclick
ct_curpos,
NULL, // close
NULL, // help
NULL);// adjust_place
//---------------------------------------------------------------------------
// Create a custom view window
bool idaapi plugin_ctx_t::run(size_t)
{
TWidget *widget = find_widget("Sample custom view");
if ( widget != NULL )
{
activate_widget(widget, true);
return true;
}
// allocate block to hold info about our sample view
si = new sample_info_t();
last_si = si;
// prepare the data to display. we could prepare it on the fly too.
// but for that we have to use our own custom place_t class decendant.
for ( int i=0; i < qnumber(sample_text); i++ )
{
si->sv.push_back(simpleline_t("")); // add empty line
si->sv.push_back(simpleline_t(sample_text[i].text));
si->sv.back().bgcolor = sample_text[i].color;
}
// create two place_t objects: for the minimal and maximal locations
simpleline_place_t s1;
simpleline_place_t s2(si->sv.size()-1);
// create a custom viewer
si->cv = create_custom_viewer("Sample custom view", &s1, &s2, &s1, NULL, &si->sv, &handlers, si);
// also set the ui event callback
hook_event_listener(HT_UI, this);
// finally display the form on the screen
display_widget(si->cv, WOPN_DP_TAB|WOPN_RESTORE);
// Register the action. This one will be attached
// live, to the popup menu.
register_action(sample_action);
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_MULTI, // plugin flags
init, // initialize
nullptr,
nullptr,
"", // long comment about the plugin
"", // multiline help about the plugin
"Sample custview", // the preferred short name of the plugin
"" // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,11 @@
PROC=custview
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)custview$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
custview.cpp

View File

@@ -0,0 +1,171 @@
//-------------------------------------------------------------------------
// Mimic GDB's debug file-seeking mechanism. Let:
// PATH be for-each components of the full DWARF_DEBUG_FILE_DIRECTORY.
// BIN be the full path to the binary file.
// DLINK the value of the section ".gnu_debuglink"
// BLDID build ID from note section GNU/3
// BLDCAR the lowercase hex-formatted value of the first byte of BLDID
// BLDCDR the lowercase hex-formatted value of the remaining bytes of BLDID
//
// 1) Look for file by build ID
// foreach PATH in DWARF_DEBUG_FILE_DIRECTORY:
// if $PATH/.build-id/$BLDCAR/$BLDCRD.debug exists and matches:
// found!
// 2) If not found, look for file by debug link
// if dir($BIN)/$DLINK exists and matches:
// found!
// if dir($BIN)/.debug/$DLINK exists and matches:
// found!
// foreach PATH in DWARF_DEBUG_FILE_DIRECTORY:
// if $PATH/dir($BIN)/$DLINK exists and matches:
// found!
class debug_info_file_visitor_t
{
public:
char fullpath[MAXSTR]; // current path
enum checkmethod_t
{
BUILDID,
DEBUGLINK
};
// visit fullpath
// returns 0 - continue to search
virtual int visit_fullpath(checkmethod_t check_method)
{
int code = 0;
if ( qfileexist(fullpath) )
{
code = 1;
if ( check_method == DEBUGLINK )
code = check_debuglink_crc32() ? 1 : 0;
}
return code;
}
int call_visit_fullpath(checkmethod_t check_method)
{
int code = visit_fullpath(check_method);
debugout("debug_info_file_visitor_t::visit_fullpath(check_method=%s), fullpath=%s => %d\n",
check_method == BUILDID ? "BUILDID" : "DEBUGLINK",
fullpath,
code);
return code;
}
debug_info_file_visitor_t(
const char *_glbl_deb_dirs, // global debug directories
bool from_envvar, // taken from environment variable
const char *_path_to_binary, // binary's absolute file name
const char *_debuglink, // name of the separate debug info file
uint32 _debuglink_crc32, // CRC32 of the separate debug info file
const char *_buildid) // build ID
: path_to_binary(_path_to_binary),
debuglink(_debuglink),
buildid(_buildid),
debuglink_crc32(_debuglink_crc32)
{
fullpath[0] = '\0';
const char *sep = from_envvar ? DELIMITER : ";"; //-V583
char buf[QMAXPATH];
qstrncpy(buf, _glbl_deb_dirs, sizeof(buf));
char *saved_ptr;
char *p = qstrtok(buf, sep, &saved_ptr);
while ( p != NULL )
{
glbl_deb_dirs.push_back(p);
p = qstrtok(NULL, sep, &saved_ptr);
}
}
virtual ~debug_info_file_visitor_t() {}
// accept visitor
// stop searching if visitor returns non-zero,
// returns visitor's result
int accept(void)
{
int code = 0;
// Look for file by build ID
if ( !glbl_deb_dirs.empty() && !buildid.empty() )
{
// looks in the .build-id subdirectory of each one of the global debug directories
// for a file named nn/nnnnnnnn.debug,
// where nn are the first 2 hex characters of the build ID bit string,
// and nnnnnnnn are the rest of the bit string
qstring bid_car(buildid.c_str(), 2);
qstring bid_cdr(buildid.c_str() + 2);
bid_cdr.append(".debug");
for ( qstrvec_t::const_iterator p=glbl_deb_dirs.begin(); p != glbl_deb_dirs.end(); ++p )
{
qmakepath(fullpath, sizeof(fullpath), p->c_str(), ".build-id", bid_car.c_str(), bid_cdr.c_str(), NULL);
code = call_visit_fullpath(BUILDID);
if ( code != 0 )
goto END;
}
}
// If not found, look for file by debug link
if ( !debuglink.empty() )
{
char bindir[QMAXPATH];
if ( qdirname(bindir, sizeof(bindir), path_to_binary.c_str()) )
{
// in the directory of the executable file
qmakepath(fullpath, sizeof(fullpath), bindir, debuglink.c_str(), NULL);
code = call_visit_fullpath(DEBUGLINK);
if ( code != 0 )
goto END;
// then in a subdirectory of that directory named .debug
qmakepath(fullpath, sizeof(fullpath), bindir, ".debug", debuglink.c_str(), NULL);
code = call_visit_fullpath(DEBUGLINK);
if ( code != 0 )
goto END;
// and finally under each one of the global debug directories,
// in a subdirectory whose name is identical to the leading directories
// of the executable's absolute file name
for ( qstrvec_t::const_iterator p=glbl_deb_dirs.begin(); p != glbl_deb_dirs.end(); ++p )
{
qmakepath(fullpath, sizeof(fullpath), p->c_str(), bindir, debuglink.c_str(), NULL);
code = call_visit_fullpath(DEBUGLINK);
if ( code != 0 )
goto END;
}
}
}
END:
return code;
}
// check debuglink file CRC32
bool check_debuglink_crc32(void)
{
linput_t *li = open_linput(fullpath, false);
uint32 crc32 = calc_file_crc32(li);
close_linput(li);
return debuglink_crc32 == crc32;
}
private:
qstrvec_t glbl_deb_dirs;
const qstring path_to_binary;
const qstring debuglink;
const qstring buildid;
uint32 debuglink_crc32;
AS_PRINTF(2, 3) void debugout(const char *format, ...)
{
if ( (debug & LOOK_FOR_DEBUG_FILE_DEBUG_FLAG) != 0 )
{
va_list va;
va_start(va, format);
vmsg(format, va);
va_end(va);
}
}
};
#undef DIFV_DEB

View File

@@ -0,0 +1,143 @@
// 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);
hook_event_listener(HT_IDP, &idp_listener);
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(NULL, idc, "main", NULL, 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 != NULL )
{
// 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
};

View File

@@ -0,0 +1,12 @@
PROC=ex_debidc
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)ex_debidc$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp \
$(I)nalt.hpp $(I)netnode.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)ua.hpp $(I)xref.hpp ex_debidc.cpp

View File

@@ -0,0 +1,85 @@
/*
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
};

View File

@@ -0,0 +1,11 @@
PROC=ex_events1
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)ex_events1$(O): $(I)allins.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ex_events1.cpp

View File

@@ -0,0 +1,4 @@
{
global: PLUGIN;
local: *;
};

View File

@@ -0,0 +1,166 @@
/*
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 != NULL )
*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 != NULL )
*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 != NULL )
*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
};

View File

@@ -0,0 +1,11 @@
PROC=extlang
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)extlang$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)expr.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp extlang.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,266 @@
// FindCrypt - find constants used in crypto algorithms
// Copyright 2006 Ilfak Guilfanov <ig@hexblog.com>
// This is a freeware program.
// This copyright message must be kept intact.
// This plugin looks for constant arrays used in popular crypto algorithms.
// If a crypto algorithm is found, it will rename the appropriate locations
// of the program and put bookmarks on them.
// Version 2.0
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <bytes.hpp>
#include <name.hpp>
#include <moves.hpp>
#include <auto.hpp>
#include "findcrypt.hpp"
// #define VERIFY_CONSTANTS 1
//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
plugin_ctx_t()
{
// agree to work with any database
#ifndef TESTABLE_BUILD
hook_event_listener(HT_IDP, this);
#endif
}
~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;
};
//--------------------------------------------------------------------------
// retrieve the first byte of the specified array
// take into account the byte sex
inline uchar get_first_byte(const array_info_t *a)
{
const uchar *ptr = (const uchar *)a->array;
if ( !inf_is_be() )
return ptr[0];
return ptr[a->elsize-1];
}
//--------------------------------------------------------------------------
// check that all constant arrays are distinct (no duplicates)
//lint -e528 not used
#ifdef VERIFY_CONSTANTS
static void verify_constants(const array_info_t *consts)
{
typedef std::set<qstring> strset_t;
strset_t myset;
for ( const array_info_t *ptr=consts; ptr->size != 0; ptr++ )
{
qstring s((char*)ptr->array, ptr->size);
if ( !myset.insert(s).second )
error("duplicate array %s!", ptr->name);
}
}
#endif
//--------------------------------------------------------------------------
// match a constant array against the database at the specified address
static bool match_array_pattern(ea_t ea, const array_info_t *ai)
{
uchar *ptr = (uchar *)ai->array;
for ( size_t i=0; i < ai->size; i++ )
{
switch ( ai->elsize )
{
case 1:
if ( get_byte(ea) != *(uchar*)ptr )
return false;
break;
case 2:
if ( get_word(ea) != *(ushort*)ptr )
return false;
break;
case 4:
if ( get_dword(ea) != *(uint32*)ptr )
return false;
break;
case 8:
if ( get_qword(ea) != *(uint64*)ptr )
return false;
break;
default:
error("interr: unexpected array '%s' element size %" FMT_Z,
ai->name, ai->elsize);
}
ptr += ai->elsize;
ea += ai->elsize;
}
return true;
}
//--------------------------------------------------------------------------
// match a sparse array against the database at the specified address
// NB: all sparse arrays must be word32!
static bool match_sparse_pattern(ea_t ea, const array_info_t *ai)
{
const word32 *ptr = (const word32*)ai->array;
if ( get_dword(ea) != *ptr++ )
return false;
ea += 4;
for ( size_t i=1; i < ai->size; i++ )
{
word32 c = *ptr++;
if ( inf_is_be() )
c = swap32(c);
// look for the constant in the next N bytes
const size_t N = 64;
uchar mem[N+4];
memset(mem, 0xFF, sizeof(mem));
get_bytes(mem, sizeof(mem), ea);
int j;
for ( j=0; j < N; j++ )
if ( *(uint32*)(mem+j) == c )
break;
if ( j == N )
return false;
ea += j + 4;
}
return true;
}
//--------------------------------------------------------------------------
// mark a location with the name of the algorithm
// use the first free slot for the marker
static void mark_location(ea_t ea, const char *name)
{
idaplace_t ipl(ea, 0);
renderer_info_t rinfo;
rinfo.rtype = TCCRT_FLAT;
rinfo.pos.cx = 0;
rinfo.pos.cy = 5;
lochist_entry_t e(&ipl, rinfo);
uint32 i, n = bookmarks_t::size(e, NULL);
for ( i = 0; i < n; ++i )
{
qstring desc;
lochist_entry_t loc(e);
if ( !bookmarks_t::get(&loc, &desc, &i, NULL) )
break;
// reuse old "Crypto: " slots
if ( strneq(desc.c_str(), "Crypto: ", 7) && loc.place()->toea() == ea )
break;
}
qstring buf;
buf.sprnt("Crypto: %s", name);
bookmarks_t::mark(e, i, NULL, buf.c_str(), NULL);
}
//--------------------------------------------------------------------------
// try to find constants at the given address range
static void recognize_constants(ea_t ea1, ea_t ea2)
{
int count = 0;
show_wait_box("Searching for crypto constants...");
for ( ea_t ea=ea1; ea < ea2; ea=next_addr(ea) )
{
if ( (ea % 0x1000) == 0 )
{
show_addr(ea);
if ( user_cancelled() )
break;
}
uchar b = get_byte(ea);
// check against normal constants
for ( const array_info_t *ptr=non_sparse_consts; ptr->size != 0; ptr++ )
{
if ( b != get_first_byte(ptr) )
continue;
if ( match_array_pattern(ea, ptr) )
{
msg("%a: found const array %s (used in %s)\n", ea, ptr->name, ptr->algorithm);
mark_location(ea, ptr->algorithm);
force_name(ea, ptr->name);
count++;
break;
}
}
// check against sparse constants
for ( const array_info_t *ptr=sparse_consts; ptr->size != 0; ptr++ )
{
if ( b != get_first_byte(ptr) )
continue;
if ( match_sparse_pattern(ea, ptr) )
{
msg("%a: found sparse constants for %s\n", ea, ptr->algorithm);
mark_location(ea, ptr->algorithm);
count++;
break;
}
}
}
hide_wait_box();
if ( count != 0 )
msg("Found %d known constant arrays in total.\n", count);
}
//--------------------------------------------------------------------------
// This callback is called for IDP notification events
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list /*va*/)
{
if ( code == processor_t::ev_newfile ) // a new file has been loaded
recognize_constants(inf_get_min_ea(), inf_get_max_ea());
return 0;
}
//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
ea_t ea1;
ea_t ea2;
read_range_selection(NULL, &ea1, &ea2); // if fails, inf.min_ea and inf.max_ea will be used
recognize_constants(ea1, ea2);
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
#ifdef VERIFY_CONSTANTS
verify_constants(non_sparse_consts);
verify_constants(sparse_consts);
#endif
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
static const char help[] = "Find crypt v2";
static const char comment[] = "Find crypt v2";
static const char wanted_name[] = "Find crypt v2";
static const char wanted_hotkey[] = "";
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_PROC // Load plugin when a processor module is loaded
| 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
};

View File

@@ -0,0 +1,31 @@
#define IS_LITTLE_ENDIAN
#if defined(__GNUC__) || defined(__MWERKS__)
#define WORD64_AVAILABLE
typedef unsigned long long word64;
typedef unsigned long word32;
typedef unsigned char byte;
#define W64LIT(x) x##LL
#elif defined(_MSC_VER) || defined(__BCPLUSPLUS__)
#define WORD64_AVAILABLE
typedef unsigned __int64 word64;
typedef unsigned __int32 word32;
typedef unsigned __int8 byte;
#define W64LIT(x) x##ui64
#endif
struct array_info_t
{
const void *array;
size_t size;
size_t elsize;
const char *name;
const char *algorithm;
};
extern const array_info_t non_sparse_consts[];
extern const array_info_t sparse_consts[];
#define ARR(x) x, qnumber(x), sizeof(x[0]), #x

View File

@@ -0,0 +1,16 @@
PROC=findcrypt
O1=consts
O2=sparse
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)consts$(O) : $(I)llong.hpp $(I)pro.h consts.cpp findcrypt.hpp
$(F)findcrypt$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)dirtree.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)moves.hpp \
$(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp \
$(I)xref.hpp findcrypt.cpp findcrypt.hpp
$(F)sparse$(O) : $(I)llong.hpp $(I)pro.h findcrypt.hpp sparse.cpp

View File

@@ -0,0 +1,122 @@
#include <pro.h>
#include "findcrypt.hpp"
// Various constants used in crypto algorithms
// They were copied from public domain codes
static const word32 SHA_1[] =
{
0x67452301L,
0xEFCDAB89L,
0x98BADCFEL,
0x10325476L,
0xC3D2E1F0L,
};
static const word32 RC5_RC6[] =
{
0xb7e15163L, // magic constant P for wordsize
0x9e3779b9L, // magic constant Q for wordsize
};
static const word32 MD5[] =
{
0xd76aa478,
0xe8c7b756,
0x242070db,
0xc1bdceee,
0xf57c0faf,
0x4787c62a,
0xa8304613,
0xfd469501,
0x698098d8,
0x8b44f7af,
0xffff5bb1,
0x895cd7be,
0x6b901122,
0xfd987193,
0xa679438e,
0x49b40821,
0xf61e2562,
0xc040b340,
0x265e5a51,
0xe9b6c7aa,
0xd62f105d,
0x02441453,
0xd8a1e681,
0xe7d3fbc8,
0x21e1cde6,
0xc33707d6,
0xf4d50d87,
0x455a14ed,
0xa9e3e905,
0xfcefa3f8,
0x676f02d9,
0x8d2a4c8a,
0xfffa3942,
0x8771f681,
0x6d9d6122,
0xfde5380c,
0xa4beea44,
0x4bdecfa9,
0xf6bb4b60,
0xbebfbc70,
0x289b7ec6,
0xeaa127fa,
0xd4ef3085,
0x04881d05,
0xd9d4d039,
0xe6db99e5,
0x1fa27cf8,
0xc4ac5665,
0xf4292244,
0x432aff97,
0xab9423a7,
0xfc93a039,
0x655b59c3,
0x8f0ccc92,
0xffeff47d,
0x85845dd1,
0x6fa87e4f,
0xfe2ce6e0,
0xa3014314,
0x4e0811a1,
0xf7537e82,
0xbd3af235,
0x2ad7d2bb,
0xeb86d391,
};
static const word32 MD4[] =
{
0x67452301L,
0xefcdab89L,
0x98badcfeL,
0x10325476L,
};
static const word32 HAVAL[] =
{
0x243F6A88,
0x85A308D3,
0x13198A2E,
0x03707344,
0xA4093822,
0x299F31D0,
0x082EFA98,
0xEC4E6C89,
};
// NB: all sparse arrays must be word32!
const array_info_t sparse_consts[] =
{
{ ARR(SHA_1), "SHA-1" },
{ ARR(RC5_RC6), "RC5_RC6" },
{ ARR(MD5), "MD5" },
{ ARR(MD4), "MD4" },
{ ARR(HAVAL), "HAVAL" },
{ NULL, 0, 0, NULL, NULL }
};

View File

@@ -0,0 +1,316 @@
/*
* This plugin demonstrates how to use choosers inside forms.
*
*/
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#define ACTION_NAME "formchooser:action"
#define TITLE_PFX "Form with choosers"
//--------------------------------------------------------------------------
// raw data of the png icon (16x16)
static const unsigned char icon_data[182] =
{
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0xF3, 0xFF,
0x61, 0x00, 0x00, 0x00, 0x7D, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0x63, 0x64, 0xC0, 0x0E, 0xFE,
0xE3, 0x10, 0x67, 0x24, 0x28, 0x00, 0xD2, 0xFC, 0xF3, 0xAF, 0x36, 0x56, 0xDD, 0xEC, 0xCC, 0x57,
0x31, 0xF4, 0x20, 0x73, 0xC0, 0xB6, 0xE2, 0xD2, 0x8C, 0x66, 0x08, 0x5C, 0x2F, 0x8A, 0x01, 0x84,
0x34, 0x63, 0x73, 0x09, 0x23, 0xA9, 0x9A, 0xD1, 0x0D, 0x61, 0x44, 0xD7, 0xCC, 0xCF, 0x02, 0x71,
0xE2, 0xC7, 0x3F, 0xA8, 0x06, 0x62, 0x13, 0x07, 0x19, 0x42, 0x7D, 0x03, 0x48, 0xF5, 0xC6, 0x20,
0x34, 0x00, 0xE4, 0x57, 0x74, 0xFF, 0xE3, 0x92, 0x83, 0x19, 0xC0, 0x40, 0x8C, 0x21, 0xD8, 0x34,
0x33, 0x40, 0xA3, 0x91, 0x01, 0x97, 0x21, 0xC8, 0x00, 0x9B, 0x66, 0x38, 0x01, 0x33, 0x00, 0x44,
0x50, 0x92, 0x94, 0xB1, 0xBA, 0x04, 0x8B, 0x66, 0x9C, 0x99, 0x09, 0xC5, 0x10, 0x1C, 0xE2, 0x18,
0xEA, 0x01, 0xA3, 0x65, 0x55, 0x0B, 0x33, 0x14, 0x07, 0x63, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
};
//-------------------------------------------------------------------------
struct formchooser_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *ctx) override
{
msg("Menu item clicked. Current selection:");
for ( size_t i = 0, n = ctx->chooser_selection.size(); i < n; ++i )
msg(" %" FMT_Z, ctx->chooser_selection[i]);
msg("\n");
return 1;
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override
{
bool ok = ctx->widget_type == BWN_CHOOSER;
if ( ok )
{
qstring name;
ok = get_widget_title(&name, ctx->widget)
&& strneq(name.c_str(), TITLE_PFX, qstrlen(TITLE_PFX));
}
return ok ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET;
}
};
//--------------------------------------------------------------------------
//lint -e{958} padding needed
struct plugin_ctx_t : public plugmod_t
{
int icon_id = load_custom_icon(icon_data, sizeof(icon_data), "png");
formchooser_ah_t formchooser_ah;
const action_desc_t formchooser_desc = ACTION_DESC_LITERAL_PLUGMOD(
ACTION_NAME,
"Test",
&formchooser_ah,
this,
"Ctrl-K",
nullptr,
icon_id);
int main_current_index = -1;
virtual bool idaapi run(size_t) override;
static int idaapi modcb(int fid, form_actions_t &fa);
void refresh_selection_edit(form_actions_t & fa);
};
//---------------------------------------------------------------------------
struct mainch_chooser_t : public chooser_t
{
protected:
static const int widths_[];
static const char *const header_[];
friend struct auxch_chooser_t;
public:
// this chooser is embedded into the modal form
inline mainch_chooser_t(int icon_id);
virtual size_t idaapi get_count() const override { return 10; }
virtual void idaapi get_row(
qstrvec_t *cols,
int *icon_,
chooser_item_attrs_t *attrs,
size_t n) const override;
};
//---------------------------------------------------------------------------
struct auxch_chooser_t : public chooser_multi_t
{
public:
plugin_ctx_t &ctx;
// this chooser is embedded into the modal form
auxch_chooser_t(plugin_ctx_t &ctx, int icon_id);
virtual size_t idaapi get_count() const override
{
return ctx.main_current_index + 1;
}
virtual void idaapi get_row(
qstrvec_t *cols,
int *icon_,
chooser_item_attrs_t *attrs,
size_t n) const override;
};
//--------------------------------------------------------------------------
const int mainch_chooser_t::widths_[] = { 40 };
const char *const mainch_chooser_t::header_[] = { "Item" };
//-------------------------------------------------------------------------
inline mainch_chooser_t::mainch_chooser_t(int icon_)
: chooser_t(CH_KEEP | CH_NOIDB,
qnumber(widths_), widths_, header_)
{
CASSERT(qnumber(widths_) == qnumber(header_));
icon = icon_;
}
//-------------------------------------------------------------------------
void idaapi mainch_chooser_t::get_row(
qstrvec_t *cols_,
int *,
chooser_item_attrs_t *,
size_t n) const
{
qstrvec_t &cols = *cols_;
cols[0].sprnt("Option %" FMT_Z, n + 1);
CASSERT(qnumber(header_) == 1);
}
//-------------------------------------------------------------------------
auxch_chooser_t::auxch_chooser_t(plugin_ctx_t &ctx_, int icon_)
: chooser_multi_t(
CH_KEEP | CH_NOIDB,
qnumber(mainch_chooser_t::widths_),
mainch_chooser_t::widths_,
mainch_chooser_t::header_),
ctx(ctx_)
{
icon = icon_;
}
//-------------------------------------------------------------------------
void idaapi auxch_chooser_t::get_row(
qstrvec_t *cols_,
int *,
chooser_item_attrs_t *,
size_t n) const
{
qstrvec_t &cols = *cols_;
cols[0].sprnt("Item %" FMT_Z, n + 1);
}
//-------------------------------------------------------------------------
void plugin_ctx_t::refresh_selection_edit(form_actions_t & fa)
{
qstring str;
if ( main_current_index == -1 )
{
str = "No selection";
}
else
{
str.sprnt("Main %d", main_current_index + 1);
sizevec_t array;
fa.get_chooser_value(4, &array);
if ( array.size() > 0 )
{
str.append(" - Aux item(s) ");
for ( int i = 0; i < array.size(); i++ )
{
if ( i != 0 )
str.append(", ");
str.cat_sprnt("%" FMT_Z, array[i] + 1);
}
}
}
fa.set_string_value(5, &str);
}
//--------------------------------------------------------------------------
int idaapi plugin_ctx_t::modcb(int fid, form_actions_t &fa)
{
plugin_ctx_t &ctx = *(plugin_ctx_t *)fa.get_ud();
switch ( fid )
{
case CB_INIT:
msg("initializing\n");
ctx.refresh_selection_edit(fa);
break;
case CB_YES:
msg("terminating\n");
break;
// main chooser
case 3:
{
msg("main chooser selection change\n");
sizevec_t array;
fa.get_chooser_value(3, &array);
ctx.main_current_index = !array.empty() ? array[0] : -1;
// refresh auxiliar chooser
fa.refresh_field(4);
ctx.refresh_selection_edit(fa);
}
break;
// auxiliar chooser
case 4:
ctx.refresh_selection_edit(fa);
break;
// Aux value text control
case 5:
break;
default:
msg("unknown id %d\n", fid);
break;
}
return 1;
}
//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
struct ida_local lambda_t
{
static ssize_t idaapi cb(void *, int code, va_list va)
{
if ( code == ui_finish_populating_widget_popup )
{
TWidget *widget = va_arg(va, TWidget *);
TPopupMenu *popup_handle = va_arg(va, TPopupMenu *);
// Let the chooser populate itself normally first.
// We'll add our own stuff on second pass.
qstring buf;
if ( get_widget_type(widget) == BWN_CHOOSER
&& get_widget_title(&buf, widget)
&& buf == TITLE_PFX":3" )
{
attach_action_to_popup(widget, popup_handle, ACTION_NAME);
}
}
return 0;
}
};
hook_to_notification_point(HT_UI, lambda_t::cb);
static const char form[] =
"STARTITEM 0\n"
TITLE_PFX"\n\n"
"%/%*"
"Select an item in the main chooser:\n"
"\n"
"<Main chooser:E3::30::><Auxiliar chooser (multi):E4::30::>\n\n"
"<Selection:q5:1023:40::>\n"
"\n";
register_action(formchooser_desc);
mainch_chooser_t main_ch(icon_id);
sizevec_t main_sel; // no selection by default
main_current_index = -1;
auxch_chooser_t aux_ch(*this, icon_id);
sizevec_t aux_sel; // no selection by default
qstring str;
CASSERT(IS_CHOOSER_BASE_T(main_ch));
CASSERT(IS_CHOOSER_BASE_T(aux_ch));
if ( ask_form(form, modcb, this,
&main_ch, &main_sel,
&aux_ch, &aux_sel,
&str) > 0 )
{
msg("Selection: %s\n", str.c_str());
}
unhook_from_notification_point(HT_UI, lambda_t::cb);
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,
nullptr,
nullptr,
nullptr,
nullptr,
"Forms chooser sample",// the preferred short name of the plugin
nullptr,
};

View File

@@ -0,0 +1,204 @@
import idaapi
from ida_kernwin import Choose, Form
# --------------------------------------------------------------------------
class MainChooserClass(Choose):
def __init__(self, title, icon):
Choose.__init__(self,
title,
[ ["Item", 10] ],
icon=icon,
flags=Choose.CH_NOIDB,
embedded=True, width=30, height=20)
def OnGetLine(self, n):
return ["Option %d" % (n + 1)]
def OnGetSize(self):
return 10
# --------------------------------------------------------------------------
class AuxChooserClass(Choose):
def __init__(self, title, icon):
Choose.__init__(self,
title,
[ ["Item", 10] ],
icon=icon,
flags=Choose.CH_NOIDB | Choose.CH_MULTI,
embedded=True, width=30, height=20)
def OnGetLine(self, n):
return ["Item %d" % (n + 1)]
def OnGetSize(self):
return self.form.main_current_index + 1
# --------------------------------------------------------------------------
class TestActionHandler(idaapi.action_handler_t):
def __init__(self, chooser):
idaapi.action_handler_t.__init__(self)
self.chooser = chooser
def activate(self, ctx):
sel = []
for idx in ctx.chooser_selection:
sel.append(str(idx))
print("Menu item clicked. Current selection: %s" % sel);
def update(self, ctx):
return idaapi.AST_ENABLE_ALWAYS
# --------------------------------------------------------------------------
class MyChooserForm(Form):
# Custom icon data
icon_data = (
b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"
b"\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF"
b"\x61\x00\x00\x00\x7D\x49\x44\x41\x54\x78\xDA\x63\x64\xC0\x0E\xFE"
b"\xE3\x10\x67\x24\x28\x00\xD2\xFC\xF3\xAF\x36\x56\xDD\xEC\xCC\x57"
b"\x31\xF4\x20\x73\xC0\xB6\xE2\xD2\x8C\x66\x08\x5C\x2F\x8A\x01\x84"
b"\x34\x63\x73\x09\x23\xA9\x9A\xD1\x0D\x61\x44\xD7\xCC\xCF\x02\x71"
b"\xE2\xC7\x3F\xA8\x06\x62\x13\x07\x19\x42\x7D\x03\x48\xF5\xC6\x20"
b"\x34\x00\xE4\x57\x74\xFF\xE3\x92\x83\x19\xC0\x40\x8C\x21\xD8\x34"
b"\x33\x40\xA3\x91\x01\x97\x21\xC8\x00\x9B\x66\x38\x01\x33\x00\x44"
b"\x50\x92\x94\xB1\xBA\x04\x8B\x66\x9C\x99\x09\xC5\x10\x1C\xE2\x18"
b"\xEA\x01\xA3\x65\x55\x0B\x33\x14\x07\x63\x00\x00\x00\x00\x49\x45"
b"\x4E\x44\xAE\x42\x60\x82")
TITLE_PFX = "Form with choosers"
def Free(self):
self.hooks.unhook()
self.hooks = None
# Call the base
Form.Free(self)
# Free icon
if self.icon_id != 0:
idaapi.free_custom_icon(self.icon_id)
self.icon_id = 0
# Remove local bindings
self.EChMain = None
self.EChAux = None
def __init__(self):
# Load custom icon
self.icon_id = idaapi.load_custom_icon(data=MyChooserForm.icon_data)
if self.icon_id == 0:
raise RuntimeError("Failed to load icon data!")
self.main_current_index = -1
self.EChMain = MainChooserClass("MainChooser", self.icon_id)
self.EChAux = AuxChooserClass("AuxChooser", self.icon_id)
# Link the form to the EChooser
self.EChMain.form = self
self.EChAux.form = self
Form.__init__(self, r"""STARTITEM 0
%s
{FormChangeCb}
Select an item in the main chooser:
<Main chooser:{ctrlMainChooser}><Auxiliar chooser (multi):{ctrlAuxChooser}>
<Selection:{ctrlSelectionEdit}>
""" % self.TITLE_PFX, {
'ctrlSelectionEdit' : Form.StringInput(),
'FormChangeCb' : Form.FormChangeCb(self.OnFormChange),
'ctrlMainChooser' : Form.EmbeddedChooserControl(self.EChMain),
'ctrlAuxChooser' : Form.EmbeddedChooserControl(self.EChAux),
})
# Add an action to the popup menu of the main chooser
class Hooks(idaapi.UI_Hooks):
def __init__(self, form):
idaapi.UI_Hooks.__init__(self)
self.form = form
def finish_populating_widget_popup(self, widget, popup):
# ids are assigned alphabetically by name
# so the id of the MainChooser is 3
if idaapi.get_widget_type(widget) == idaapi.BWN_CHOOSER \
and idaapi.get_widget_title(widget) == \
MyChooserForm.TITLE_PFX + ":3":
actdesc = idaapi.action_desc_t(
None,
"Test",
TestActionHandler(self.form.EChMain),
"Ctrl-K", # shortcut
None, # tooltip
self.form.icon_id)
idaapi.attach_dynamic_action_to_popup(
widget,
popup,
actdesc)
self.hooks = Hooks(self)
self.hooks.hook()
def refresh_selection_edit(self):
if self.main_current_index == -1:
s = "No selection in the main chooser"
else:
s = "Main %d" % (self.main_current_index + 1)
# Get selection in the aux chooser
sel = self.GetControlValue(self.ctrlAuxChooser)
if sel:
s = "%s - Aux item(s): %s" % (s, ",".join(str(x + 1) for x in sel))
# Update string input
self.SetControlValue(self.ctrlSelectionEdit, s)
def OnFormChange(self, fid):
if fid == -1:
print("initializing")
self.refresh_selection_edit()
elif fid == -2:
print("terminating");
elif fid == self.ctrlMainChooser.id:
print("main chooser selection change");
l = self.GetControlValue(self.ctrlMainChooser);
self.main_current_index = l[0] if l else -1
# Refresh auxiliar chooser
self.RefreshField(self.ctrlAuxChooser)
self.refresh_selection_edit()
elif fid == self.ctrlAuxChooser.id:
self.refresh_selection_edit()
elif fid == self.ctrlSelectionEdit.id:
pass
else:
print("unknown id %d" % fid)
return 1
def main():
global f
f = MyChooserForm()
try:
f.Compile()
r = f.Execute()
print("Execute returned: %d" % r)
f.Free()
except Exception as e:
print("Failed to show form: %s" % str(e))
if __name__=='__main__':
main()

View File

@@ -0,0 +1,11 @@
PROC=formchooser
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)formchooser$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
formchooser.cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

View File

@@ -0,0 +1,174 @@
/*
* 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, NULL) )
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;
}
//--------------------------------------------------------------------------
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
"ask_form sample", // the preferred short name of the plugin
nullptr, // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,11 @@
PROC=formsample
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)formsample$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
formsample.cpp

View File

@@ -0,0 +1,226 @@
/*
* 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 != NULL )
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",
};

View File

@@ -0,0 +1,12 @@
PROC=funclist
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)funclist$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)entry.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)name.hpp $(I)netnode.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
funclist.cpp

View File

@@ -0,0 +1,86 @@
/*
* 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
};

View File

@@ -0,0 +1,11 @@
PROC=getlines
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)getlines$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
getlines.cpp

View File

@@ -0,0 +1,38 @@
#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
};

View File

@@ -0,0 +1,30 @@
#include <idc.idc>
class myplugin_t
{
myplugin_t()
{
this.flags = 0;
this.comment = "This is a comment";
this.help = "This is help";
this.wanted_name = "Sample IDC plugin";
this.wanted_hotkey = "Alt-F6";
}
init()
{
return PLUGIN_OK;
}
run(arg)
{
msg("Hello world\n");
return 0;
}
term()
{
}
}
static PLUGIN_ENTRY()
{
return myplugin_t();
}

View File

@@ -0,0 +1,21 @@
import idaapi
class myplugin_t(idaapi.plugin_t):
flags = idaapi.PLUGIN_UNL
comment = "This is a comment"
help = "This is help"
wanted_name = "My Python plugin"
wanted_hotkey = "Alt-F8"
def init(self):
return idaapi.PLUGIN_OK
def run(self, arg):
print "Hello world!"
def term(self):
pass
def PLUGIN_ENTRY():
return myplugin_t()

View File

@@ -0,0 +1,26 @@
PROC=hello
include ../plugin.mak
all: scripts
SCRIPTS := $(addprefix $(BIN_PATH),idchello.idc pyhello.py)
.PHONY: scripts
scripts: $(SCRIPTS)
$(BIN_PATH)%.idc: %.idc
$(CP) $? $@
$(BIN_PATH)%.py: %.py
$(CP) $? $@
uninstall::
rm -rf $(SCRIPTS)
# MAKEDEP dependency list ------------------
$(F)hello$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
hello.cpp

View File

@@ -0,0 +1,227 @@
//--------------------------------------------------------------------------
// hex place methods
typedef hex_place_t hp_t;
//--------------------------------------------------------------------------
// Short information about the current location.
// It will be displayed in the status line
void ida_export hex_place_t__print(const hp_t *, qstring *, void *)
{
}
//--------------------------------------------------------------------------
// Convert current location to 'uval_t'
uval_t ida_export hex_place_t__touval(const hp_t *ths, void *)
{
return ths->n;
}
//--------------------------------------------------------------------------
// Make a copy
place_t *ida_export hex_place_t__clone(const hp_t *ths)
{
return new hp_t(*ths);
}
//--------------------------------------------------------------------------
// Copy from another hex_place_t object
void ida_export hex_place_t__copyfrom(hp_t *ths, const place_t *from)
{
hp_t *s = (hp_t *)from;
ths->d = s->d;
ths->n = s->n;
ths->lnnum = s->lnnum;
}
//--------------------------------------------------------------------------
// Create a hex_place_t object at the specified address
// with the specified data
place_t *ida_export hex_place_t__makeplace(const hp_t *ths, void *, uval_t x, int lnnum)
{
hex_place_t *p = new hex_place_t();
p->d = ths->d;
p->n = x;
p->lnnum = lnnum;
return p;
}
//--------------------------------------------------------------------------
int ida_export hex_place_t__compare(const hp_t *ths, const place_t *t2)
{
hp_t *s = (hp_t *)t2;
return compare(ths->n, s->n);
}
//--------------------------------------------------------------------------
// Compare two hex_place_t objects
// Return -1, 0, 1
int ida_export hex_place_t__compare2(const hp_t *ths, const place_t *t2, void *)
{
return hex_place_t__compare(ths, t2);
}
//--------------------------------------------------------------------------
// Check if the location data is correct and if not, adjust it
void ida_export hex_place_t__adjust(hp_t *ths, void *)
{
if ( ths->n > ths->d->maxline() )
{
ths->n = 0;
ths->lnnum = 0;
}
}
//--------------------------------------------------------------------------
// Move to the previous location
bool ida_export hex_place_t__prev(hp_t *ths, void *)
{
if ( ths->n == 0 )
return false;
ths->n--;
return true;
}
//--------------------------------------------------------------------------
// Move to the next location
bool ida_export hex_place_t__next(hp_t *ths, void *)
{
if ( ths->n >= ths->d->maxline() )
return false;
ths->n++;
return true;
}
//--------------------------------------------------------------------------
// Are we at the beginning of the data?
bool ida_export hex_place_t__beginning(const hp_t *ths, void *)
{
return ths->n == 0;
}
//--------------------------------------------------------------------------
// Are we at the end of the data?
bool ida_export hex_place_t__ending(const hp_t *ths, void *)
{
return ths->n == ths->d->maxline();
}
//--------------------------------------------------------------------------
// Generate text for the current location
int ida_export hex_place_t__generate(
const hp_t *ths,
qstrvec_t *out,
int *default_lnnum,
color_t *,
bgcolor_t *,
void *,
int maxsize)
{
int idx = ths->n;
if ( idx > ths->d->maxline() || maxsize <= 0 )
return 0;
uint alignment = ths->d->alignment();
uchar *data = (uchar *)qalloc(alignment);
if ( !ths->d->read(alignment * ths->n, data, alignment) )
{
qfree(data);
return 0;
}
#define HEX_ASCII_SEP 2
size_t bufsize = 4 * alignment + HEX_ASCII_SEP + 20;
char *str = (char *)qalloc(bufsize);
if ( str == NULL )
nomem("hexplace");
str[0] = 0;
// add hex values
static const char hexstr[] = "0123456789ABCDEF";
size_t pos = qstrlen(str);
for ( uint i = 0; i < alignment; i++ )
{
str[pos++] = ' ';
uchar c = data[i];
str[pos++] = hexstr[c >> 4];
str[pos++] = hexstr[c & 0xF];
}
memset(&str[pos], ' ', HEX_ASCII_SEP);
pos += HEX_ASCII_SEP;
// add ascii values
char *ptr = str + pos;
char *end = str + bufsize;
APPCHAR(ptr, end, COLOR_ON);
APPCHAR(ptr, end, COLOR_NUMBER);
for ( uint i = 0; i < alignment; i++ )
APPCHAR(ptr, end, qisprint(data[i]) ? (char)data[i] : '.');
APPCHAR(ptr, end, COLOR_OFF);
APPCHAR(ptr, end, COLOR_NUMBER);
APPZERO(ptr, end);
qfree(data);
out->push_back(str);
*default_lnnum = 0;
return 1;
}
//-------------------------------------------------------------------------
void ida_export hex_place_t__serialize(const hex_place_t *_this, bytevec_t *out)
{
place_t__serialize(_this, out);
out->pack_ea(_this->n);
}
//-------------------------------------------------------------------------
bool ida_export hex_place_t__deserialize(hex_place_t *_this, const uchar **pptr, const uchar *end)
{
if ( !place_t__deserialize(_this, pptr, end) || *pptr >= end )
return false;
_this->n = unpack_ea(pptr, end);
return true;
}
//-------------------------------------------------------------------------
// this var is static because it is not database specific
static int hex_place_id = -1;
static const hex_place_t _template;
void register_hex_place()
{
hex_place_id = register_place_class(&_template, PCF_MAKEPLACE_ALLOCATES, &PLUGIN);
}
//-------------------------------------------------------------------------
int ida_export hex_place_t__id(const hex_place_t *)
{
return hex_place_id;
}
//-------------------------------------------------------------------------
const char *ida_export hex_place_t__name(const hex_place_t *)
{
return "hexview:hex_place_t";
}
//-------------------------------------------------------------------------
ea_t ida_export hex_place_t__toea(const hex_place_t *)
{
return BADADDR;
}
//-------------------------------------------------------------------------
place_t *ida_export hex_place_t__enter(const hex_place_t *, uint32 *)
{
return NULL;
}
//-------------------------------------------------------------------------
void ida_export hex_place_t__leave(const hex_place_t *, uint32)
{
}
//-------------------------------------------------------------------------
bool ida_export hex_place_t__rebase(hex_place_t *, const segm_move_infos_t &)
{
return false;
}

View File

@@ -0,0 +1,190 @@
//---------------------------------------------------------------------------
// Hex view sample plugin
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
//---------------------------------------------------------------------------
// hex data
class hex_data_t : public plugmod_t, public event_listener_t
{
TWidget *cv = nullptr;
TWidget *hexview = nullptr;
FILE *f = nullptr;
uint64 sz = 10000;
const uint align = 16;
public:
hex_data_t()
{
hook_event_listener(HT_VIEW, this);
}
~hex_data_t()
{
// listeners are uninstalled automatically
// when the owner module is unloaded
close();
}
virtual bool idaapi run(size_t) override;
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
bool open(const char *fname)
{
close();
f = qfopen(fname, "rb");
if ( f == NULL )
return false;
// 64 bit functions could be used instead
qfseek(f, 0, SEEK_END);
sz = qftell(f);
return true;
}
void close()
{
cv = nullptr;
hexview = nullptr;
if ( f != nullptr )
{
qfclose(f);
f = nullptr;
sz = 0;
}
}
bool read(uint64 pos, void *buf, size_t bufsize)
{
// 64 bit functions could be used instead
if ( qfseek(f, pos, SEEK_SET) != 0 )
return false;
return qfread(f, buf, bufsize) == bufsize;
}
uint64 size() const
{
return sz;
}
int alignment() const
{
return align;
}
uval_t pos_to_line(uint64 pos) const
{
return pos / align;
}
uval_t maxline() const
{
return pos_to_line(sz - 1);
}
};
//---------------------------------------------------------------------------
// hex place
define_place_exported_functions(hex_place_t)
//-------------------------------------------------------------------------
class hex_place_t : public place_t
{
public:
hex_data_t *d;
uval_t n;
hex_place_t() : d(nullptr), n(0) { lnnum = 0; }
hex_place_t(hex_data_t *_d, uint64 pos = 0) : d(_d)
{
n = d->pos_to_line(pos);
lnnum = 0;
}
define_place_virtual_functions(hex_place_t)
};
#include "hexplace.cpp"
//--------------------------------------------------------------------------
ssize_t idaapi hex_data_t::on_event(ssize_t code, va_list va)
{
switch ( code )
{
case ui_widget_invisible:
{
TWidget *w = va_arg(va, TWidget *);
if ( w == hexview || w == cv )
{
close();
unhook_event_listener(HT_UI, this);
}
}
break;
}
return 0;
}
//---------------------------------------------------------------------------
// Create a custom view window
bool idaapi hex_data_t::run(size_t)
{
register_hex_place();
static const char title[] = "Sample hexview";
TWidget *widget = find_widget(title);
if ( widget != NULL )
{
warning("Hexview already open. Switching to it.");
activate_widget(widget, true);
return true;
}
// ask the user to select a file
char *filename = ask_file(false, NULL, "Select a file to display...");
if ( filename == NULL || filename[0] == 0 )
return true;
// open the file
if ( !open(filename) )
return true;
// create two place_t objects: for the minimal and maximal locations
hex_place_t s1(this);
hex_place_t s2(this, size() - 1);
// create a custom viewer
cv = create_custom_viewer(title, &s1, &s2, &s1, NULL, this, NULL, NULL);
// create a code viewer container for the custom view
hexview = create_code_viewer(cv);
// set the radix and alignment for the offsets
set_code_viewer_lines_radix(hexview, 16);
set_code_viewer_lines_alignment(hexview, size() > 0xFFFFFFFF ? 16 : 8);
// also set the ui event callback
hook_event_listener(HT_UI, this);
// finally display the form on the screen
display_widget(hexview, WOPN_DP_TAB|WOPN_RESTORE);
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
return new hex_data_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,
nullptr, // long comment about the plugin
nullptr, // multiline help about the plugin
"Sample hexview", // the preferred short name of the plugin
nullptr, // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,11 @@
PROC=hexview
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)hexview$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
hexplace.cpp hexview.cpp

View File

@@ -0,0 +1,193 @@
// 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
};

View File

@@ -0,0 +1,11 @@
PROC=highlighter
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)highlighter$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idd.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp highlighter.cpp

View File

@@ -0,0 +1,220 @@
/*
* 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;
};
//-------------------------------------------------------------------------
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(f, p, desc, nullptr, 0);
}
plgmod->desc_notification("msg_popup");
}
}
break;
}
return 0;
}
//---------------------------------------------------------------------------
// 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:30: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
};

View File

@@ -0,0 +1,11 @@
PROC=ht_output
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)ht_output$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
ht_output.cpp

View File

@@ -0,0 +1,243 @@
/*
* 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, NULL, NULL, -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 != NULL )
{
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
};

View File

@@ -0,0 +1,11 @@
PROC=ht_view
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)ht_view$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)gdl.hpp $(I)graph.hpp $(I)ida.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ht_view.cpp

70
idasdk76/plugins/makefile Normal file
View File

@@ -0,0 +1,70 @@
include ../allmake.mak
GOALS += plugins
GOALS += samples
ifeq ($(or $(IDAHOME),$(DEMO_OR_FREE)),)
GOALS += examples
endif
.PHONY: $(GOALS)
all: $(GOALS)
#----------------------------------------------------------------------
PLUGINS += callee
PLUGINS += callgraph
PLUGINS += choose
PLUGINS += findcrypt2
PLUGINS += ht_output
PLUGINS += ht_view
PLUGINS += pdb
PLUGINS += tracing_api
PLUGINS += uiswitch
PLUGINS-$(__NT__) += uunp
PLUGINS += $(PLUGINS-1)
plugins: $(PLUGINS)
#----------------------------------------------------------------------
SAMPLES += custdata
SAMPLES += custview
SAMPLES += formchooser
SAMPLES += formsample
SAMPLES += funclist
SAMPLES += getlines
SAMPLES += hexview
SAMPLES += mtsample
SAMPLES += openform
SAMPLES += procext
SAMPLES += ugraph
SAMPLES += ugraph3
SAMPLES += vcsample
samples: $(SAMPLES)
#----------------------------------------------------------------------
# Note: examples are not installed in the plugins/ directory by default.
# to install them there, run 'make' from the plugin subdirectory.
# to uninstall the plugin afterwards, run 'make uninstall' from
# the plugin subdirectory or 'make examples_uninstall' from the
# 'plugins' directory.
HAS_QT := $(call ls,$(QTDIR))
EXAMPLES += ex_debidc
EXAMPLES += ex_events1
EXAMPLES += script_plg
EXAMPLES-$(HAS_QT) += qproject
EXAMPLES-$(HAS_QT) += qwindow
# do not include ugraph2 in the samples because it is buggy:
# in some cases it combines blocks in such a way that switching to
# graph view becomes impossible
EXAMPLES += ugraph2
# the z80dbg plugin will conflict with functionality already submitted
# to the z80 processor module, so we don't install it
EXAMPLES += z80dbg
EXAMPLES += $(EXAMPLES-1)
examples: $(EXAMPLES)
#----------------------------------------------------------------------
.PHONY: $(PLUGINS) $(SAMPLES) $(EXAMPLES)
$(PLUGINS) $(SAMPLES) $(EXAMPLES):
$(Q)$(MAKE) -C $@
#----------------------------------------------------------------------
clean::
$(foreach dir,$(PLUGINS) $(SAMPLES) $(EXAMPLES),$(MAKE) -C $(dir) clean;)

View File

@@ -0,0 +1,11 @@
PROC=mtsample
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)mtsample$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
mtsample.cpp

View File

@@ -0,0 +1,137 @@
/*
* 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
};

View File

@@ -0,0 +1,11 @@
PROC=navcolor
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)navcolor$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
navcolor.cpp

View File

@@ -0,0 +1,93 @@
/*
* 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(NULL, NULL, 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
};

View File

@@ -0,0 +1,11 @@
PROC=openform
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)openform$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
openform.cpp

View File

@@ -0,0 +1,306 @@
/*
* 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 != NULL;
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:30:1::><|><Text:t1:30:40:::>\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",
NULL,
_dock ? DP_INSIDE : 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
update_buttons(fa);
break;
case CB_CLOSE: // Closing
msg("closing control form\n");
control_fa = NULL;
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:
break;
}
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", NULL, 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,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

7560
idasdk76/plugins/pdb/dia2.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
PROC=pdb
CONFIGS=pdb.cfg
ifdef __NT__
O1=old
STDLIBS += ole32.lib
STDLIBS += oleaut32.lib
else
LIBS += $(L)network$(A)
endif
include ../plugin.mak
$(F)pdb$(O): CC_WNO-$(call gte,$(GCC_VERSION),6.1) += -Wno-null-dereference

View File

@@ -0,0 +1,40 @@
//----------------------------------------------------------------------
static const char *symtag_to_string(uint32 tag)
{
static const char *const names[] =
{
"Null",
"Exe",
"Compiland",
"CompilandDetails",
"CompilandEnv",
"Function",
"Block",
"Data",
"Annotation",
"Label",
"PublicSymbol",
"UDT",
"Enum",
"FunctionType",
"PointerType",
"ArrayType",
"BaseType",
"Typedef",
"BaseClass",
"Friend",
"FunctionArgType",
"FuncDebugStart",
"FuncDebugEnd",
"UsingNamespace",
"VTableShape",
"VTable",
"Custom",
"Thunk",
"CustomType",
"ManagedType",
"Dimension"
};
return tag < qnumber(names) ? names[tag] : "???";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
#ifndef MSDIA_HPP
#define MSDIA_HPP
#include "pdbaccess.hpp"
#include "pdblocal.hpp"
//----------------------------------------------------------------------------
struct pdb_session_t
{
HMODULE dia_hmod;
int refcount;
local_pdb_access_t *pdb_access;
pdb_session_t()
: dia_hmod(NULL),
refcount(1),
pdb_access(NULL),
pSource(NULL)
{
session_count++;
}
~pdb_session_t();
HRESULT open_session(const pdbargs_t &pdbargs);
void close();
const char *get_used_fname() const { return used_fname.begin(); }
private:
DECLARE_UNCOPYABLE(pdb_session_t)
HRESULT create_dia_source(int *dia_version);
IDiaDataSource *pSource;
qwstring winput;
qwstring wspath;
enum load_data_type_t
{
EXE_LOCAL,
MEM_LOCAL, // PDB_PLUGIN
EXE_WIN32, // PDB_WIN32_SERVER
MEM_WIN32, // PDB_WIN32_SERVER
};
HRESULT load_data_for_exe(const pdbargs_t &pdbargs, load_data_type_t type);
HRESULT load_input_path(const pdbargs_t &pdbargs, const char *input_path);
// The total number of different PDB sessions; kept track of
// in order to know when we can safely CoUninitialize().
static int session_count;
// Whether COM is initialized in this thread.
static bool co_initialized;
#ifdef _DEBUG
public:
qstring _pdb_path;
#endif
qstring used_fname;
};
//----------------------------------------------------------------------------
class pdb_session_ref_t
{
public:
pdb_session_t *session; // refcounted object
pdb_session_ref_t(void) : session(NULL) {}
pdb_session_ref_t(const pdb_session_ref_t &r);
~pdb_session_ref_t();
pdb_session_ref_t &operator=(const pdb_session_ref_t &r);
void create_session();
void close();
bool empty() const { return session == NULL; }
bool opened() const { return !empty() && session->pdb_access != NULL; }
HRESULT open_session(const pdbargs_t &args);
};
//----------------------------------------------------------------------------
const char *pdberr(int code);
#endif

View File

@@ -0,0 +1,202 @@
// Old interface to PDB files
// It is used as a fallback method if DIA interface fails
#include <windows.h>
#pragma pack(push, 8)
#include "cvconst.h"
#include "dbghelp.h"
#pragma pack(pop)
#include <ida.hpp>
#include <idp.hpp>
#include <err.h>
#include "oldpdb.h"
//----------------------------------------------------------------------
typedef DWORD IMAGEAPI SymSetOptions_t(IN DWORD SymOptions);
typedef BOOL IMAGEAPI SymInitialize_t(IN HANDLE hProcess, IN LPCSTR UserSearchPath, IN BOOL fInvadeProcess);
typedef DWORD64 IMAGEAPI SymLoadModule64_t(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll);
typedef BOOL IMAGEAPI SymEnumSymbols_t(IN HANDLE hProcess, IN ULONG64 BaseOfDll, IN PCSTR Mask, IN PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, IN PVOID UserContext);
typedef BOOL IMAGEAPI SymUnloadModule64_t(IN HANDLE hProcess, IN DWORD64 BaseOfDll);
typedef BOOL IMAGEAPI SymCleanup_t(IN HANDLE hProcess);
static HINSTANCE dbghelp = NULL;
static SymSetOptions_t *pSymSetOptions = NULL;
static SymInitialize_t *pSymInitialize = NULL;
static SymLoadModule64_t *pSymLoadModule64 = NULL;
static SymEnumSymbols_t *pSymEnumSymbols = NULL;
static SymUnloadModule64_t *pSymUnloadModule64 = NULL;
static SymCleanup_t *pSymCleanup = NULL;
static int symbols_found = 0;
//----------------------------------------------------------------------
// Dynamically load and link to DBGHELP or IMAGEHLP libraries
// Return: success
static bool setup_pointers(bool *must_free)
{
char dll[QMAXPATH];
// check if it's already loaded
dbghelp = GetModuleHandle("dbghelp.dll");
*must_free = false;
if ( dbghelp == NULL )
{
// nope, load it
// use search_path to avoid dll current directory attacks
if ( !search_path(dll, sizeof(dll), "dbghelp.dll", false) )
return false;
dbghelp = LoadLibrary(dll);
*must_free = true;
}
if ( dbghelp == NULL )
{
deb(IDA_DEBUG_DBGINFO, "PDB plugin: failed to load DBGHELP.DLL");
}
else
{
*(FARPROC*)&pSymSetOptions = GetProcAddress(dbghelp, "SymSetOptions");
*(FARPROC*)&pSymInitialize = GetProcAddress(dbghelp, "SymInitialize");
*(FARPROC*)&pSymLoadModule64 = GetProcAddress(dbghelp, "SymLoadModule64");
*(FARPROC*)&pSymEnumSymbols = GetProcAddress(dbghelp, "SymEnumSymbols");
*(FARPROC*)&pSymUnloadModule64 = GetProcAddress(dbghelp, "SymUnloadModule64");
*(FARPROC*)&pSymCleanup = GetProcAddress(dbghelp, "SymCleanup");
if ( pSymSetOptions != NULL
&& pSymInitialize != NULL
&& pSymLoadModule64 != NULL
&& pSymUnloadModule64 != NULL
&& pSymCleanup != NULL
&& pSymEnumSymbols != NULL ) // required XP or higher
{
return true;
}
}
deb(IDA_DEBUG_DBGINFO, "PDB plugin: Essential DBGHELP.DLL functions are missing\n");
if ( dbghelp != NULL )
{
FreeLibrary(dbghelp);
dbghelp = NULL;
}
return false;
}
//----------------------------------------------------------------------
// New method: symbol enumeration callback
//lint -e{818} could be declared as pointing to const
static BOOL CALLBACK EnumerateSymbolsProc(
PSYMBOL_INFO psym,
ULONG /*SymbolSize*/,
PVOID delta)
{
symbols_found++;
ea_t ea = (ea_t)(psym->Address + *(adiff_t*)delta);
const char *name = psym->Name;
int maybe_func = 0; // maybe
switch ( psym->Tag )
{
case SymTagFunction:
case SymTagThunk:
maybe_func = 1;
break;
case SymTagNull:
case SymTagExe:
case SymTagCompiland:
case SymTagCompilandDetails:
case SymTagCompilandEnv:
case SymTagData:
case SymTagAnnotation:
case SymTagUDT:
case SymTagEnum:
case SymTagFunctionType:
case SymTagPointerType:
case SymTagArrayType:
case SymTagBaseType:
case SymTagTypedef:
case SymTagBaseClass:
case SymTagFunctionArgType:
case SymTagUsingNamespace:
case SymTagVTableShape:
case SymTagVTable:
case SymTagCustom:
case SymTagCustomType:
case SymTagManagedType:
case SymTagDimension:
maybe_func = -1;
break;
case SymTagBlock:
case SymTagLabel:
case SymTagFuncDebugStart:
case SymTagFuncDebugEnd:
maybe_func = 2;
break;
case SymTagPublicSymbol:
case SymTagFriend:
default:
break;
}
bool ok = apply_name(ea, name, maybe_func);
// New dbghelp.dll/symsrv.dll files return names without the terminating zero.
// So, as soon as we have a long name, shorter names will have garbage at the end.
// Clean up the name to avoid problems.
size_t len = strlen(name);
memset((void*)name, '\0', len);
return ok;
}
//----------------------------------------------------------------------
// Display a system error message
static void error_msg(const char *name)
{
int code = GetLastError();
if ( code != 0 )
msg("%s: %s\n", name, winerr(code));
}
//----------------------------------------------------------------------
// Try old method of loading symbols
bool old_pdb_plugin(ea_t loaded_base, const char *input, const char *spath)
{
bool ok = false;
bool must_free;
if ( setup_pointers(&must_free) )
{
pSymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_FAVOR_COMPRESSED|SYMOPT_NO_PROMPTS);
void *fake_proc = (void *)(uintptr_t)0xBEEFFEED;
if ( !pSymInitialize(fake_proc, spath, FALSE) )
{
error_msg("SymInitialize");
}
else
{
DWORD64 symbase = pSymLoadModule64(fake_proc, 0, (char*)input, NULL, loaded_base, 0);
if ( symbase != 0 )
{
load_vc_til();
symbols_found = 0;
adiff_t delta = adiff_t(loaded_base - symbase);
ok = pSymEnumSymbols(fake_proc, symbase, NULL, EnumerateSymbolsProc, &delta)
&& symbols_found > 0;
if ( !ok )
error_msg("EnumSymbols");
if ( !pSymUnloadModule64(fake_proc, symbase) )
error_msg("SymUnloadModule64");
}
if ( !pSymCleanup(fake_proc) )
error_msg("SymCleanup");
}
if ( must_free )
{
FreeLibrary(dbghelp);
dbghelp = NULL;
}
}
return ok;
}

View File

@@ -0,0 +1,9 @@
// Common definitions for the old and new pdb plugins
// helper functions provided by the new pdb plugin
bool apply_name(ea_t ea, const qstring &name, int maybe_func);
void load_vc_til(void);
// entry point of the old pdb plugin
bool old_pdb_plugin(ea_t loaded_base, const char *input, const char *spath);

View File

@@ -0,0 +1,47 @@
// PDB plugin
// PDB information provider
#define PDB_PROVIDER_MSDIA 1 // use MSDIA local/remote provider
#define PDB_PROVIDER_PDBIDA 2 // use PDBIDA provider
//PDB_PROVIDER = PDB_PROVIDER_PDBIDA
// it is possible to specify the desired provider in the command line:
// ida -Opdb:off input_file
// ida -Opdb:msdia input_file
// ida -Opdb:pdbida input_file
// Symbol search path
// The _NT_SYMBOL_PATH environment variable overrides this setting.
// If none of these variables is set then the default value will be used:
// "SRV*CACHEDIR*http://msdl.microsoft.com/download/symbols"
// where
// CACHEDIR=%TEMP%\ida for Windows
// CACHEDIR=$TMPDIR/ida or $TMP/ida or /tmp/ida for non-Windows OSes
//
//_NT_SYMBOL_PATH = "SRV*c:\\symbols*http://symbols.mozilla.org/firefox;SRV*c:\\symbols*http://msdl.microsoft.com/download/symbols";
// Network communications while looking for PDB file can be restricted.
// Valid only for PDBIDA provider.
#define PDB_NETWORK_OFF 0 // local directories search only
#define PDB_NETWORK_PE 1 // local directories search for COFF, full search for PE
#define PDB_NETWORK_ON 2 // no restrictions
//PDB_NETWORK = PDB_NETWORK_PE
// PDBIDA is able to load MSF 7.0 PDB files only.
// MSDIA can load all PDB files, including old MSF 2.0 files.
// If you set the following option to YES, IDA will automatically switch
// to MSDIA for old files.
// Please note that under Linux/macOS the MSDIA provider requires you to configure
// the win32_remote.exe server because it can run only on Windows.
// It is possible to specify the desired behavior in the command line:
// ida -Opdb:fallback input_file
// ida -Opdb:nofallback input_file
PDB_MSDIA_FALLBACK = NO
// remote server where win32_remote.exe is running
// used when loading PDB symbols on non-Windows platforms
// NB: it will be used only if there is not already an existing debugging session started
PDB_REMOTE_SERVER = "localhost";
PDB_REMOTE_PORT = 23946
// password for the remote server
PDB_REMOTE_PASSWD = "";

1414
idasdk76/plugins/pdb/pdb.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,217 @@
//
// Copyright (c) 2005-2021 Hex-Rays SA <support@hex-rays.com>
// ALL RIGHTS RESERVED.
//
#pragma once
#include <idp.hpp>
#include <idd.hpp>
#include <typeinf.hpp>
#include "../../ldr/pe/pe.h"
#define PDB_NODE_NAME "$ pdb"
#define PDB_DLLBASE_NODE_IDX 0
#define PDB_DLLNAME_NODE_IDX 0
#define PDB_LOADING_WIN32_DBG 1
#define PDB_TYPESONLY_NODE_IDX 2
enum pdb_callcode_t
{
// user invoked 'load pdb' command, load pdb for the input file.
// after invocation, result (boolean) is stored in: netnode(PDB_NODE_NAME).altval(PDB_DLLBASE_NODE_IDX)
PDB_CC_USER = 0,
// ida decided to call the plugin itself
PDB_CC_IDA = 1,
// load additional pdb. This is semantically the same as
// PDB_CC_USER (i.e., "File > Load file > PDB file..."), except
// it won't ask the user for the data; rather it expects it in
// netnode(PDB_NODE_NAME):
// load_addr: netnode(PDB_NODE_NAME).altval(PDB_DLLBASE_NODE_IDX)
// dll_name: netnode(PDB_NODE_NAME).supstr(PDB_DLLNAME_NODE_IDX)
PDB_CC_USER_WITH_DATA = 3,
// load debug info from the COFF file
// ida decided to call the plugin itself
// dbginfo_params_t: netnode(DBGINFO_PARAM_NODE_NAME).supval(DBGINFO_PARAMS_KEY)
PDB_CC_IDA_COFF = 4,
};
//----------------------------------------------------------------------
struct pdb_signature_t
{
uint32 guid[4]; // if all zeroes, then consider as non-existing
uint32 sig;
uint32 age;
pdb_signature_t(void) { memset(this, 0, sizeof(*this)); }
};
//----------------------------------------------------------------------------
struct pdbargs_t
{
qstring pdb_path; // Path to PDB file.
qstring input_path; // Path to PE file with associated PDB.
pdb_signature_t pdb_sign;
qstring spath;
ea_t loaded_base;
void *user_data;
uint32 flags;
#define PDBFLG_DBG_MODULE 0x0001
#define PDBFLG_ONLY_TYPES 0x0002
#define PDBFLG_EFD 0x0004
#define PDBFLG_COFF_FILE 0x0008
#define PDBFLG_USE_HTTP 0x0100
pdbargs_t(void)
: loaded_base(BADADDR),
user_data(NULL),
flags(0)
{}
// If true, we are in a debugging session and the file specified by
// input_path is an additional module that has been loaded by the
// debugger itself.
bool is_dbg_module(void) const
{
return (flags & PDBFLG_DBG_MODULE) != 0;
}
// PDB?
bool is_pdbfile(void) const { return (flags & PDBFLG_COFF_FILE) == 0; }
bool use_http() const { return (flags & PDBFLG_USE_HTTP) != 0; }
const char *fname(void) const
{
return !pdb_path.empty() ? pdb_path.begin() : input_path.c_str();
}
};
//----------------------------------------------------------------------------
struct pdb_ctx_t : public plugmod_t, public event_listener_t
{
processor_t &ph;
// PDB search path (in _NT_SYMBOL_PATH format)
qstring full_sympath;
peheader_t pe;
// config options
int pdb_remote_port = DEBUGGER_PORT_NUMBER;
int pdb_remote_port_64 = -1;
qstring pdb_remote_server;
qstring pdb_remote_passwd;
#define PDB_PROVIDER_MSDIA 1
uint pdb_provider = PDB_PROVIDER_MSDIA;
#define PDB_NETWORK_OFF 0 // local directories search only
#define PDB_NETWORK_PE 1 // local directories search for COFF, full search for PE
#define PDB_NETWORK_ON 2 // no restrictions
uint pdb_network = PDB_NETWORK_PE;
bool use_http(bool is_pe) const
{
bool ok = pdb_network == PDB_NETWORK_PE && is_pe
|| pdb_network == PDB_NETWORK_ON;
deb(IDA_DEBUG_DBGINFO, ok ? "PDB: symbol servers will be used\n"
: "PDB: local directories search only\n");
return ok;
}
// Plugin options
uint opt_provider = 0;
// -1 don't specified
// 0 set PDB_FALLBACK to false
// 1 set PDB_FALLBACK to true
int opt_fallback = -1;
using namelist_t = std::map<ea_t, qstring>;
namelist_t namelist;
// srcinfo provider
class pdb_provider_t *pdb_srcinfo_provider = nullptr;
pdb_ctx_t();
virtual ~pdb_ctx_t();
virtual bool idaapi run(size_t arg) override;
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
void parse_options(bool *opt_skip);
void init_sympaths();
void load_vc_til(void) const;
// maybe_func: -1:no, 0-maybe, 1-yes, 2:no,but iscode
bool apply_name_in_idb(ea_t ea, const qstring &name, int maybe_func, uint32 the_machine_type);
bool apply_debug_info(pdbargs_t &pdbargs);
// printable register name
bool get_pdb_register_info(int *p_reg, uint64 *p_mask, int machine, int reg);
// Because we need to be able to call the 'old' pdb plugin
// code, which knows nothing about the til_builder_t (and
// thus its 'machine_type' field, and also because, at the
// very time we call the old pdb code, our til_builder_t
// instance will have been long forgotten and destroyed,
// we must keep this machine type information somewhere.
uint32 g_machine_type = 0; // will be set to CV_CFL_80386 in ctor
private:
//-------------------------------------------------------------------------
int utf16_encidx = -1;
int get_utf16_encoding_idx();
bool checked_types = false;
bool has_sid = false;
bool check_for_ids(ea_t ea, const char *name, bool has_typeinfo);
void alloc_pdb_srcinfo_provider();
void free_pdb_srcinfo_provider();
public:
//-------------------------------------------------------------------------
//#define CHECK_CREATED_TYPES
#ifdef CHECK_CREATED_TYPES
struct type_to_check_t
{
// one of the following 3 will be valid:
ea_t ea;
int id;
qstring name;
// the type itself
tinfo_t type;
};
qvector<type_to_check_t> types_to_check;
int check_n = 0;
void check_tinfo(ea_t ea, int id, const char *name, const tinfo_t &tif)
{
type_to_check_t &tc = types_to_check.push_back();
tc.ea = ea;
tc.id = id;
tc.name = name;
tc.type = tif;
}
void check_added_types(void)
{
for ( const auto &tc : types_to_check )
{
if ( !tc.type.is_correct() )
{
msg("%d: INCORRECT TYPE ", check_n);
if ( !tc.name.empty() )
msg("%s", tc.name.begin());
else if ( tc.ea != BADADDR )
msg("%a", tc.ea);
else
msg("#%d", tc.id);
qstring res;
tc.type.print(&res);
msg(": %s\n", res.c_str());
check_n++;
}
}
}
#else
inline void check_tinfo(ea_t,int,const char*,const tinfo_t &) {}
inline void check_added_types(void) {}
#endif
};
extern int data_id;

View File

@@ -0,0 +1,661 @@
#ifndef PDBACCESS__H
#define PDBACCESS__H
#include <pro.h>
#include "cvconst.h"
#ifdef __NT__
#include <windows.h>
#include <oaidl.h>
#include "dia2.h"
#else
// FIXME: It'd be good if those windows declarations for non-windows
// systems were somewhere else than in the PE loader.
#include "../../ldr/pe/mycor.h"
#endif
#include "pdb.hpp"
//----------------------------------------------------------------------------
class pdb_access_t;
class local_pdb_access_t;
class remote_pdb_access_t;
struct pdb_exception_t
{
pdb_exception_t(const qstring &_what) : what(_what) {}
qstring what;
};
//----------------------------------------------------------------------------
#ifdef __NT__
enum sym_token_t : uint64
#else
enum sym_token_t
#endif
{
t_start = 1 << 0,
// bool
t_bool_start = t_start,
t_constType = t_bool_start,
t_isStatic = 1 << 1,
t_virtual = 1 << 2,
t_volatileType = 1 << 3,
t_code = 1 << 4,
t_hasAssignmentOperator = 1 << 5,
t_hasCastOperator = 1 << 6,
t_function = 1 << 7, // FIXME!
t_constructor = 1 << 8,
t_isVirtualBaseClass = 1 << 9,
t_bool_end = t_isVirtualBaseClass,
// dword
t_dword_start = 1 << 10,
t_backEndMajor = t_dword_start,
t_baseType = 1 << 11,
t_bitPosition = 1 << 12,
t_callingConvention = 1 << 13,
t_count = 1 << 14,
t_dataKind = 1 << 15,
t_locationType = 1 << 16,
t_registerId = 1 << 17,
t_relativeVirtualAddress = 1 << 18,
t_symIndexId = 1 << 19,
t_symTag = 1 << 20,
t_udtKind = 1 << 21,
t_virtualBaseOffset = 1 << 22,
t_machineType = 1 << 23,
t_classParentId = 1 << 24,
t_typeId = 1 << 25,
t_lexicalParentId = 1 << 26,
t_dword_end = t_lexicalParentId,
// dword64
t_dword64_start = 1 << 27,
t_length = t_dword64_start,
t_dword64_end = t_length,
// string
t_string_start = 1 << 28,
t_name = t_string_start,
t_string_end = t_name,
// long
t_long_start = 1 << 29,
t_offset = t_long_start,
t_long_end = t_offset,
// ulonglong
t_ulonglong_start = 1 << 30,
t_virtualAddress = t_ulonglong_start,
t_ulonglong_end = t_virtualAddress,
// variant
t_variant_start = 1ULL << 31,
t_value = t_variant_start,
t_variant_end = t_value,
t_end = 1ULL << 32,
};
CASSERT(sizeof(sym_token_t) == 8);
inline bool is_sym_token_bool(sym_token_t t) { return t >= t_bool_start && t <= t_bool_end; }
inline bool is_sym_token_dword(sym_token_t t) { return t >= t_dword_start && t <= t_dword_end; }
inline bool is_sym_token_dword64(sym_token_t t) { return t >= t_dword64_start && t <= t_dword64_end; }
// inline bool is_sym_token_pdb_sym(sym_token_t t) { return t >= t_pdb_sym_start && t <= t_pdb_sym_end; }
inline bool is_sym_token_string(sym_token_t t) { return t >= t_string_start && t <= t_string_end; }
inline bool is_sym_token_long(sym_token_t t) { return t >= t_long_start && t <= t_long_end; }
inline bool is_sym_token_ulonglong(sym_token_t t) { return t >= t_ulonglong_start && t <= t_ulonglong_end; }
inline bool is_sym_token_variant(sym_token_t t) { return t >= t_variant_start && t <= t_variant_end; } //-V560 is always true
typedef uint64 token_mask_t;
#define TOKEN_MASK_FULL token_mask_t(-1)
//----------------------------------------------------------------------------
// engine PDB symbol implementation identity
enum pdb_sym_id_t
{
DIA_PDB_SYM,
REMOTE_PDB_SYM,
OWN_PDB_SYM,
};
//----------------------------------------------------------------------------
struct pdb_sym_t
{
virtual HRESULT get_backEndMajor(DWORD *out) = 0;
virtual HRESULT get_baseType(DWORD *out) = 0;
virtual HRESULT get_bitPosition(DWORD *out) = 0;
virtual HRESULT get_callingConvention(DWORD *out) = 0;
virtual HRESULT get_code(BOOL *out) = 0;
virtual HRESULT get_constructor(BOOL *out) = 0;
virtual HRESULT get_isVirtualBaseClass(BOOL *out) = 0;
virtual HRESULT get_constType(BOOL *out) = 0;
virtual HRESULT get_count(DWORD *out) = 0;
virtual HRESULT get_dataKind(DWORD *out) = 0;
virtual HRESULT get_function(BOOL *out) = 0;
virtual HRESULT get_hasAssignmentOperator(BOOL *out) = 0;
virtual HRESULT get_hasCastOperator(BOOL *out) = 0;
virtual HRESULT get_isStatic(BOOL *out) = 0;
virtual HRESULT get_length(DWORD64 *out) = 0;
virtual HRESULT get_lexicalParent(pdb_sym_t *out) = 0;
virtual HRESULT get_locationType(DWORD *out) = 0;
virtual HRESULT get_machineType(DWORD *out) = 0;
virtual HRESULT get_name(qstring *out) = 0;
virtual HRESULT get_offset(LONG *out) = 0;
virtual HRESULT get_registerId(DWORD *out) = 0;
virtual HRESULT get_relativeVirtualAddress(DWORD *out) = 0;
virtual HRESULT get_symIndexId(DWORD *out) = 0;
virtual HRESULT get_symTag(DWORD *out) = 0;
virtual HRESULT get_udtKind(DWORD *out) = 0;
virtual HRESULT get_value(VARIANT *out) = 0;
virtual HRESULT get_virtual(BOOL *out) = 0;
virtual HRESULT get_virtualAddress(ULONGLONG *out) = 0;
virtual HRESULT get_virtualBaseOffset(DWORD *out) = 0;
virtual HRESULT get_volatileType(BOOL *out) = 0;
// Be very, very careful to _not_ use classParent if you can avoid it:
// In case the symbol was *not* resolved through get_type(), the link
// to the parent might be lost, and a bug in the DIA SDK will
// return S_FALSE.
virtual HRESULT get_classParent(pdb_sym_t *out) = 0;
virtual HRESULT get_type(pdb_sym_t *out) = 0;
//------------------------------------------------------------
virtual HRESULT get_ordinal(DWORD *pRetVal) = 0;
// careful with this!
virtual void steal_data(pdb_sym_t &other) = 0;
virtual pdb_sym_id_t whoami(void) = 0;
virtual bool empty(void) = 0;
virtual ~pdb_sym_t() {}
// Declare, but ***don't*** define: we don't want
// that to happen, and thus we'll have a linker
// error if that would happen in the code.
DECLARE_UNCOPYABLE(pdb_sym_t);
protected:
pdb_sym_t() {}
};
DECLARE_TYPE_AS_MOVABLE(pdb_sym_t);
typedef janitor_t<pdb_sym_t*> pdb_sym_janitor_t;
template <> inline pdb_sym_janitor_t::~janitor_t()
{
delete resource;
resource = NULL;
}
//----------------------------------------------------------------------------
enum packing_info_t
{
SYMDAT_PACKED = 1,
SYMDAT_UNPACKED
};
//----------------------------------------------------------------------------
struct sym_data_t
{
sym_data_t(token_mask_t _tokens, const uchar *buf, size_t bufsize, packing_info_t _packing, bool *_warned);
~sym_data_t();
DWORD get_id() const
{
DWORD id;
if ( get_dword(t_symIndexId, &id) != S_OK )
INTERR(30211);
return id;
}
HRESULT get_bool(sym_token_t token, BOOL *out) const;
HRESULT get_dword(sym_token_t token, DWORD *out) const;
HRESULT get_dword64(sym_token_t token, DWORD64 *out) const;
HRESULT get_pdb_sym(sym_token_t token, pdb_sym_t *out) const;
HRESULT get_string(sym_token_t token, qstring *out) const;
HRESULT get_dword(sym_token_t token, LONG *out) const;
HRESULT get_ulonglong(sym_token_t token, ULONGLONG *out) const;
HRESULT get_variant(sym_token_t token, VARIANT *out) const;
private:
sym_data_t();
bool token_present(sym_token_t token) const
{
return (present & token) == token;
}
void assert_token(sym_token_t token) const
{
if ( !token_present(token) )
INTERR(30210);
}
const BOOL *bool_ptr(sym_token_t token) const
{
return (const BOOL *)any_ptr(token, t_bool_start, t_bool_end);
}
const DWORD *dword_ptr(sym_token_t token) const
{
return (const DWORD *)any_ptr(token, t_dword_start, t_dword_end);
}
const DWORD64 *dword64_ptr(sym_token_t token) const
{
return (const DWORD64 *)any_ptr(token, t_dword64_start, t_dword64_end);
}
const LONG *long_ptr(sym_token_t token) const
{
return (const LONG *)any_ptr(token, t_long_start, t_long_end);
}
const ULONGLONG *uint64_ptr(sym_token_t token) const
{
return (const ULONGLONG *)any_ptr(token, t_ulonglong_start, t_ulonglong_end);
}
const char **string_ptr(sym_token_t token) const
{
return (const char **)any_ptr(token, t_string_start, t_string_end);
}
const VARIANT *variant_ptr(sym_token_t token) const
{
return (const VARIANT *)any_ptr(token, t_variant_start, t_variant_end);
}
enum type_t
{
t_bool = 0,
t_dword,
t_dword64,
t_string,
t_long,
t_ulonglong,
t_variant,
t_max
};
static const uint32 sizes[];
const void *any_ptr(sym_token_t token, sym_token_t start, sym_token_t end) const;
token_mask_t present; // The tokens that are present in this instance.
bytevec_t data;
uint8 counters[t_max];
struct children_t
{
DWORD *ids;
uint32 cnt;
};
children_t children_infos[SymTagMax];
friend class remote_pdb_access_t; // accesses children_infos directly
bool *warned;
};
//----------------------------------------------------------------------------
#ifdef __NT__
struct dia_pdb_sym_t : public pdb_sym_t
{
dia_pdb_sym_t(pdb_access_t *_pdb_access, IDiaSymbol *_data, bool _own_sym)
: pdb_access(_pdb_access),
data(_data),
own_sym(_own_sym)
{}
~dia_pdb_sym_t()
{
if ( data != NULL && own_sym )
{
data->Release();
data = NULL;
}
pdb_access = NULL;
}
pdb_access_t *pdb_access;
IDiaSymbol *data;
bool own_sym;
pdb_sym_id_t whoami(void) { return DIA_PDB_SYM; }
void set_symbol_data(IDiaSymbol *s, bool own)
{
data = s;
own_sym = own;
}
bool empty(void) { return data == NULL; }
HRESULT get_backEndMajor(DWORD *out) { return data->get_backEndMajor(out); }
HRESULT get_baseType(DWORD *out) { return data->get_baseType(out); }
HRESULT get_bitPosition(DWORD *out) { return data->get_bitPosition(out); }
HRESULT get_callingConvention(DWORD *out) { return data->get_callingConvention(out); }
HRESULT get_code(BOOL *out) { return data->get_code(out); }
HRESULT get_constType(BOOL *out) { return data->get_constType(out); }
HRESULT get_count(DWORD *out) { return data->get_count(out); }
HRESULT get_constructor(BOOL *out) { return data->get_constructor(out); }
HRESULT get_isVirtualBaseClass(BOOL *out) { return data->get_virtualBaseClass(out); }
HRESULT get_dataKind(DWORD *out) { return data->get_dataKind(out); }
HRESULT get_function(BOOL *out) { return data->get_function(out); }
HRESULT get_hasAssignmentOperator(BOOL *out) { return data->get_hasAssignmentOperator(out); }
HRESULT get_hasCastOperator(BOOL *out) { return data->get_hasCastOperator(out); }
HRESULT get_isStatic(BOOL *out) { return data->get_isStatic(out); }
HRESULT get_length(DWORD64 *out) { return data->get_length(out); }
HRESULT get_lexicalParent(pdb_sym_t *out)
{
IDiaSymbol *t;
HRESULT res = data->get_lexicalParent(&t);
return handle_related_symbol(res, t, out);
}
HRESULT get_locationType(DWORD *out) { return data->get_locationType(out); }
HRESULT get_machineType(DWORD *out) { return data->get_machineType(out); }
HRESULT get_name(qstring *out)
{
BSTR name;
HRESULT hr = data->get_name(&name);
return maybe_convert_bstr(out, hr, &name);
}
HRESULT get_offset(LONG *out) { return data->get_offset(out); }
HRESULT get_registerId(DWORD *out) { return data->get_registerId(out); }
HRESULT get_relativeVirtualAddress(DWORD *out) { return data->get_relativeVirtualAddress(out); }
HRESULT get_symIndexId(DWORD *out) { return data->get_symIndexId(out); }
HRESULT get_symTag(DWORD *out) { return data->get_symTag(out); }
HRESULT get_udtKind(DWORD *out) { return data->get_udtKind(out); }
HRESULT get_value(VARIANT *out) { return data->get_value(out); }
HRESULT get_virtual(BOOL *out) { return data->get_virtual(out); }
HRESULT get_virtualAddress(ULONGLONG *out) { return data->get_virtualAddress(out); }
HRESULT get_virtualBaseOffset(DWORD *out) { return data->get_virtualBaseOffset(out); }
HRESULT get_volatileType(BOOL *out) { return data->get_volatileType(out); }
HRESULT get_classParent(pdb_sym_t *out)
{
IDiaSymbol *t;
HRESULT res = data->get_classParent(&t);
return handle_related_symbol(res, t, out);
}
HRESULT get_type(pdb_sym_t *out)
{
IDiaSymbol *t;
HRESULT res = data->get_type(&t);
return handle_related_symbol(res, t, out);
}
//------------------------------------------------------------
virtual HRESULT get_ordinal(DWORD *) override { return S_FALSE; }
HRESULT handle_related_symbol(HRESULT fetch_success, IDiaSymbol *t, pdb_sym_t *out)
{
if ( out == NULL )
return S_FALSE;
QASSERT(30545, out->whoami() == DIA_PDB_SYM);
dia_pdb_sym_t *sym = (dia_pdb_sym_t *)out;
sym->set_symbol_data(fetch_success == S_OK ? t : NULL, true);
return fetch_success;
}
// careful with this!
void steal_data(pdb_sym_t &other)
{
QASSERT(30541, whoami() == other.whoami());
dia_pdb_sym_t &other_dia = (dia_pdb_sym_t &)other;
QASSERT(30503, other_dia.own_sym && other_dia.data != NULL && data == NULL);
data = other_dia.data;
own_sym = true;
other_dia.own_sym = false;
other_dia.data = NULL;
}
private:
dia_pdb_sym_t();
HRESULT maybe_convert_bstr(qstring *out, HRESULT hr, BSTR *s)
{
if ( hr == S_OK )
{
utf16_utf8(out, *s);
SysFreeString(*s);
}
return hr;
}
};
DECLARE_TYPE_AS_MOVABLE(dia_pdb_sym_t);
//----------------------------------------------------------------------------
#else // __NT__
struct remote_pdb_sym_t : public pdb_sym_t
{
remote_pdb_sym_t(pdb_access_t *_pdb_access, sym_data_t *_data)
: pdb_access(_pdb_access),
data(_data)
{}
~remote_pdb_sym_t()
{
data = NULL;
pdb_access = NULL;
}
void set_symbol_data(sym_data_t *s) { data = s; }
HRESULT get_backEndMajor(DWORD *out) override { return data->get_dword(t_backEndMajor, out); }
HRESULT get_baseType(DWORD *out) override { return data->get_dword(t_baseType, out); }
HRESULT get_bitPosition(DWORD *out) override { return data->get_dword(t_bitPosition, out); }
HRESULT get_callingConvention(DWORD *out) override { return data->get_dword(t_callingConvention, out); }
HRESULT get_code(BOOL *out) override { return data->get_bool(t_code, out); }
HRESULT get_constructor(BOOL *out) override { return data->get_bool(t_constructor, out); }
HRESULT get_isVirtualBaseClass(BOOL *out) override { return data->get_bool(t_isVirtualBaseClass, out); }
HRESULT get_constType(BOOL *out) override { return data->get_bool(t_constType, out); }
HRESULT get_count(DWORD *out) override { return data->get_dword(t_count, out); }
HRESULT get_dataKind(DWORD *out) override { return data->get_dword(t_dataKind, out); }
HRESULT get_function(BOOL *out) override { return data->get_bool(t_function, out); }
HRESULT get_hasAssignmentOperator(BOOL *out) override { return data->get_bool(t_hasAssignmentOperator, out); }
HRESULT get_hasCastOperator(BOOL *out) override { return data->get_bool(t_hasCastOperator, out); }
HRESULT get_isStatic(BOOL *out) override { return data->get_bool(t_isStatic, out); }
HRESULT get_length(DWORD64 *out) override { return data->get_dword64(t_length, out); }
HRESULT get_lexicalParent(pdb_sym_t *out) override;
HRESULT get_locationType(DWORD *out) override { return data->get_dword(t_locationType, out); }
HRESULT get_machineType(DWORD *out) override { return data->get_dword(t_machineType, out); }
HRESULT get_name(qstring *out) override { return data->get_string(t_name, out); }
HRESULT get_offset(LONG *out) override { return data->get_dword(t_offset, out); }
HRESULT get_registerId(DWORD *out) override { return data->get_dword(t_registerId, out); }
HRESULT get_relativeVirtualAddress(DWORD *out) override { return data->get_dword(t_relativeVirtualAddress, out); }
HRESULT get_symIndexId(DWORD *out) override { return data->get_dword(t_symIndexId, out); }
HRESULT get_symTag(DWORD *out) override { return data->get_dword(t_symTag, out); }
HRESULT get_udtKind(DWORD *out) override { return data->get_dword(t_udtKind, out); }
HRESULT get_value(VARIANT *out) override { return data->get_variant(t_value, out); }
HRESULT get_virtual(BOOL *out) override { return data->get_bool(t_virtual, out); }
HRESULT get_virtualAddress(ULONGLONG *out) override { return data->get_ulonglong(t_virtualAddress, out); }
HRESULT get_virtualBaseOffset(DWORD *out) override { return data->get_dword(t_virtualBaseOffset, out); }
HRESULT get_volatileType(BOOL *out) override { return data->get_bool(t_volatileType, out); }
HRESULT get_classParent(pdb_sym_t *out) override;
HRESULT get_type(pdb_sym_t *out) override;
//------------------------------------------------------------
virtual HRESULT get_ordinal(DWORD *) override { return S_FALSE; }
// careful with this!
void steal_data(pdb_sym_t &other) override
{
QASSERT(30542, whoami() == other.whoami());
remote_pdb_sym_t &other_rem = (remote_pdb_sym_t &)other;
QASSERT(30492, other_rem.data != NULL && data == NULL);
data = other_rem.data;
// we don't want to set 'other.data = NULL', because in remote access,
// the pdb_sym_t doesn't actually own the data anyway: it's part of
// the cache.
}
pdb_sym_id_t whoami(void) override { return REMOTE_PDB_SYM; }
bool empty(void) override { return data == NULL; }
pdb_access_t *pdb_access;
sym_data_t *data;
private:
remote_pdb_sym_t();
};
DECLARE_TYPE_AS_MOVABLE(remote_pdb_sym_t);
#endif // !__NT__
//----------------------------------------------------------------------------
#define BAD_MACHINE_TYPE ((uint32) -1)
#define BADSYM ((uint32) -1)
//-------------------------------------------------------------------------
struct pdb_lnnum_t
{
pdb_lnnum_t()
: va(BADADDR), length(0),
columnNumber(0), columnNumberEnd(0),
lineNumber(0), lineNumberEnd(0),
file_id(DWORD(-1)), statement(0) {}
ULONGLONG va;
DWORD length;
DWORD columnNumber;
DWORD columnNumberEnd;
DWORD lineNumber;
DWORD lineNumberEnd;
DWORD file_id;
BOOL statement;
};
//--------------------------------------------------------------------------
typedef qvector<pdb_lnnum_t> pdb_lnnum_vec_t;
typedef std::map<int, pdb_lnnum_vec_t> lnmap_t;
//-------------------------------------------------------------------------
struct pdb_lnnums_t : pdb_lnnum_vec_t
{
pdb_lnnums_t() : inited(false) {}
bool get_item_bounds(rangeset_t *set) const;
int get_lnnum() const;
int get_colnum() const;
int get_end_lnnum() const;
int get_end_colnum() const;
bool inited;
};
//----------------------------------------------------------------------------
class pdb_access_t
{
public:
pdb_access_t(const pdbargs_t &args)
: pdbargs(args),
machine_type((uint32) -1),
dia_version(0),
base_address(BADADDR),
global_sym_id(BADSYM)
{
}
virtual ~pdb_access_t() {}
//----------------------------------------------------------------------
struct children_visitor_t
{
children_visitor_t()
: parent(NULL) {}
virtual HRESULT visit_child(pdb_sym_t &child) = 0;
virtual ~children_visitor_t() {}
pdb_sym_t *parent;
};
//-------------------------------------------------------------------------
struct dummy_visitor_t : public children_visitor_t
{
virtual HRESULT visit_child(pdb_sym_t &) override { return S_OK; }
};
//----------------------------------------------------------------------------
HRESULT iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor);
//----------------------------------------------------------------------------
virtual HRESULT do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor) = 0;
virtual HRESULT iterate_subtags(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor);
virtual HRESULT load(pdb_sym_t &sym, DWORD id) = 0;
// source-level debugging-specific
virtual HRESULT sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length) = 0;
virtual HRESULT sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum) = 0;
virtual HRESULT sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor) = 0;
virtual HRESULT sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor) = 0;
virtual HRESULT sip_retrieve_file_path(
qstring *out,
qstring *errbuf,
DWORD file_id) = 0;
virtual HRESULT sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &sym) = 0;
virtual HRESULT sip_find_files(
qvector<DWORD> *out,
const char *filename) = 0; // case insensitive search
// /source-level debugging-specific
//----------------------------------------------------------------------------
virtual DWORD get_global_symbol_id() const { return global_sym_id; }
virtual ea_t get_base_address() const { return base_address; }
virtual uint32 get_machine_type() const { return machine_type; }
virtual int get_dia_version() const { return dia_version; }
void set_global_symbol_id(DWORD _global_sym_id) { global_sym_id = _global_sym_id; }
void set_machine_type(uint32 _machine_type) { machine_type = _machine_type; }
void set_base_address(ea_t _base_address) { base_address = _base_address; }
void set_dia_version(int _dia_version) { dia_version = _dia_version; }
//----------------------------------------------------------------------------
virtual pdb_sym_t *create_sym(void *data=NULL, bool own=false) = 0;
pdb_sym_t *create_sym(DWORD sym_id)
{
pdb_sym_t *sym = create_sym();
if ( load(*sym, sym_id) != S_OK )
{
qstring err;
err.sprnt("Failed loading symbol data for ID %u", sym_id);
delete sym;
throw pdb_exception_t(err.c_str());
}
return sym;
}
//----------------------------------------------------------------------------
const pdbargs_t &pdbargs;
private:
uint32 machine_type;
int dia_version;
ea_t base_address;
DWORD global_sym_id;
DECLARE_UNCOPYABLE(pdb_access_t)
};
#endif // PDBACCESS__H

View File

@@ -0,0 +1,399 @@
//----------------------------------------------------------------------------
template <typename T>
struct dia_ptr_t
{
dia_ptr_t() : thing(NULL) {}
~dia_ptr_t()
{
if ( thing != NULL )
thing->Release();
}
T *thing;
};
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::_do_iterate_symbols_enumerator(
IDiaEnumSymbols *sym_enum,
children_visitor_t &visitor)
{
std::set<DWORD> seen;
HRESULT hr = S_OK;
while ( true )
{
ULONG celt = 0;
IDiaSymbol *pChild = NULL;
hr = sym_enum->Next(1, &pChild, &celt);
if ( FAILED(hr) || celt != 1 )
{
hr = S_OK; // end of enumeration
break;
}
pdb_sym_t *child = create_sym(pChild, true);
pdb_sym_janitor_t janitor_pType(child);
DWORD sym_id;
hr = child->get_symIndexId(&sym_id);
if ( hr != S_OK )
break;
// It seems we can, in some cases, iterate over the
// same child more than once.
// Fortunately, it appears to be the same symbol data;
// and not another symbol w/ the same ID
// See also: sip_iterate_symbols_at_ea()
if ( seen.insert(sym_id).second )
{
hr = visitor.visit_child(*child);
if ( FAILED(hr) )
break;
}
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::safe_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor)
{
HRESULT hr = E_FAIL;
IDiaEnumSymbols *pEnumSymbols;
try
{
QASSERT(30536, sym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &diasym = (dia_pdb_sym_t &)sym;
hr = dia_session->findChildren(diasym.data, type, NULL, nsNone, &pEnumSymbols);
if ( hr == S_OK )
{
hr = _do_iterate_symbols_enumerator(pEnumSymbols, visitor);
pEnumSymbols->Release();
}
}
catch ( const std::bad_alloc & )
{
// try to free some memory before quitting (and saving the idb)
delete this;
nomem("pdb");
}
catch ( const std::exception &e )
{
error("Unhandled C++ exception: %s", e.what());
}
catch ( ... )
{
error("Unhandled C++ exception!");
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor)
{
int code;
HRESULT hr = E_FAIL;
__try
{
hr = safe_iterate_children(sym, type, visitor);
}
__except ( code=GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER )
{
// complain to the user
ask_for_feedback(
"%s: %s\n"
"Is the corresponding PDB file valid?",
pdbargs.input_path.c_str(),
winerr(code));
// we may arrive here because we ran out of memory
// try to free some memory before quitting (and saving the idb)
delete this;
error(NULL); // and die... this will save the idb
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::load(pdb_sym_t &pdbsym, DWORD id)
{
IDiaSymbol *dia_sym;
HRESULT hr = dia_session->symbolById(id, &dia_sym);
if ( hr == S_OK )
{
QASSERT(30543, pdbsym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &sym = (dia_pdb_sym_t &)pdbsym;
sym.set_symbol_data(dia_sym, true);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::_copy_line_numbers(
pdb_lnnums_t *out,
IDiaEnumLineNumbers *enumerator) const
{
LONG count = 0;
HRESULT hr = enumerator->get_Count(&count);
if ( hr == S_OK )
{
IDiaLineNumber *lines[64];
ULONG got = 0;
for ( LONG i=0; i < count; i += got )
{
// Fetch many line number information at once
enumerator->Next(qnumber(lines), lines, &got);
if ( got == 0 )
break;
for ( ULONG j=0; j < got; j++ )
{
IDiaLineNumber *l = lines[j];
pdb_lnnum_t &lo = out->push_back();
l->get_virtualAddress(&lo.va);
l->get_length(&lo.length);
l->get_columnNumber(&lo.columnNumber);
l->get_columnNumberEnd(&lo.columnNumberEnd);
l->get_lineNumber(&lo.lineNumber);
l->get_lineNumberEnd(&lo.lineNumberEnd);
l->get_statement(&lo.statement);
IDiaSourceFile *f = NULL;
if ( l->get_sourceFile(&f) == S_OK )
{
f->get_uniqueId(&lo.file_id);
f->Release();
}
lines[j]->Release();
}
}
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length)
{
dia_ptr_t<IDiaEnumLineNumbers> pEnumLineNumbers;
HRESULT hr = dia_session->findLinesByVA(va, length, &pEnumLineNumbers.thing);
if ( hr == S_OK )
hr = _copy_line_numbers(out, pEnumLineNumbers.thing);
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( FAILED(hr) )
return hr;
dia_ptr_t<IDiaEnumSymbols> pEnumSymbols;
hr = pFile.thing->get_compilands(&pEnumSymbols.thing);
if ( FAILED(hr) )
return hr;
while ( true )
{
ULONG got = 0;
IDiaSymbol *compiland;
pEnumSymbols.thing->Next(1, &compiland, &got);
if ( got == 0 )
break;
dia_ptr_t<IDiaEnumLineNumbers> pEnumLineNumbers;
HRESULT hr2;
if ( lnnum == 0 )
hr2 = dia_session->findLines(
compiland,
pFile.thing,
&pEnumLineNumbers.thing);
else
hr2 = dia_session->findLinesByLinenum(
compiland,
pFile.thing,
lnnum,
colnum,
&pEnumLineNumbers.thing);
compiland->Release();
if ( hr == S_OK )
_copy_line_numbers(out, pEnumLineNumbers.thing);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor)
{
// See also: _do_iterate_symbols_enumerator
std::set<DWORD> seen;
ea_t cur = va;
while ( true )
{
if ( cur >= va + size )
break;
ea_t old = cur;
qnotused(old);
LONG disp;
IDiaSymbol *sym = NULL;
HRESULT hr = dia_session->findSymbolByVAEx(cur, tag, &sym, &disp);
if ( FAILED(hr) || sym == NULL )
break;
// perform all get_*'s on 'sym' _before_ the visitor is called: it might
// very well 'steal' the symbol & destroy it in case it's not needed.
// (see source_items_vec_builder_t::visit_child())
pdb_sym_t *psym = create_sym(sym, true);
pdb_sym_janitor_t janitor_psym(psym);
DWORD sym_id;
hr = psym->get_symIndexId(&sym_id);
if ( hr != S_OK )
break;
ULONGLONG length = 0;
sym->get_length(&length);
if ( seen.insert(sym_id).second )
{
hr = visitor.visit_child(*psym);
if ( FAILED(hr) )
break;
}
cur -= disp;
cur += length;
QASSERT(30169, cur > old); // to avoid endless loops - i do not know if they are possible
}
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( FAILED(hr) )
return hr;
dia_ptr_t<IDiaEnumSymbols> pEnumSymbols;
hr = pFile.thing->get_compilands(&pEnumSymbols.thing);
if ( hr == S_OK )
hr = _do_iterate_symbols_enumerator(pEnumSymbols.thing, visitor);
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_file_path(
qstring *out,
qstring *errbuf,
DWORD file_id)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( hr == S_OK )
{
BSTR path;
hr = pFile.thing->get_fileName(&path);
if ( hr == S_OK )
{
utf16_utf8(out, path);
SysFreeString(path);
}
}
if ( FAILED(hr) )
{
if ( errbuf != NULL )
*errbuf = winerr(hr);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::_copy_files_ids(
qvector<DWORD> *out,
IDiaEnumSourceFiles *enumerator) const
{
ULONG celt = 0;
IDiaSourceFile *file = NULL;
while ( enumerator->Next(1, &file, &celt) == S_OK && celt > 0 )
{
DWORD file_id;
if ( file->get_uniqueId(&file_id) == S_OK )
out->push_back(file_id);
file->Release();
}
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &sym)
{
// Retrieve source file name associated with the current symbol
QASSERT(30537, sym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &diasym = (dia_pdb_sym_t &)sym;
BSTR path;
HRESULT hr = diasym.data->get_sourceFileName(&path);
if ( hr == S_OK ) // cannot use SUCCEEDED(hr) because S_OK means success
{
dia_ptr_t<IDiaEnumSourceFiles> pEnumSourceFiles;
hr = dia_session->findFile(NULL, path, nsfFNameExt, &pEnumSourceFiles.thing);
SysFreeString(path);
if ( hr == S_OK )
_copy_files_ids(out, pEnumSourceFiles.thing);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_find_files(
qvector<DWORD> *out,
const char *filename)
{
qwstring fnamebuf;
wchar16_t *fname = NULL;
if ( filename != NULL )
{
qstring fnametmp = filename;
utf8_utf16(&fnamebuf, &fnametmp[0]);
fname = fnamebuf.begin();
}
dia_ptr_t<IDiaEnumSourceFiles> pEnumSourceFiles;
HRESULT hr = dia_session->findFile(
NULL,
fname,
nsfFNameExt | nsfCaseInsensitive,
&pEnumSourceFiles.thing);
if ( hr == S_OK )
_copy_files_ids(out, pEnumSourceFiles.thing);
return hr;
}

View File

@@ -0,0 +1,116 @@
#ifndef PDBLOCAL_HPP
#define PDBLOCAL_HPP
// The PDB related code that works on Windows and uses DIA
//----------------------------------------------------------------------------
class local_pdb_access_t : public pdb_access_t
{
public:
local_pdb_access_t(
const pdbargs_t &args,
IDiaDataSource *pSource,
IDiaSession *pSession,
IDiaSymbol *pGlobal)
: pdb_access_t(args),
dia_source(pSource),
dia_session(pSession),
dia_global(pGlobal)
{
}
virtual ~local_pdb_access_t()
{
#define RELEASE(thing) do { if ( thing != NULL ) { (thing)->Release(); thing = NULL; } } while ( false )
RELEASE(dia_global);
RELEASE(dia_session);
RELEASE(dia_source);
#undef RELEASE
set_global_symbol_id(BADSYM);
}
HRESULT init()
{
DWORD id;
HRESULT hr = dia_global->get_symIndexId(&id);
if ( hr != S_OK )
return hr;
set_global_symbol_id(id);
DWORD64 load_addr;
hr = dia_session->get_loadAddress(&load_addr);
if ( hr != S_OK )
return hr;
set_base_address(load_addr);
return S_OK;
}
virtual HRESULT do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor) override;
virtual HRESULT load(pdb_sym_t &sym, DWORD id) override;
virtual HRESULT sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length) override;
virtual HRESULT sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum) override;
virtual HRESULT sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor) override;
virtual HRESULT sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor) override;
virtual HRESULT sip_retrieve_file_path(
qstring *out,
qstring *errbuf,
DWORD file_id) override;
virtual HRESULT sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &sym) override;
virtual HRESULT sip_find_files(
qvector<DWORD> *out,
const char *name) override;
virtual pdb_sym_t *create_sym(void *data, bool own) override
{
IDiaSymbol *sym = (IDiaSymbol *)data;
return new dia_pdb_sym_t(this, sym, own);
}
pdb_sym_t *create_sym(DWORD sym_id) { return pdb_access_t::create_sym(sym_id); }
IDiaDataSource *dia_source;
IDiaSession *dia_session;
IDiaSymbol *dia_global;
private:
HRESULT safe_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor);
HRESULT _do_iterate_symbols_enumerator(
IDiaEnumSymbols *sym_enum,
children_visitor_t &visitor);
HRESULT _copy_line_numbers(
pdb_lnnums_t *out,
IDiaEnumLineNumbers *enumerator) const;
HRESULT _copy_files_ids(
qvector<DWORD> *out,
IDiaEnumSourceFiles *enumerator) const;
DECLARE_UNCOPYABLE(local_pdb_access_t)
};
#endif // PDBLOCAL_HPP

View File

@@ -0,0 +1,898 @@
#include <pro.h>
#include "pdbremote.hpp"
#include "varser.hpp"
// Since we're using the win32 local stup debugger at the moment,
// this is necessary.
#include <dbg.hpp>
//-------------------------------------------------------------------------
bool is_win32_remote_debugger_loaded()
{
return dbg != NULL && dbg->is_remote() && streq(dbg->name, "win32");
}
//----------------------------------------------------------------------------
HRESULT remote_pdb_sym_t::get_classParent(pdb_sym_t *out)
{
DWORD parent_id;
HRESULT hr = data->get_dword(t_classParentId, &parent_id);
if ( hr == S_OK )
hr = pdb_access->load(*out, parent_id);
return hr;
}
//----------------------------------------------------------------------------
HRESULT remote_pdb_sym_t::get_type(pdb_sym_t *out)
{
DWORD type_id;
HRESULT hr = data->get_dword(t_typeId, &type_id);
if ( hr == S_OK )
hr = pdb_access->load(*out, type_id);
return hr;
}
//----------------------------------------------------------------------------
HRESULT remote_pdb_sym_t::get_lexicalParent(pdb_sym_t *out)
{
DWORD lparent_id;
HRESULT hr = data->get_dword(t_lexicalParentId, &lparent_id);
if ( hr == S_OK )
hr = pdb_access->load(*out, lparent_id);
return hr;
}
//----------------------------------------------------------------------------
const uint32 sym_data_t::sizes[] =
{
sizeof(BOOL),
sizeof(DWORD),
sizeof(DWORD64),
sizeof(char *),
sizeof(LONG),
sizeof(ULONGLONG),
sizeof(VARIANT)
};
//----------------------------------------------------------------------------
sym_data_t::sym_data_t(
token_mask_t _tokens,
const uchar *buf,
size_t bufsize,
packing_info_t _packing,
bool *_warned)
: present(_tokens),
warned(_warned)
{
memset(counters, 0, sizeof(counters));
memset(children_infos, 0, sizeof(children_infos));
if ( _packing == SYMDAT_PACKED )
{
const uchar *ptr = buf;
const uchar *const end = buf + bufsize;
for ( uint64 bit = t_start; bit != t_end; bit <<= 1 )
{
sym_token_t token = sym_token_t(bit);
if ( !token_present(token) )
continue;
if ( is_sym_token_bool(token) )
{
counters[t_bool]++;
uint8 tmp = unpack_db(&ptr, end);
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_dword(token) )
{
counters[t_dword]++;
uint32 tmp = unpack_dd(&ptr, end);
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_dword64(token) )
{
counters[t_dword64]++;
uint64 tmp = unpack_dq(&ptr, end);
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_string(token) )
{
counters[t_string]++;
char *tmp = qstrdup(unpack_str(&ptr, end));
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_long(token) )
{
counters[t_long]++;
LONG tmp = unpack_dd(&ptr, end);
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_ulonglong(token) )
{
counters[t_ulonglong]++;
ULONGLONG tmp = unpack_dq(&ptr, end);
data.append(&tmp, sizeof(tmp));
}
else if ( is_sym_token_variant(token) )
{
counters[t_variant]++;
VARIANT var;
if ( varser_t::deserialize(var, &ptr, end) )
{
data.append(&var, sizeof(var));
}
else
{
if ( !*warned )
{
warning("The PDB file contains VARIANT items that cannot be deserialized.");
*warned = true;
}
}
}
else
{
INTERR(30200);
}
}
QASSERT(30201, data.size() == counters[t_bool] * sizes[t_bool]
+ counters[t_dword] * sizes[t_dword]
+ counters[t_dword64] * sizes[t_dword64]
+ counters[t_string] * sizes[t_string]
+ counters[t_long] * sizes[t_long]
+ counters[t_ulonglong] * sizes[t_ulonglong]
+ counters[t_variant] * sizes[t_variant]);
QASSERT(30202, ptr == end);
}
else
{
data.append(buf, bufsize);
// Not supported yet. All that's left to do
// is count the types (counters[]), though.
INTERR(30203);
}
}
//----------------------------------------------------------------------------
sym_data_t::~sym_data_t()
{
for ( int i = 0; i < SymTagMax; i++ )
{
children_t &children = children_infos[i];
if ( children.ids != NULL )
{
qfree(children.ids);
children.ids = NULL;
children.cnt = 0;
}
}
uint8 nstring = counters[t_string];
if ( nstring > 0 )
{
char **cur_str_ptr = (char **)string_ptr(t_string_start);
for ( uint8 i = 0; i < nstring; i++, cur_str_ptr++ )
qfree(*cur_str_ptr);
}
uint8 nvariant = counters[t_variant];
if ( nvariant > 0 )
{
VARIANT *cur_variant_ptr = (VARIANT *)variant_ptr(t_variant_start);
for ( uint8 i = 0; i < nvariant; i++, cur_variant_ptr++ )
if ( cur_variant_ptr->vt == VT_LPSTR )
qfree(cur_variant_ptr->punkVal);
}
warned = nullptr;
}
#define READ_IF_FOUND(type, fun) \
const type *ptr = fun##_ptr(token); \
if ( ptr == NULL ) \
{ \
return S_FALSE; \
} \
else \
{ \
*out = *ptr; \
return S_OK; \
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_bool(sym_token_t token, BOOL *out) const
{
READ_IF_FOUND(BOOL, bool)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_dword(sym_token_t token, DWORD *out) const
{
READ_IF_FOUND(DWORD, dword)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_dword64(sym_token_t token, DWORD64 *out) const
{
READ_IF_FOUND(DWORD64, dword64)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_string(sym_token_t token, qstring *out) const
{
READ_IF_FOUND(char *, string)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_dword(sym_token_t token, LONG *out) const
{
READ_IF_FOUND(LONG, long)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_ulonglong(sym_token_t token, ULONGLONG *out) const
{
READ_IF_FOUND(ULONGLONG, uint64)
}
//----------------------------------------------------------------------------
HRESULT sym_data_t::get_variant(sym_token_t token, VARIANT *out) const
{
READ_IF_FOUND(VARIANT, variant)
}
#undef READ_IF_FOUND
//----------------------------------------------------------------------------
const void *sym_data_t::any_ptr(sym_token_t token, sym_token_t start, sym_token_t end) const
{
if ( !token_present(token) )
return NULL;
static const sym_token_t ends[] =
{
t_bool_end,
t_dword_end,
t_dword64_end,
t_string_end,
t_long_end,
t_ulonglong_end,
t_variant_end,
};
CASSERT(qnumber(ends) == qnumber(counters));
CASSERT(qnumber(sizes) == qnumber(counters));
// count how many bytes we have to skip and determine the type size
uint32 type_size = 0;
const uchar *ptr = data.begin();
for ( int i=0; i < qnumber(ends); i++ )
{
if ( token <= ends[i] )
{
type_size = sizes[i];
break;
}
ptr += counters[i] * sizes[i];
}
QASSERT(30204, type_size != 0);
// how many tokens of our type we have to skip?
uint32 bit;
for ( bit = start; bit <= end; bit <<= 1 )
{
sym_token_t t = sym_token_t(bit);
if ( token_present(t) )
{
if ( t == token )
return ptr;
ptr += type_size;
}
}
return NULL; // did not find the requested token
}
//----------------------------------------------------------------------------
remote_pdb_access_t::~remote_pdb_access_t()
{
typedef std::map<DWORD,sym_data_t*>::iterator iter;
for ( iter it = cache.begin(), end = cache.end(); it != end; it++ )
delete it->second;
close_connection();
}
//----------------------------------------------------------------------------
void remote_pdb_access_t::close_connection()
{
if ( remote_session_id > 0 )
{
bytevec_t dummy;
perform_op(WIN32_IOCTL_PDB_CLOSE, dummy, NULL);
remote_session_id = -1;
}
if ( !was_connected && dbg != NULL )
dbg->term_debugger();
}
//----------------------------------------------------------------------
// load and connect to a remote win32 debugger, if necessary
bool remote_pdb_access_t::load_win32_debugger(void)
{
was_connected = false;
if ( dbg != NULL && !is_win32_remote_debugger_loaded() )
{
// a debugger is loaded, but it's not a remote win32
warning("Loading PDB symbols requires a remote win32 debugger. "
"Please stop the current debugging session and try again.");
return false;
}
if ( get_process_state() != DSTATE_NOTASK )
{
// the debugger is already connected
was_connected = true;
return true;
}
netnode pdbnode(PDB_NODE_NAME);
pdbnode.altset(PDB_LOADING_WIN32_DBG, true);
bool win32_dbg_loaded = load_debugger("win32", true) && dbg != NULL;
pdbnode.altdel(PDB_LOADING_WIN32_DBG);
if ( !win32_dbg_loaded )
{
warning("Could not load remote Win32 debugger.");
return false;
}
qstring server;
server = host[0] != '\0' ? host : "localhost";
qstring pass;
if ( pwd != NULL )
pass = pwd;
qstring dbg_errbuf;
while ( !dbg->init_debugger(server.c_str(), port, pass.c_str(), &dbg_errbuf) )
{
if ( batch ) // avoid endless (and useless) loop in batch mode
return false;
if ( !dbg_errbuf.empty() )
msg("%s\n", dbg_errbuf.begin());
// hrw
const char *winremote = inf_is_64bit() ? "win64_remote64.exe" : "win32_remote.exe";
qstring formstr;
formstr.sprnt(
"Remote PDB server\n"
"In order to load PDB information, IDA requires a running %s debugger server\n"
"running on a Windows host, but it could not connect to the %s debugger\n"
"at the current specified address.\n"
"Please make sure that %s is running there.\n\n"
"<#Name of the remote host#~H~ostname :q:1023:30::> <#Remote port number#Po~r~t:D::8::>\n"
"<#Password for the remote host#Pass~w~ord :q:1023:30::>\n"
"Hint: to change this permanently, edit pdb.cfg.\n\n",
winremote, winremote, winremote);
uval_t sport = port;
int r = ask_form(formstr.c_str(), &server, &sport, &pass);
if ( r != 1 )
return false;
port = sport;
}
msg("PDB: successfully connected to %s\n", server.c_str());
return true;
}
//----------------------------------------------------------------------------
#define REPORT_ERROR(Msg, Rc) \
do \
{ \
qfree(outbuf); \
qstrncpy(errbuf, Msg, sizeof(errbuf)); \
return Rc; \
} while ( false )
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::open_connection()
{
// Load win32 debugger (FIXME: Should just use an RPC client, not a full debugger!)
if ( !load_win32_debugger() )
return S_FALSE;
// Init remote.
bytevec_t oper;
compiler_info_t cc;
inf_get_cc(&cc);
oper.append(&cc, sizeof(cc));
oper.pack_str(pdbargs.pdb_path);
oper.pack_str(pdbargs.input_path);
oper.append(&pdbargs.pdb_sign, sizeof(pdbargs.pdb_sign));
oper.pack_str(pdbargs.spath);
oper.pack_ea64(get_base_address());
oper.pack_dd(pdbargs.flags);
void *outbuf = NULL;
ssize_t outsize = 0;
ioctl_pdb_code_t rc = send_ioctl(
WIN32_IOCTL_PDB_OPEN,
oper.begin(), oper.size(), &outbuf, &outsize);
if ( rc != pdb_ok || outsize < 1 )
REPORT_ERROR(
"PDB symbol extraction is not supported by the remote server",
E_FAIL);
// remote PDB session has become active
bytevec_t sidbuf;
{
const uchar *ptr = (const uchar *) outbuf;
const uchar *const end = ptr + outsize;
remote_session_id = unpack_dd(&ptr, end);
QASSERT(30493, remote_session_id > 0);
sidbuf.pack_dd(remote_session_id);
}
// now, do the polling game.
bool done = false;
while ( !done )
{
qfree(outbuf);
outbuf = NULL;
qsleep(100);
user_cancelled(); // refresh the output window
rc = send_ioctl(
WIN32_IOCTL_PDB_OPERATION_COMPLETE,
sidbuf.begin(), sidbuf.size(),
&outbuf, &outsize);
if ( rc != pdb_ok || outsize <= 0 )
REPORT_ERROR(
"remote server reported error while opening PDB",
E_FAIL);
const uchar *ptr = (const uchar *)outbuf;
const uchar *const end = ptr + outsize;
pdb_op_completion_t status = pdb_op_completion_t(unpack_dd(&ptr, end));
done = true; // only 'not complete' status will make us continue.
switch ( status )
{
case pdb_op_not_complete:
done = false;
break;
case pdb_op_complete:
{
set_global_symbol_id(unpack_dd(&ptr, end));
set_machine_type(unpack_dd(&ptr, end));
set_dia_version(unpack_dd(&ptr, end));
const char *fname = unpack_str(&ptr, end);
// TODO The printed path is wrong (test with pc_gdb_notepad.exe).
msg("PDB: opened \"%s\"\n", fname);
}
break;
case pdb_op_failure:
{
const char *errmsg = unpack_str(&ptr, end);
REPORT_ERROR(errmsg, E_FAIL);
// if opening pdb fails, win32_remote closes the MSDIA pdb
// session automatically.
remote_session_id = -1; //-V779 Unreachable code detected
}
break;
default:
break;
}
}
qfree(outbuf);
return remote_session_id > 0 ? S_OK : E_FAIL;
}
//----------------------------------------------------------------------------
ioctl_pdb_code_t remote_pdb_access_t::send_ioctl(
int fn,
const void *buf,
size_t size,
void **outbuf,
ssize_t *outsz)
{
if ( dbg == NULL )
return pdb_error;
deb(IDA_DEBUG_DEBUGGER, "PDB: send_ioctl(fn=%d, size=%" FMT_Z ")\n", fn, size);
// internal_ioctl() will either send the request to the debugger thread if
// it exists (i.e., we are in a debugging session), or perform it directly.
ioctl_pdb_code_t code = ioctl_pdb_code_t(internal_ioctl(fn, buf, size, outbuf, outsz));
// ioctl_pdb_code_t code = ioctl_pdb_code_t(internal_ioctl dbg->send_ioctl(fn, buf, size, outbuf, outsz));
deb(IDA_DEBUG_DEBUGGER, "PDB: send_ioctl(fn=%d) complete. Code=%d\n", fn, int(code));
return code;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::_do_iterate_symbols_ids(
const DWORD *ids,
size_t count,
enum SymTagEnum type,
children_visitor_t &visitor)
{
HRESULT hr = S_OK;
for ( size_t i = 0, n = count; i < n; ++i, ++ids )
{
DWORD tag;
pdb_sym_t *cur = create_sym(*ids);
pdb_sym_janitor_t janitor_cur(cur);
if ( type == SymTagNull
|| cur->get_symTag(&tag) == S_OK && tag == type )
{
hr = visitor.visit_child(*cur);
if ( FAILED(hr) )
break;
}
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT remote_pdb_access_t::do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor)
{
sym_data_t *symbol;
ioctl_pdb_code_t code = get_sym_data(sym, &symbol);
QASSERT(30205, code == pdb_ok);
QASSERT(30206, type < SymTagMax);
sym_data_t::children_t &children = symbol->children_infos[type];
if ( children.ids == NULL )
{
qvector<DWORD> children_ids;
code = fetch_children_infos(sym, type, &children_ids);
if ( code == pdb_ok )
{
children.cnt = children_ids.size();
children.ids = children_ids.extract();
}
}
HRESULT hr = E_FAIL;
if ( code == pdb_ok )
hr = _do_iterate_symbols_ids(
children.ids,
children.cnt,
type,
visitor);
return hr;
}
//----------------------------------------------------------------------------
HRESULT remote_pdb_access_t::load(pdb_sym_t &pdbsym, DWORD id)
{
sym_data_t *sd;
if ( get_sym_data(id, &sd) != pdb_ok )
return E_FAIL;
QASSERT(30544, pdbsym.whoami() == REMOTE_PDB_SYM);
remote_pdb_sym_t &sym = (remote_pdb_sym_t &)pdbsym;
sym.set_symbol_data(sd);
return S_OK;
}
#define HAS_REMAINING_OR_FAIL(Ptr, End) \
do \
{ \
if ( Ptr >= End ) \
return E_FAIL; \
} while ( false )
#define ALL_CONSUMED_OR_FAIL(Ptr, End) \
do \
{ \
if ( Ptr != End ) \
return E_FAIL; \
} while ( false )
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::handle_fetch_lnnums(
pdb_lnnums_t *out,
const bytevec_t &resp) const
{
const uchar *ptr = resp.begin();
const uchar *const end = resp.end();
uint32 nlines = unpack_dd(&ptr, end);
for ( uint32 i = 0; i < nlines; ++i )
{
HAS_REMAINING_OR_FAIL(ptr, end);
pdb_lnnum_t &ln = out->push_back();
ln.va = ULONGLONG(unpack_ea64(&ptr, end));
ln.length = unpack_dd(&ptr, end);
ln.columnNumber = unpack_dd(&ptr, end);
ln.columnNumberEnd = unpack_dd(&ptr, end);
ln.lineNumber = unpack_dd(&ptr, end);
ln.lineNumberEnd = unpack_dd(&ptr, end);
ln.file_id = unpack_dd(&ptr, end);
ln.statement = unpack_db(&ptr, end);
}
ALL_CONSUMED_OR_FAIL(ptr, end);
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length)
{
bytevec_t req, resp;
req.pack_ea64(va);
req.pack_dq(length);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA, req, &resp);
return code == pdb_ok ? handle_fetch_lnnums(out, resp) : E_FAIL;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum)
{
bytevec_t req, resp;
req.pack_dd(file_id);
req.pack_dd(lnnum);
req.pack_dd(colnum);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS, req, &resp);
return code == pdb_ok ? handle_fetch_lnnums(out, resp) : E_FAIL;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor)
{
qvector<DWORD> ids;
bytevec_t req;
req.pack_ea64(va);
req.pack_dq(size);
req.pack_dd(tag);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA, req, &ids);
if ( code != pdb_ok )
return E_FAIL;
return _do_iterate_symbols_ids(
ids.begin(),
ids.size(),
tag,
visitor);
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor)
{
qvector<DWORD> ids;
bytevec_t req;
req.pack_dd(file_id);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS, req, &ids);
if ( code != pdb_ok )
return E_FAIL;
return _do_iterate_symbols_ids(
ids.begin(),
ids.size(),
SymTagNull,
visitor);
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_retrieve_file_path(
qstring *out,
qstring *,
DWORD file_id)
{
bytevec_t req, resp;
req.pack_dd(file_id);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH, req, &resp);
if ( code != pdb_ok )
return E_FAIL;
const uchar *ptr = resp.begin();
const uchar *const end = resp.end();
HAS_REMAINING_OR_FAIL(ptr, end);
*out = unpack_str(&ptr, end);
ALL_CONSUMED_OR_FAIL(ptr, end);
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::handle_fetch_file_ids(
qvector<DWORD> *out,
const bytevec_t &resp) const
{
const uchar *ptr = resp.begin();
const uchar *const end = resp.end();
uint32 nfiles = unpack_dd(&ptr, end);
out->resize(nfiles);
for ( uint32 i = 0; i < nfiles; ++i )
{
HAS_REMAINING_OR_FAIL(ptr, end);
out->at(i) = unpack_dd(&ptr, end);
}
ALL_CONSUMED_OR_FAIL(ptr, end);
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &pdbsym)
{
QASSERT(30538, pdbsym.whoami() == REMOTE_PDB_SYM);
remote_pdb_sym_t &sym = (remote_pdb_sym_t &)pdbsym;
bytevec_t req, resp;
req.pack_dd(sym.data->get_id());
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES, req, &resp);
return code == pdb_ok ? handle_fetch_file_ids(out, resp) : E_FAIL;
}
//-------------------------------------------------------------------------
HRESULT remote_pdb_access_t::sip_find_files(
qvector<DWORD> *out,
const char *fileName)
{
bytevec_t req, resp;
req.pack_str(fileName);
ioctl_pdb_code_t code = perform_op(
WIN32_IOCTL_PDB_SIP_FIND_FILES, req, &resp);
return code == pdb_ok ? handle_fetch_file_ids(out, resp) : E_FAIL;
}
//----------------------------------------------------------------------------
DWORD remote_pdb_access_t::build_and_register_sym_data(
const uchar **raw,
const uchar *end)
{
DWORD child_sym = unpack_dd(raw, end);
token_mask_t tokens = unpack_dq(raw, end);
uint32 datasz = unpack_dd(raw, end);
const uchar *data = (const uchar *)unpack_obj_inplace(raw, end, datasz);
cache[child_sym] = new sym_data_t(tokens, data, datasz, SYMDAT_PACKED, &warned);
return child_sym;
}
//----------------------------------------------------------------------------
void remote_pdb_access_t::handle_fetch_response(
const uchar **ptr,
const uchar *end,
qvector<DWORD> *ids_storage)
{
// Build cache!
uint32 nchildren = 0;
unpack_obj(&nchildren, sizeof(nchildren), ptr, end);
if ( ids_storage != NULL )
ids_storage->reserve(nchildren);
for ( uint32 i = 0; i < nchildren; i++ )
{
DWORD created = build_and_register_sym_data(ptr, end);
if ( ids_storage != NULL )
ids_storage->push_back(created);
}
}
//----------------------------------------------------------------------------
ioctl_pdb_code_t remote_pdb_access_t::perform_op(
int op_type,
const bytevec_t &oper,
void *data)
{
void *outbuf = NULL;
ssize_t outsize = 0;
bytevec_t raw;
QASSERT(30494, remote_session_id > 0);
raw.pack_dd(remote_session_id);
if ( !oper.empty() )
raw.append(oper.begin(), oper.size());
ioctl_pdb_code_t rc = send_ioctl(op_type, raw.begin(), raw.size(), &outbuf, &outsize);
if ( rc != pdb_ok )
REPORT_ERROR(
"PDB symbol extraction is not supported by the remote server",
rc);
// msg(" ok\n");
// By now, the operation will be done. Let's parse
// the contents of the output buffer.
const uchar *ptr = (const uchar *)outbuf;
const uchar *const end = ptr + outsize;
switch ( op_type )
{
case WIN32_IOCTL_PDB_FETCH_SYMBOL:
case WIN32_IOCTL_PDB_FETCH_CHILDREN:
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA:
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS:
QASSERT(30207, outsize >= (4 /*(unpacked) nchildren*/));
handle_fetch_response(&ptr, end, (qvector<DWORD> *)data);
break;
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA:
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS:
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH:
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES:
case WIN32_IOCTL_PDB_SIP_FIND_FILES:
{
bytevec_t *bvout = (bytevec_t *) data;
bvout->append(outbuf, outsize);
}
break;
case WIN32_IOCTL_PDB_CLOSE:
break;
default:
INTERR(30208);
}
qfree(outbuf);
return pdb_ok;
}
//----------------------------------------------------------------------------
ioctl_pdb_code_t remote_pdb_access_t::fetch_children_infos(
pdb_sym_t &pdbsym,
enum SymTagEnum type,
qvector<DWORD> *children_ids)
{
QASSERT(30539, pdbsym.whoami() == REMOTE_PDB_SYM);
remote_pdb_sym_t &sym = (remote_pdb_sym_t &)pdbsym;
bytevec_t oper;
oper.pack_dd(sym.data->get_id());
oper.pack_dd(type);
// msg("Fetching children: 0x%x", sym);
return perform_op(WIN32_IOCTL_PDB_FETCH_CHILDREN, oper, children_ids);
}
//----------------------------------------------------------------------------
sym_data_t *remote_pdb_access_t::get_sym_data_from_cache(DWORD id)
{
typedef std::map<DWORD,sym_data_t*>::const_iterator citer;
citer it = cache.find(id);
if ( it != cache.end() )
return it->second;
return NULL;
}
//----------------------------------------------------------------------------
ioctl_pdb_code_t remote_pdb_access_t::get_sym_data(pdb_sym_t &pdbsym, sym_data_t **out)
{
QASSERT(30540, pdbsym.whoami() == REMOTE_PDB_SYM);
remote_pdb_sym_t &sym = (remote_pdb_sym_t &)pdbsym;
DWORD id = sym.data->get_id();
return get_sym_data(id, out);
}
//----------------------------------------------------------------------------
ioctl_pdb_code_t remote_pdb_access_t::get_sym_data(DWORD id, sym_data_t **out)
{
sym_data_t *found = get_sym_data_from_cache(id);
if ( found != NULL )
{
*out = found;
return pdb_ok;
}
else
{
bytevec_t oper;
oper.pack_dd(id);
ioctl_pdb_code_t rc = perform_op(WIN32_IOCTL_PDB_FETCH_SYMBOL, oper, NULL);
if ( rc == pdb_ok )
{
rc = get_sym_data(id, out);
QASSERT(30209, rc == pdb_ok);
}
return rc;
}
}

View File

@@ -0,0 +1,163 @@
#ifndef PDBREMOTE_HPP
#define PDBREMOTE_HPP
#include <network.hpp>
#include "../../dbg/win32/win32_rpc.h"
#include "pdbaccess.hpp"
// The PDB related code that works on Unix
// It connects to a Windows computer and asks to retrieve PDB info
//----------------------------------------------------------------------------
bool is_win32_remote_debugger_loaded();
//----------------------------------------------------------------------------
//-V:remote_pdb_access_t:730 not all members of a class are initialized inside the constructor
class remote_pdb_access_t : public pdb_access_t
{
public:
remote_pdb_access_t(
const pdbargs_t &args,
const char *_host,
int _port,
const char *_pwd)
: pdb_access_t(args),
host(_host),
port(_port),
pwd(_pwd),
remote_session_id(-1)
{
set_base_address(args.loaded_base);
}
virtual ~remote_pdb_access_t();
// Open connection, create PDB session.
HRESULT open_connection();
// Close PDB session, close connection.
void close_connection();
virtual HRESULT do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor) override;
virtual HRESULT load(pdb_sym_t &sym, DWORD id) override;
virtual HRESULT sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length) override;
virtual HRESULT sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum) override;
virtual HRESULT sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor) override;
virtual HRESULT sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor) override;
virtual HRESULT sip_retrieve_file_path(
qstring *out,
qstring *errbuf,
DWORD file_id) override;
virtual HRESULT sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &sym) override;
virtual HRESULT sip_find_files(
qvector<DWORD> *out,
const char *name) override;
virtual pdb_sym_t *create_sym(void *data, bool) override
{
sym_data_t *sym = (sym_data_t *)data;
return new remote_pdb_sym_t(this, sym);
}
pdb_sym_t *create_sym(DWORD sym_id) { return pdb_access_t::create_sym(sym_id); }
// Possibly remote operation.
// If NULL is returned, it means the symbol is not available, nor
// could it be fetched remotely.
ioctl_pdb_code_t get_sym_data(pdb_sym_t &sym, sym_data_t **);
ioctl_pdb_code_t get_sym_data(DWORD sym_id, sym_data_t **);
private:
HRESULT _do_iterate_symbols_ids(
const DWORD *ids,
size_t count,
enum SymTagEnum type,
children_visitor_t &visitor);
#define SAFE_GET(type) \
sym_data_t *sym_data; \
ioctl_pdb_code_t result = get_sym_data(sym, &sym_data); \
if ( result == pdb_ok ) \
return sym_data->get_##type(token, out); \
else \
return E_FAIL
// Build sym_data_t instance, and register it into the 'cache'.
DWORD build_and_register_sym_data(const uchar **raw, const uchar *end);
// Whenever fetch_children_infos() or get_sym_data() performs
// a remote operation, this is used to handle the response
// and add the fetched symbol data to the cache.
void handle_fetch_response(
const uchar **ptr,
const uchar *end,
qvector<DWORD> *ids_storage);
// Remote operation.
ioctl_pdb_code_t fetch_children_infos(
pdb_sym_t &sym,
enum SymTagEnum type,
qvector<DWORD> *children_ids);
HRESULT handle_fetch_lnnums(
pdb_lnnums_t *out,
const bytevec_t &resp) const;
HRESULT handle_fetch_file_ids(
qvector<DWORD> *out,
const bytevec_t &resp) const;
sym_data_t *get_sym_data_from_cache(DWORD id);
// Low-level interface used by open_connection(), fetch_children_infos(), and get_sym_data().
// 'fetch_type' is one of
// WIN32_IOCTL_PDB_OPEN,
// WIN32_IOCTL_PDB_FETCH_SYMBOL,
// WIN32_IOCTL_PDB_FETCH_CHILDREN
ioctl_pdb_code_t perform_op(int op_type, const bytevec_t &oper, void *data);
ioctl_pdb_code_t send_ioctl(
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize);
std::map<DWORD, sym_data_t*> cache;
const char *user_spath;
char errbuf[MAXSTR];
// For the moment, we'll channel all IOCTL requests
// through the debugger. Ideally, we should be able to just
// use a RPC client.
bool load_win32_debugger(void);
const char *host;
int port;
const char *pwd;
bool was_connected;
bool is_dbg_module;
int remote_session_id;
bool warned = false;
};
#endif // PDBREMOTE_HPP

1294
idasdk76/plugins/pdb/sip.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
#ifndef PDB_SIP_H
#define PDB_SIP_H
bool apply_debug_info(pdbargs_t &pdbargs);
#include "pdbaccess.hpp"
#endif // PDB_SIP_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
#pragma once
//----------------------------------------------------------------------------
enum cvt_code_t
{
cvt_failed,
cvt_ok,
cvt_typedef // conversion resulted in a typedef to a named type
};
//----------------------------------------------------------------------------
// PBD provides the offset of a bitfield inside a bitfield group.
// We subclass udt_member_t in order to keep that information separate from
// the 'offset' field.
struct pdb_udt_member_t : public udt_member_t
{
uint32 bit_offset; ///< member offset in bits from start of bitfield group
};
DECLARE_TYPE_AS_MOVABLE(pdb_udt_member_t);
typedef qvector<pdb_udt_member_t> pdbudtmembervec_t; ///< vector of pdb udt member objects
//----------------------------------------------------------------------------
// stripped-down version of udt_type_data_t with only the fields used by pdb.
struct pdb_udt_type_data_t : public pdbudtmembervec_t
{
size_t total_size; ///< total structure size in bytes
uint32 taudt_bits; ///< TA... and TAUDT... bits
bool is_union; ///< is union or struct?
pdb_udt_type_data_t(void)
: total_size(0),
taudt_bits(0),
is_union(false)
{
}
void convert_to_tinfo_udt(udt_type_data_t *out);
};
DECLARE_TYPE_AS_MOVABLE(pdb_udt_type_data_t);
//----------------------------------------------------------------------------
class til_builder_t
{
protected:
pdb_ctx_t &pv;
public:
//----------------------------------------------------------------------------
struct tpinfo_t
{
cvt_code_t cvt_code;
bool is_notype;
tinfo_t type;
til_t *ti; // FIXME: do we need this?
tpinfo_t(void) : cvt_code(cvt_ok), is_notype(false), ti(NULL) {}
tpinfo_t(til_t *_ti, const tinfo_t &t) : cvt_code(cvt_ok), is_notype(false), type(t), ti(_ti) {}
const char *dstr(void) const
{
if ( cvt_code == cvt_failed )
return "#cvt_failed";
static qstring res;
if ( !type.print(&res) )
res = "#print_failed";
return res.c_str();
}
};
//----------------------------------------------------------------------------
til_builder_t(pdb_ctx_t &_pv, til_t *_ti, pdb_access_t *_pa)
: pv(_pv),
unnamed_idx(0),
level(0),
ti(_ti),
pdb_access(NULL),
enregistered_bug(false)
{
set_pdb_access(_pa);
}
virtual ~til_builder_t()
{
typemap.clear();
tpdefs.clear();
handled.clear();
creating.clear();
unnamed_types.clear();
}
void set_pdb_access(pdb_access_t *_pdb_access)
{
pdb_access = _pdb_access;
}
typedef std::map<DWORD, tpinfo_t> typemap_t;
typedef std::map<DWORD, tinfo_t> tpdefs_t;
typedef std::set<DWORD> idset_t;
typedef std::map<qstring, int> creating_t;
typedef std::set<uint32> unnamed_t;
struct vft_info_t
{
udt_type_data_t udt; // collected vft members
qstring base0; // base vftable at offset 0
vft_info_t() { udt.taudt_bits |= TAUDT_VFTABLE; }
bool empty() const { return udt.empty() && base0.empty(); }
};
typedef std::map<qstring, vft_info_t> vftmap_t;
// remove `anonymous-namespace'::
// also remove `anonymous namespace'::
void remove_anonymous_namespaces(qstring &storage);
bool get_symbol_type(tpinfo_t *out, pdb_sym_t &sym, int *p_id);
bool retrieve_type(tpinfo_t *out, pdb_sym_t &sym, pdb_sym_t *parent, int *p_id);
bool retrieve_arguments(
pdb_sym_t &sym,
func_type_data_t &fi,
pdb_sym_t *funcSym);
cm_t convert_cc(DWORD cc0) const;
bool get_variant_string_value(qstring *out, pdb_sym_t &sym) const;
uint32 get_variant_long_value(pdb_sym_t &sym) const;
bool begin_creation(DWORD tag, const qstring &name, uint32 *p_id);
uint32 end_creation(const qstring &name);
bool is_member_func(tinfo_t *class_type, pdb_sym_t &typeSym, pdb_sym_t *funcSym);
bool is_frame_reg(int regnum) const;
bool is_complex_return(pdb_sym_t &sym) const;
bool is_unnamed_tag_typedef(const tinfo_t &tif) const;
bool is_intel386(DWORD machine_type) const;
bool is_arm(DWORD machine_type) const;
int get_symbol_funcarg_info(
funcarg_t *out,
pdb_sym_t &sym,
DWORD /*dwDataKind*/,
DWORD locType,
int stack_off);
void enum_function_args(pdb_sym_t &sym, func_type_data_t &args);
cvt_code_t verify_struct(pdb_udt_type_data_t &udt) const;
cvt_code_t verify_union(
pdb_udt_type_data_t *out,
pdb_udt_type_data_t::iterator p1,
pdb_udt_type_data_t::const_iterator p2) const;
cvt_code_t create_union(
tinfo_t *out,
size_t *p_total_size,
pdb_udt_type_data_t::iterator p1,
pdb_udt_type_data_t::const_iterator p2) const;
cvt_code_t convert_basetype(tpinfo_t *out, DWORD baseType, int size) const;
cvt_code_t make_vtable_struct(tinfo_t *out, pdb_sym_t &sym);
cvt_code_t convert_udt(tinfo_t *out, pdb_sym_t &sym, DWORD64 size);
cvt_code_t create_udt(tinfo_t *out, pdb_udt_type_data_t *udt, int udtKind, const char *udt_name) const;
cvt_code_t create_udt_ref(tinfo_t *out, pdb_udt_type_data_t *udt, int udt_kind) const;
cvt_code_t really_convert_type(tpinfo_t *out, pdb_sym_t &sym, pdb_sym_t *parent, DWORD tag);
cvt_code_t convert_type(
tpinfo_t *out,
pdb_sym_t &sym,
pdb_sym_t *parent,
DWORD type,
DWORD tag);
cvt_code_t handle_overlapping_members(pdb_udt_type_data_t *udt) const;
// Will iterate on children, and call handle_function_child()
HRESULT handle_symbols(pdb_sym_t &pGlobal);
HRESULT handle_globals(pdb_sym_t &pGlobal);
HRESULT handle_publics(pdb_sym_t &pGlobal);
HRESULT handle_types(pdb_sym_t &pGlobal);
HRESULT build(pdb_sym_t &pGlobal);
ea_t get_load_address() const { return pdb_access->get_base_address(); }
HRESULT handle_symbol(pdb_sym_t &sym);
size_t get_symbol_type_length(pdb_sym_t &sym) const;
void create_vftables();
// check for MS or IDA vftable name,
// get type ordinal of vftable
// returns the type is creating
bool get_vft_name(qstring *vft_name, uint32 *ord, const char *udt_name, uint32_t offset=0);
virtual HRESULT before_iterating(pdb_sym_t &global_sym);
virtual HRESULT after_iterating(pdb_sym_t &global_sym);
virtual bool iterate_symbols_once_more(pdb_sym_t & /*global_sym*/) { return false; }
virtual bool get_symbol_name(pdb_sym_t &sym, qstring &storage);
virtual bool handle_symbol_at_ea(
pdb_sym_t &sym,
DWORD tag,
ea_t ea,
qstring &name);
virtual void type_created(ea_t /*ea*/, int /*id*/, const char * /*name*/, const tinfo_t & /*ptr*/) const;
virtual void handle_function_type(pdb_sym_t &fun_sym, ea_t ea);
virtual HRESULT handle_function_child(
pdb_sym_t &fun_sym,
ea_t ea,
pdb_sym_t &child_sym,
DWORD child_tag,
DWORD child_loc_type);
virtual cvt_code_t handle_unnamed_overlapping_member(
pdb_udt_type_data_t * /*udt*/,
qstack<qstring> * /*union_names*/,
qstring * /*name*/) const
{
return cvt_ok;
}
protected:
typemap_t typemap; // id -> type info
tpdefs_t tpdefs; // id -> enum type defined in base til
idset_t handled; // set of handled symbols
creating_t creating;
unnamed_t unnamed_types;
vftmap_t vftmap; // vftable name -> vft info
int unnamed_idx;
int level;
public:
til_t *ti;
pdb_access_t *pdb_access;
bool enregistered_bug;
};

View File

@@ -0,0 +1,198 @@
#ifndef VARSER_HPP
#define VARSER_HPP
// Variant serializer/deserializer.
struct varser_t
{
#ifdef __NT__
static bool serialize(bytevec_t &out, const VARIANT &var);
#else
static bool deserialize(VARIANT &var, const uchar **in, const uchar *const end);
#endif
};
#ifdef __NT__
//-------------------------------------------------------------------------
bool varser_t::serialize(bytevec_t &out, const VARIANT &var)
{
out.pack_dw(var.vt);
if ( (var.vt & VT_BYREF) == VT_BYREF
|| (var.vt & VT_ARRAY) == VT_ARRAY )
{
return false;
}
const size_t sz_before = out.size();
switch ( var.vt )
{
case VT_EMPTY: // = 0x0000,
case VT_NULL: // = 0x0001,
break;
case VT_I2: // = 0x0002,
case VT_UI2: // = 0x0012,
out.pack_dw(var.uiVal);
break;
case VT_I4: // = 0x0003,
case VT_UI4: // = 0x0013,
out.pack_dd(var.ulVal);
break;
case VT_R4: // = 0x0004,
out.pack_dd(*(uint32*)&var.fltVal);
break;
case VT_R8: // = 0x0005,
out.pack_dq(*(uint64*)&var.dblVal);
break;
case VT_CY: // = 0x0006,
case VT_DATE: // = 0x0007,
break;
case VT_BSTR: // = 0x0008,
{
uint8 *ptr = (uint8*) var.bstrVal;
ptr -= 4;
uint32 bcnt = * (uint32*) ptr;
out.pack_buf(ptr + 4, bcnt);
}
break;
case VT_DISPATCH: // = 0x0009,
case VT_ERROR: // = 0x000A,
case VT_BOOL: // = 0x000B,
case VT_VARIANT: // = 0x000C,
case VT_UNKNOWN: // = 0x000D,
case VT_DECIMAL: // = 0x000E,
case VT_I1: // = 0x0010,
case VT_UI1: // = 0x0011,
out.pack_db(var.bVal);
break;
case VT_I8: // = 0x0014,
case VT_UI8: // = 0x0015,
out.pack_dq(var.ullVal);
break;
case VT_INT: // = 0x0016,
case VT_UINT: // = 0x0017,
case VT_HRESULT: // = 0x0019,
out.pack_dd(var.uintVal);
break;
case VT_VOID: // = 0x0018,
case VT_PTR: // = 0x001A,
case VT_SAFEARRAY: // = 0x001B,
case VT_CARRAY: // = 0x001C,
case VT_USERDEFINED: // = 0x001D,
case VT_LPSTR: // = 0x001E,
case VT_LPWSTR: // = 0x001F,
case VT_RECORD: // = 0x0024,
case VT_INT_PTR: // = 0x0025,
case VT_UINT_PTR: // = 0x0026,
break;
default: break;
}
return out.size() > sz_before;
}
#else
//-------------------------------------------------------------------------
bool varser_t::deserialize(VARIANT &var, const uchar **in, const uchar *const end)
{
var.vt = unpack_dw(in, end);
if ( (var.vt & VT_BYREF) == VT_BYREF
|| (var.vt & VT_ARRAY) == VT_ARRAY )
{
return false;
}
bool ok = false;
switch ( var.vt )
{
case VT_EMPTY: // = 0x0000,
case VT_NULL: // = 0x0001,
break;
case VT_I2: // = 0x0002,
case VT_UI2: // = 0x0012,
var.uiVal = unpack_dw(in, end);
ok = true;
break;
case VT_I4: // = 0x0003,
case VT_UI4: // = 0x0013,
var.ulVal = unpack_dd(in, end);
ok = true;
break;
case VT_R4: // = 0x0004,
{
uint32 res = unpack_dd(in, end);
var.fltVal = *(FLOAT*)&res;
ok = true;
}
break;
case VT_R8: // = 0x0005,
{
uint64 res = unpack_dq(in, end);
var.dblVal = *(DOUBLE*)&res;
ok = true;
}
break;
case VT_CY: // = 0x0006,
case VT_DATE: // = 0x0007,
break;
case VT_BSTR: // = 0x0008,
{
uint32 bcnt = unpack_dd(in, end);
uint32 nbytes = bcnt + 4 + 2; // +2 for terminating null character
QASSERT(30472, nbytes > bcnt); // check for integer overflow
uint8 *raw = (uint8 *)qalloc(nbytes);
if ( raw != NULL )
{
*(uint32*)raw = bcnt;
raw += 4;
unpack_obj(raw, bcnt, in, end);
raw[bcnt] = '\0';
raw[bcnt+1] = '\0';
var.bstrVal = raw;
ok = true;
}
}
break;
case VT_LPSTR: // = 0x001E,
case VT_LPWSTR: // = 0x001F,
{
char *tmp = qstrdup(unpack_str(in, end));
var.byref = tmp;
ok = true;
}
break;
case VT_DISPATCH: // = 0x0009,
case VT_ERROR: // = 0x000A,
case VT_BOOL: // = 0x000B,
case VT_VARIANT: // = 0x000C,
case VT_UNKNOWN: // = 0x000D,
case VT_DECIMAL: // = 0x000E,
case VT_I1: // = 0x0010,
case VT_UI1: // = 0x0011,
var.bVal = unpack_db(in, end);
ok = true;
break;
case VT_I8: // = 0x0014,
case VT_UI8: // = 0x0015,
var.ullVal = unpack_dq(in, end);
ok = true;
break;
case VT_INT: // = 0x0016,
case VT_UINT: // = 0x0017,
case VT_HRESULT: // = 0x0019,
var.uintVal = unpack_dd(in, end);
ok = true;
break;
case VT_VOID: // = 0x0018,
case VT_PTR: // = 0x001A,
case VT_SAFEARRAY: // = 0x001B,
case VT_CARRAY: // = 0x001C,
case VT_USERDEFINED: // = 0x001D,
case VT_RECORD: // = 0x0024,
case VT_INT_PTR: // = 0x0025,
case VT_UINT_PTR: // = 0x0026,
break;
default: break;
}
return ok;
}
#endif // __NT__
#endif // VARSER_HPP

View File

@@ -0,0 +1,21 @@
__FUZZ_PLUGINS__=1
SRC_PATH = $(IDA)plugins/
ifdef EXAMPLE
BIN_PATH = $(R)plugins-examples/
else
BIN_PATH = $(R)plugins/
endif
ifndef NO_DEFAULT_TARGETS
BASE_OBJS += $(F)$(PROC)$(O)
endif
include ../../module.mak
ifdef __NT__
ifndef NDEBUG
$(MODULES): PDBFLAGS = /PDB:$(@:$(DLLEXT)=.pdb)
endif
endif

View File

@@ -0,0 +1,12 @@
PROC=nec
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)nec$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp \
$(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
nec.cpp

View File

@@ -0,0 +1,317 @@
/*
* 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;
}
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
};

View File

@@ -0,0 +1,26 @@
# definitions for idapython (& other plugins dynamically linked to Python)
ifdef __NT__
PYTHON_CFLAGS := -I"$(PYTHON_ROOT)/include"
ifeq ($(PYTHON_VERSION_MAJOR),3)
PYTHON_LDFLAGS := "$(PYTHON_ROOT)/libs/python$(PYTHON_VERSION_MAJOR).lib"
else
PYTHON_LDFLAGS := "$(PYTHON_ROOT)/libs/python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR).lib"
endif
else
PYTHON_CFLAGS := $(shell $(PYTHON)-config --includes)
ifdef __APPLE_SILICON__
# to avoid codesigning complications on arm64 macs, we link against a stub tbd file. see plugins/idapython/tbd.readme
PYTHON_LDFLAGS := -L$(R) -lpython$(PYTHON_VERSION_MAJOR) -ldl -framework CoreFoundation
else
# Yay! https://bugs.python.org/issue36721
ifeq ($(PYTHON_VERSION_MAJOR),3)
USE_EMBED := $(shell [ $(PYTHON_VERSION_MINOR) -ge 8 ] && echo true)
endif
ifeq ($(USE_EMBED),true)
PYTHON_LDFLAGS := $(shell $(PYTHON)-config --ldflags --embed)
else
PYTHON_LDFLAGS := $(shell $(PYTHON)-config --ldflags)
endif
endif
endif

View File

@@ -0,0 +1,168 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QPainter>
#include "edge.h"
#include "node.h"
#include <math.h>
//lint -e1535 member function '' exposes lower access pointer member ''
//lint -e1536 member function '' exposes lower access member ''
//lint -e1537 const member function '' exposes pointer member '' as pointer to non-const
//lint -e1540 non-static pointer data member '' not deallocated nor zeroed by destructor
//lint -e2466 '' was used despite being marked as 'unused'
static const double Pi = 3.14159265358979323846264338327950288419717;
static const double TwoPi = 2.0 * Pi;
Edge::Edge(Node *_sourceNode, Node *_destNode)
: arrowSize(10)
{
setAcceptedMouseButtons(0);
source = _sourceNode;
dest = _destNode;
source->addEdge(this);
dest->addEdge(this);
adjust();
}
Edge::~Edge()
{
}
Node *Edge::sourceNode() const
{
return source;
}
void Edge::setSourceNode(Node *node)
{
source = node;
adjust();
}
Node *Edge::destNode() const
{
return dest;
}
void Edge::setDestNode(Node *node)
{
dest = node;
adjust();
}
void Edge::adjust()
{
if ( !source || !dest )
return;
QRectF srect = source->boundingRect();
QRectF drect = dest->boundingRect();
QLineF line(mapFromItem(source, srect.width() / 2, srect.height() / 2),
mapFromItem(dest, drect.width() / 2, drect.height() / 2));
qreal length = line.length();
prepareGeometryChange();
if ( length > qreal(40.) )
{
qreal line_angle = line.angle();
qreal angle = line_angle > 90. ? fmod(line_angle, 90.0) : line_angle;
qreal dist = qMax(angle, 45.0) - qMin(angle, 45.0);
dist += 80.0 - dist;
QPointF edgeOffset((line.dx() * dist) / length, (line.dy() * dist) / length);
sourcePoint = line.p1() + edgeOffset;
destPoint = line.p2() - edgeOffset;
qreal new_angle = QLineF(sourcePoint, destPoint).angle();
if ( qAbs(new_angle - line_angle) > 90. )
sourcePoint = destPoint = line.p1();
}
else
{
sourcePoint = destPoint = line.p1();
}
}
QRectF Edge::boundingRect() const
{
if ( !source || !dest )
return QRectF();
qreal penWidth = 1;
qreal extra = (penWidth + arrowSize) / 2.0;
QRectF r(sourcePoint, QSizeF(destPoint.x() - sourcePoint.x(),
destPoint.y() - sourcePoint.y()));
return r.normalized().adjusted(-extra, -extra, extra, extra);
}
void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
if ( !source || !dest )
return;
QLineF line(sourcePoint, destPoint);
if ( qFuzzyCompare(line.length(), qreal(0.)) )
return;
// Draw the line itself
painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter->drawLine(line);
// Draw the arrows
double angle = ::acos(line.dx() / line.length());
if ( line.dy() >= 0 )
angle = TwoPi - angle;
QPointF destArrowP1 = destPoint + QPointF(sin(angle - Pi / 3) * arrowSize,
cos(angle - Pi / 3) * arrowSize);
QPointF destArrowP2 = destPoint + QPointF(sin(angle - Pi + Pi / 3) * arrowSize,
cos(angle - Pi + Pi / 3) * arrowSize);
painter->setBrush(Qt::black);
painter->drawPolygon(QPolygonF() << line.p2() << destArrowP1 << destArrowP2);
}

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef EDGE_H
#define EDGE_H
#include <QGraphicsItem>
class Node;
class Edge : public QGraphicsItem
{
public:
Edge(Node *sourceNode, Node *destNode);
~Edge();
Node *sourceNode() const;
void setSourceNode(Node *node);
Node *destNode() const;
void setDestNode(Node *node);
void adjust();
enum { Type = UserType + 2 };
int type() const override { return Type; }
protected:
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
private:
Node *source, *dest;
QPointF sourcePoint;
QPointF destPoint;
qreal arrowSize;
};
#endif

View File

@@ -0,0 +1,235 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#include "graphwidget.h"
#include "edge.h"
#include "node.h"
#include <QDebug>
#include <QGraphicsScene>
#include <QWheelEvent>
#include <QTime>
#include <math.h>
//lint -e429 custodial pointer '' likely not freed nor returned
//lint -e665 unparenthesized parameter
//lint -e666 expression with side effects passed to repeated parameter
//lint -e1524 new in constructor for class '' which has no explicit destructor
//lint -e1793 invoking non-const member function
GraphWidget::GraphWidget()
: timerId(0)
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
QGraphicsScene *_scene = new QGraphicsScene(this);
_scene->setItemIndexMethod(QGraphicsScene::NoIndex);
_scene->setSceneRect(-400, -400, 800, 800);
setScene(_scene);
setCacheMode(CacheBackground);
setViewportUpdateMode(BoundingRectViewportUpdate);
setRenderHint(QPainter::Antialiasing);
setTransformationAnchor(AnchorUnderMouse);
setResizeAnchor(AnchorViewCenter);
Node *node1 = new Node(this);
Node *node2 = new Node(this);
Node *node3 = new Node(this);
Node *node4 = new Node(this);
centerNode = new Node(this);
Node *node6 = new Node(this);
Node *node7 = new Node(this);
Node *node8 = new Node(this);
Node *node9 = new Node(this);
_scene->addItem(node1);
_scene->addItem(node2);
_scene->addItem(node3);
_scene->addItem(node4);
_scene->addItem(centerNode);
_scene->addItem(node6);
_scene->addItem(node7);
_scene->addItem(node8);
_scene->addItem(node9);
_scene->addItem(new Edge(centerNode, node1));
_scene->addItem(new Edge(centerNode, node2));
_scene->addItem(new Edge(centerNode, node3));
_scene->addItem(new Edge(centerNode, node4));
_scene->addItem(new Edge(centerNode, node6));
_scene->addItem(new Edge(centerNode, node7));
_scene->addItem(new Edge(centerNode, node8));
_scene->addItem(new Edge(centerNode, node9));
node1->setPos(-100, -100);
node2->setPos(0, -100);
node3->setPos(100, -100);
node4->setPos(-100, 0);
centerNode->setPos(0, 0);
node6->setPos(100, 0);
node7->setPos(-100, 100);
node8->setPos(0, 100);
node9->setPos(100, 100);
scale(qreal(0.8), qreal(0.8));
setMinimumSize(400, 400);
setWindowTitle(tr("Elastic IDA Nodes"));
}
void GraphWidget::itemMoved()
{
if ( !timerId )
timerId = startTimer(10);
}
void GraphWidget::keyPressEvent(QKeyEvent *_event)
{
switch ( _event->key() )
{
case Qt::Key_Up:
centerNode->moveBy(0, -20);
break;
case Qt::Key_Down:
centerNode->moveBy(0, 20);
break;
case Qt::Key_Left:
centerNode->moveBy(-20, 0);
break;
case Qt::Key_Right:
centerNode->moveBy(20, 0);
break;
case Qt::Key_Plus:
scaleView(qreal(1.2));
break;
case Qt::Key_Minus:
scaleView(1 / qreal(1.2));
break;
case Qt::Key_Space:
case Qt::Key_Enter:
foreach ( QGraphicsItem *item, scene()->items() )
{
if ( qgraphicsitem_cast<Node *>(item) )
item->setPos(-150 + qrand() % 300, -150 + qrand() % 300);
}
break;
default:
QGraphicsView::keyPressEvent(_event);
break;
}
}
void GraphWidget::timerEvent(QTimerEvent *_event)
{
Q_UNUSED(_event);
QList<Node *> nodes;
foreach ( QGraphicsItem *item, scene()->items() )
{
if ( Node *node = qgraphicsitem_cast<Node *>(item) )
nodes << node;
}
foreach ( Node *node, nodes )
node->calculateForces();
bool itemsMoved = false;
foreach ( Node *node, nodes )
{
if ( node->_advance() )
itemsMoved = true;
}
if ( !itemsMoved )
{
killTimer(timerId);
timerId = 0;
}
}
void GraphWidget::wheelEvent(QWheelEvent *_event)
{
scaleView(pow((double)2, -_event->delta() / 240.0));
}
void GraphWidget::drawBackground(QPainter *painter, const QRectF &_rect)
{
Q_UNUSED(_rect);
// Shadow
QRectF _sceneRect = this->sceneRect();
QRectF rightShadow(_sceneRect.right(), _sceneRect.top() + 5, 5, _sceneRect.height());
QRectF bottomShadow(_sceneRect.left() + 5, _sceneRect.bottom(), _sceneRect.width(), 5);
if ( rightShadow.intersects(_rect) || rightShadow.contains(_rect) )
painter->fillRect(rightShadow, Qt::darkGray);
if ( bottomShadow.intersects(_rect) || bottomShadow.contains(_rect) )
painter->fillRect(bottomShadow, Qt::darkGray);
// Fill
QLinearGradient gradient(_sceneRect.topLeft(), _sceneRect.bottomRight());
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, Qt::lightGray);
painter->fillRect(_rect.intersected(_sceneRect), gradient);
painter->setBrush(Qt::NoBrush);
painter->drawRect(_sceneRect);
// Text
QRectF textRect(_sceneRect.left() + 4, _sceneRect.top() + 4,
_sceneRect.width() - 4, _sceneRect.height() - 4);
QString message(tr("Click and drag the nodes around, and zoom with the mouse "
"wheel or the '+' and '-' keys"));
QFont _font = painter->font();
_font.setBold(true);
_font.setPointSize(14);
painter->setFont(_font);
painter->setPen(Qt::lightGray);
painter->drawText(textRect.translated(2, 2), message);
painter->setPen(Qt::black);
painter->drawText(textRect, message);
}
void GraphWidget::scaleView(qreal scaleFactor)
{
qreal factor = matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if ( factor < 0.07 || factor > 100 )
return;
scale(scaleFactor, scaleFactor);
}

View File

@@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef GRAPHWIDGET_H
#define GRAPHWIDGET_H
#include <QGraphicsView>
//lint -esym(818, _o, _a) Pointer parameter '' could be declared as pointing to const
class Node;
class GraphWidget : public QGraphicsView
{
Q_OBJECT
public:
GraphWidget();
void itemMoved();
protected:
void keyPressEvent(QKeyEvent *event) override;
void timerEvent(QTimerEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void drawBackground(QPainter *painter, const QRectF &rect) override;
void scaleView(qreal scaleFactor);
private:
int timerId;
Node *centerNode;
};
#endif

View File

@@ -0,0 +1,18 @@
PROC=qproject
O1=moc_graphwidget
O2=graphwidget
O3=node
O4=edge
include ../qtplugin.mak
# MAKEDEP dependency list ------------------
$(F)edge$(O) : edge.cpp edge.h node.h
$(F)graphwidget$(O): edge.h graphwidget.cpp graphwidget.h node.h
$(F)moc_graphwidget$(O): $(F)moc_graphwidget.cpp graphwidget.h
$(F)node$(O) : edge.h graphwidget.h node.cpp node.h
$(F)qproject$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp graphwidget.h qproject.cpp

View File

@@ -0,0 +1,175 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QStyleOption>
#include "edge.h"
#include "node.h"
#include "graphwidget.h"
//lint -e665 unparenthesized parameter
//lint -e666 expression with side effects passed to repeated parameter
//lint -e790 possibly truncated multiplication
const char html[] =
"<span style=\"white-space: pre; font-family: FixedSys; color: blue; background: white\">"
"<span style=\"color:navy\">push </span><span style=\"color:green\">0</span>\n"
"<span style=\"color:navy\">push [ebp+</span><span style=\"color:green\">argv</span><span style=\"color:navy\">]</span>\n"
"<span style=\"color:navy\">call sub_4015B8</span>";
Node::Node(GraphWidget *graphWidget)
: graph(graphWidget)
{
setFlag(ItemIsMovable);
setFlag(ItemIsFocusable);
setFlag(ItemIsSelectable);
setFlag(ItemSendsGeometryChanges);
setCacheMode(DeviceCoordinateCache);
setZValue(-1);
setHtml(html);
}
void Node::addEdge(Edge *edge)
{
edgeList << edge;
edge->adjust();
}
QList<Edge *> Node::edges() const
{
return edgeList;
}
void Node::calculateForces()
{
if ( !scene() || scene()->mouseGrabberItem() == this )
{
newPos = pos();
return;
}
// Sum up all forces pushing this item away
qreal xvel = 0;
qreal yvel = 0;
foreach ( QGraphicsItem *item, scene()->items() )
{
Node *node = qgraphicsitem_cast<Node *>(item);
if ( !node )
continue;
QLineF line(mapFromItem(node, 0, 0), QPointF(0, 0));
qreal dx = line.dx();
qreal dy = line.dy();
double l = 2.0 * (dx * dx + dy * dy);
if ( l > 0 )
{
xvel += (dx * 150.0) / l;
yvel += (dy * 150.0) / l;
}
}
// Now subtract all forces pulling items together
double weight = (edgeList.size() + 1) * 100;
foreach ( Edge *edge, edgeList )
{
QPointF _pos;
if ( edge->sourceNode() == this )
_pos = mapFromItem(edge->destNode(), 0, 0);
else
_pos = mapFromItem(edge->sourceNode(), 0, 0);
xvel += _pos.x() / weight;
yvel += _pos.y() / weight;
}
if ( qAbs(xvel) < 0.1 && qAbs(yvel) < 0.1 )
xvel = yvel = 0;
QRectF sceneRect = scene()->sceneRect();
newPos = pos() + QPointF(xvel, yvel);
newPos.setX(qMin(qMax(newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10));
newPos.setY(qMin(qMax(newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10));
}
bool Node::_advance()
{
if ( newPos == pos() )
return false;
setPos(newPos);
return true;
}
QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
{
switch ( change )
{
case ItemPositionHasChanged:
foreach ( Edge *edge, edgeList )
edge->adjust();
graph->itemMoved();
break;
default:
break;
}
return QGraphicsTextItem::itemChange(change, value);
}
void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->fillRect(option->rect, Qt::white);
QGraphicsTextItem::paint(painter, option, widget);
}
void Node::mousePressEvent(QGraphicsSceneMouseEvent *_event)
{
update();
QGraphicsTextItem::mousePressEvent(_event);
}
void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *_event)
{
update();
QGraphicsTextItem::mouseReleaseEvent(_event);
}

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef NODE_H
#define NODE_H
#include <QGraphicsTextItem>
#include <QList>
class Edge;
class GraphWidget;
QT_BEGIN_NAMESPACE
class QGraphicsSceneMouseEvent;
QT_END_NAMESPACE
class Node : public QGraphicsTextItem
{
public:
Node(GraphWidget *graphWidget);
void addEdge(Edge *edge);
QList<Edge *> edges() const;
enum { Type = UserType + 1 };
int type() const override { return Type; }
void calculateForces();
bool _advance();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
QList<Edge *> edgeList;
QPointF newPos;
GraphWidget *graph;
};
#endif

View File

@@ -0,0 +1,135 @@
/*
* This is a sample plugin module. It demonstrates how to fully use
* the Qt environment in IDA.
*
*/
#include <QtGui>
#include <QtWidgets>
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
// include your own widget here
#include "graphwidget.h"
//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
TWidget *widget = nullptr;
plugin_ctx_t()
{
hook_event_listener(HT_UI, this);
}
~plugin_ctx_t()
{
// listeners are uninstalled automatically
// when the owner module is unloaded
widget = nullptr; // make lint happy
}
virtual bool idaapi run(size_t) override;
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};
//--------------------------------------------------------------------------
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list va)
{
if ( code == ui_widget_visible )
{
TWidget *l_widget = va_arg(va, TWidget *);
if ( l_widget == widget )
{
// widget is created, create controls
QWidget *w = (QWidget *) widget;
QHBoxLayout *mainLayout = new QHBoxLayout();
mainLayout->setMargin(0);
GraphWidget *userWidget = new GraphWidget();
mainLayout->addWidget(userWidget);
w->setLayout(mainLayout);
//lint -e429 mainLayout not freed
}
}
if ( code == ui_widget_invisible )
{
TWidget *l_widget = va_arg(va, TWidget *);
if ( l_widget == widget )
{
// widget is closed, destroy objects (if required)
widget = nullptr;
}
}
return 0;
}
//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
TWidget *g_widget = find_widget("Sample Qt Project");
if ( g_widget == nullptr )
{
widget = create_empty_widget("Sample Qt Project");
display_widget(widget, WOPN_DP_TAB|WOPN_RESTORE);
}
else
{
close_widget(g_widget, WCLS_SAVE);
}
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
if ( !is_idaq() )
return nullptr;
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
char comment[] = "This is a sample Qt Project plugin.";
char help[] =
"A sample plugin module\n"
"\n"
"This module shows you how to use fully the Qt environment in IDA.";
//--------------------------------------------------------------------------
// This is the preferred name of the plugin module in the menu system
// The preferred name may be overridden in plugins.cfg file
char wanted_name[] = "Qt Project Sample";
// This is the preferred hotkey for the plugin module
// The preferred hotkey may be overridden in plugins.cfg file
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
};

View File

@@ -0,0 +1,65 @@
include ../plugin.mak
CC_DEFS += QT_CORE_LIB
CC_DEFS += QT_DLL
CC_DEFS += QT_GUI_LIB
CC_DEFS += QT_NAMESPACE=QT
CC_DEFS += QT_THREAD_SUPPORT
CC_DEFS += QT_WIDGETS_LIB
CC_INCP += .
ifdef __LINUX__
CC_F += -fPIC
else ifdef __NT__
CFLAGS += /GS
CFLAGS += /wd4946 # reinterpret_cast used between related classes
CFLAGS += /wd4826 # Conversion from 'ptr32' to 'int64' is sign-extended. This may cause unexpected runtime behavior.
CFLAGS += /wd4628 # Digraphs not supported. Avoids errors on things such as: "template<> inline void swap<::QT::QByteArray>"
CFLAGS += /wd4718 # 'QT::QMapNode<int,int>::destroySubTree' : recursive call has no side effects, deleting
CFLAGS += /wd4481 # warning C4481: nonstandard extension used: override specifier 'override'
endif
ifdef __MAC__
PREF=$(QTDIR)lib/
CC_INCP += $(PREF)QtCore.framework/Headers
CC_INCP += $(PREF)QtGui.framework/Headers
CC_INCP += $(PREF)QtWidgets.framework/Headers
CFLAGS += -F$(PREF)
LIBS += $(PREF)QtCore.framework/QtCore
LIBS += $(PREF)QtGui.framework/QtGui
LIBS += $(PREF)QtWidgets.framework/QtWidgets
# We must change the library path in the plugin so they become relative
# to the ida executable. Otherwise the plugin loads the second copy of
# these libraries and crashes IDA
CHANGE_LIBPATH=install_name_tool -change \
$(QTDIR)lib/$1.framework/Versions/4/$1 \
@executable_path/../Frameworks/$1.framework/Versions/4/$1 $@
POSTACTION=@$(call CHANGE_LIBPATH,QtGui); \
$(call CHANGE_LIBPATH,QtWidgets); \
$(call CHANGE_LIBPATH,QtCore)
else
CC_INCP += $(QTDIR)include
CC_INCP += $(QTDIR)include/QtCore
CC_INCP += $(QTDIR)include/QtGui
CC_INCP += $(QTDIR)include/QtWidgets
ifdef __LINUX__
PREF=$(QTDIR)lib/lib
POST=.so
endif # __LINUX__
ifdef __NT__
PREF=$(QTDIR)lib/
ifdef NDEBUG
POST=$(A)
else
POST=d$(A)
endif
endif
LIBS += $(PREF)Qt5Core$(POST)
LIBS += $(PREF)Qt5Gui$(POST)
LIBS += $(PREF)Qt5Widgets$(POST)
endif
$(F)moc_%.cpp: %.h
$(QTDIR)bin/moc -I. $< > $@
# Add $(F) to vpath for $(F)moc_*$(O).
vpath %.cpp $(F)

View File

@@ -0,0 +1,13 @@
PROC=qwindow
O1=moc_myactions
include ../qtplugin.mak
# MAKEDEP dependency list ------------------
$(F)moc_myactions$(O): $(F)moc_myactions.cpp myactions.h
$(F)qwindow$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
qwindow.cpp

View File

@@ -0,0 +1,14 @@
#include <QtWidgets>
class MyActions : public QObject
{
Q_OBJECT
public:
MyActions(QObject *_parent) : QObject(_parent) {}
private slots:
void clicked();
};

View File

@@ -0,0 +1,148 @@
/*
* This is a sample plugin module. It demonstrates how to create your
* own window and populate it with Qt widgets.
*
* Note: we discourage using this plugin and using Qt widgets from C++.
* Such plugins will depends on the exact version of the Qt libraries
* and C++ compiler used to build them. Hex-Rays may change
* both Qt libraries and C++ compiler at any time used to build IDA,
* without an advance warning. Second, IDA uses a custom build of
* the Qt libraries, with a namespace.
* Please consider using PyQt to create Qt widgets, it is more robust
* and does not suffer from these problems.
*/
#include <QtWidgets>
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include "myactions.h"
//--------------------------------------------------------------------------
//lint -e1762
void MyActions::clicked()
{
info("Button is clicked");
}
//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t, public event_listener_t
{
TWidget *widget = nullptr;
plugin_ctx_t()
{
hook_event_listener(HT_UI, this);
}
~plugin_ctx_t()
{
// listeners are uninstalled automatically
// when the owner module is unloaded
widget = nullptr; // make lint happy
}
virtual bool idaapi run(size_t) override;
virtual ssize_t idaapi on_event(ssize_t code, va_list va) override;
};
//--------------------------------------------------------------------------
ssize_t idaapi plugin_ctx_t::on_event(ssize_t code, va_list va)
{
if ( code == ui_widget_visible )
{
TWidget *l_widget = va_arg(va, TWidget *);
if ( l_widget == widget )
{
QWidget *w = (QWidget *) widget;
MyActions *actions = new MyActions(w);
// create a widget
QPushButton *b = new QPushButton("Click here", w);
// connect the button to a slot
QObject::connect(b, SIGNAL(clicked()), actions, SLOT(clicked())); //lint !e2666 expression with side effects
// position and display it
b->move(50, 50);
b->show();
msg("Qt widget is displayed\n");
//lint -esym(429, actions, b) not freed
}
}
else if ( code == ui_widget_invisible )
{
TWidget *l_widget = va_arg(va, TWidget *);
if ( l_widget == widget )
{
// user defined widget is closed, destroy its controls
// (to be implemented)
msg("Qt widget is closed\n");
widget = nullptr;
}
}
return 0;
}
//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
TWidget *g_widget = find_widget("Sample Qt subwindow");
if ( g_widget == nullptr )
{
widget = create_empty_widget("Sample Qt subwindow");
display_widget(widget, WOPN_DP_TAB|WOPN_RESTORE);
}
else
{
close_widget(g_widget, WCLS_SAVE);
widget = nullptr; // make lint happy
}
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
if ( !is_idaq() )
return nullptr;
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
static const char comment[] = "This is a sample Qt plugin.";
static const char help[] =
"A sample plugin module\n"
"\n"
"This module shows you how to create a Qt window.";
//--------------------------------------------------------------------------
// 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[] = "Create Qt subwindow";
// 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
};

View File

@@ -0,0 +1,34 @@
This directory contains sample plugin modules for IDA.
Plugin modules are accessible by the user in two ways:
- they appear as menu items in menu Edit|Plugins
- they can be invoked by hotkeys
A plugin has full access to the database and can interact with
the user.
IDA looks for plugins in PLUGINS subdirectory.
In this directory there is also a configuration file.
It is not necessary for a plugin to appear in the configuration file.
Even if a plugin is not there IDA will load it.
The configuration file allows the user to reassign the hotkeys,
to change the plugin name as it appears in the menu or to change
the argument passed to the plugin.
A plugin has one exported entry (it should have the "PLUGIN" name).
The entry is a plugin descriptor (plugin_t).
It contains pointers to the following functions:
- init: is called when the plugin is loaded
- run: is called when the user calls the plugin
- term: is called before the plugin is unloaded
run() function is the function which will do the real work.
It has full access to the database (see include files for the
description of IDA API). Also it can interact with the user
(most of these functions are in kernwin.hpp file).

View File

@@ -0,0 +1,20 @@
ifdef EXAMPLE
BIN_PATH = $(R)plugins-examples/
else
BIN_PATH = $(R)plugins/
endif
INSTALLED_SCRIPTS = $(addprefix $(BIN_PATH), $(SCRIPTS))
all: $(INSTALLED_SCRIPTS)
$(BIN_PATH)%.py: %.py
$(Q)$(CP) $? $@
$(BIN_PATH)%.idc: %.idc
$(Q)$(CP) $? $@
.PHONY: uninstall
uninstall::
rm -rf $(INSTALLED_SCRIPTS)

View File

@@ -0,0 +1,52 @@
#include <idc.idc>
class myplugmod_t
{
myplugmod_t()
{
this.wanted_name = "Sample IDC plugin";
}
run(arg)
{
msg("%s: run() has been called with %d\n", this.wanted_name, arg);
return (arg % 2) == 0;
}
~myplugmod_t()
{
msg("%s: unloaded\n", this.wanted_name);
}
}
class myplugin_t
{
myplugin_t()
{
this.flags = PLUGIN_MULTI;
this.comment = "This is a sample IDC plugin";
this.help = "This is help";
this.wanted_name = "Sample IDC plugin";
this.wanted_hotkey = "Alt-F6";
}
init()
{
msg("%s: init() has been called\n", this.wanted_name);
return myplugmod_t();
}
run(arg)
{
msg("%s: ERROR: run() has been called for global object!\n", this.wanted_name);
return (arg % 2) == 0;
}
term()
{
msg("%s: ERROR: term() has been called (should never be called)\n", this.wanted_name);
}
}
static PLUGIN_ENTRY()
{
return myplugin_t();
}

View File

@@ -0,0 +1,9 @@
include ../../allmake.mak
SCRIPTS += idcplugin.idc
SCRIPTS += pyplugin.py
SCRIPTS += procext.py
include ../script_plg.mak

View File

@@ -0,0 +1,69 @@
import idaapi
mymnem = "linux_kernel_call"
"""
This is a sample plugin for extending processor modules
It extends the IBM PC processor module to disassemble
"int 80h"
as
"%s"
for ELF files
(c) Hex-Rays
""" % mymnem
NN_kernel_call = idaapi.CUSTOM_INSN_ITYPE
#--------------------------------------------------------------------------
class linux_idp_hook_t(idaapi.IDP_Hooks):
def __init__(self):
idaapi.IDP_Hooks.__init__(self)
def ev_ana_insn(self, insn):
if idaapi.get_bytes(insn.ea, 2) != b"\xCD\x80":
return False
insn.itype = NN_kernel_call
insn.size = 2
return True
def ev_out_mnem(self, outctx):
if outctx.insn.itype != NN_kernel_call:
return 0
outctx.out_custom_mnem(mymnem)
return 1
#--------------------------------------------------------------------------
class linuxprocext_t(idaapi.plugin_t):
# Processor fix plugin module
flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
comment = ""
wanted_hotkey = ""
help = "Replaces int 0x80 with %s" % mymnem
wanted_name = mymnem
def init(self):
self.prochook = None
if idaapi.ph_get_id() != idaapi.PLFM_386 or idaapi.cvar.inf.filetype != idaapi.f_ELF:
print("linuxprocext_t.init() skipped!")
return idaapi.PLUGIN_SKIP
self.prochook = linux_idp_hook_t()
self.prochook.hook()
print("linuxprocext_t.init() called!")
return idaapi.PLUGIN_KEEP
def run(self, arg):
pass
def term(self):
print("linuxprocext_t.term() called!")
if self.prochook:
self.prochook.unhook()
#--------------------------------------------------------------------------
def PLUGIN_ENTRY():
return linuxprocext_t()

View File

@@ -0,0 +1,34 @@
import ida_idaapi, ida_kernwin
class myplugmod_t(ida_idaapi.plugmod_t):
def __del__(self):
ida_kernwin.msg("unloaded myplugmod\n")
def run(self, arg):
ida_kernwin.msg("run() called with %d!\n" % arg)
return (arg % 2) == 0
class myplugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_UNL | ida_idaapi.PLUGIN_MULTI
comment = "This is a sample Python plugin"
help = "This is help"
wanted_name = "Sample Python plugin"
wanted_hotkey = "Alt-F8"
#def __del__(self):
#ida_kernwin.msg("unloaded globally\n")
def init(self):
ida_kernwin.msg("init() called!\n")
return myplugmod_t()
def run(self, arg):
ida_kernwin.msg("ERROR: run() called for global object!\n")
return (arg % 2) == 0
def term(self):
ida_kernwin.msg("ERROR: term() called (should never be called)\n")
def PLUGIN_ENTRY():
return myplugin_t()

View File

@@ -0,0 +1,11 @@
PROC=snapshots
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)snapshots$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp \
$(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
snapshots.cpp

Some files were not shown because too many files have changed in this diff Show More