In this post I’ll present some new things in IDA 6.2. There’s a new control, the code viewer, some additions to forms and the introduction of timers to discuss. All these new features have been exposed to the SDK, so that our users can benefit from them too. 😉
The first new thing I’m going to talk about is the code viewer. This control is just a container for a generic custom viewer. In fact, I just took the custom viewer sample plugin and added some lines to add the code viewer:
// create a custom viewer si->cv = create_custom_viewer("", NULL, &s1, &s2, &s1, 0, &si->sv); // create a code viewer container for the custom viewer si->codeview = create_code_viewer(form, si->cv, CDVF_LINEICONS); // set handlers for the code viewer set_code_viewer_line_handlers(si->codeview, lines_click, NULL, NULL, lines_icon, lines_linenum); // draw maximal 2 icons in the lines widget set_code_viewer_lines_icon_margin(si->codeview, 2);
After having created the custom viewer with create_custom_viewer, it is possible to create a code viewer by passing the custom viewer as argument to create_code_viewer.
The code viewer basically features for the moment a widget left to the embedded custom viewer called ‘the line widget’. This control can show the current line number, either automatically or by providing it ourselves. It can also show one or more icons and it informs us about user interaction such as mouse clicks.
As you can see, create_code_viewer was called specifying the CDVF_LINEICONS flag, which instructs the code viewer that we want icons to be drawn inside the line widget.
Available flags are:
#define CDVF_NOLINES 0x0001 // don't show line numbers #define CDVF_LINEICONS 0x0002 // icons can be drawn over the line control #define CDVF_STATUSBAR 0x0004 // keep the status bar in the custom viewer
However, specifying the flag to show the icons is not enough. We also need to specify how the maximum number of icons we want to display on a single line:
// draw maximal 2 icons in the lines widget set_code_viewer_lines_icon_margin(si->codeview, 2);
Then there’s the callback we have set by calling set_code_viewer_line_handlers:
static int idaapi lines_icon( TCustomControl * /*cv*/, const place_t *p, int *x, void * /*ud*/) { bool b = p->touval(NULL) == 6 && *x == 0; // setting the highest bit signals that there's another icon // to draw on the current line int icon_id = *x == 0 ? 51 : 177; return b ? 0x80000000 | icon_id : icon_id; }
This callback needs some explanation. The line widget calls this callback for every line. The argument x represents the position of the icon to draw. Thus, for every line the callback is called the first time with *x == 0. The return value of the callback is the id of the icon to display. If you want to display another icon for the current line, then set the highest bit in the return value. If you don’t want to display an icon at all for the current line, simply return -1.
You might wonder why x is a pointer. Imagine you don’t want to display an icon at position 0, but skip that position and display an icon at the next one. This can be done without calling the callback again, since we can set the value of x. Remember that you can load custom icons and use them in the code viewer.
Apart from the usual mouse and context menu callbacks, there’s a handy callback to be notified when the user clicks in the space reserved for icons:
static void idaapi lines_click( TCustomControl * /*cv*/, const place_t *p, int pos, int /*shift*/, void * /*ud*/) { if ( p != NULL ) msg("Line click at position: %d\n", pos); else msg("Click occurred outside of a line\n"); }
This is useful to set/unset an icon for instance.
I’ve also created another sample plugin with the code viewer featuring a hex view.
This can give you some more ideas of what this control might good for. Most of the logic behind this sample stands in the custom view and not in the code view, which, in this case, handles everything automatically without the use of callbacks:
// create a code viewer container for the custom view si->hexview = create_code_viewer(form, si->cv); // set the radix and alignment for the offsets set_code_viewer_lines_radix(si->hexview, 16); set_code_viewer_lines_alignment(si->hexview, si->data.size() > 0xFFFFFFFF ? 16 : 8);
Forms were certainly lacking two very important controls: a generic combo box and a multi-line edit. The combo box comes in two variants: editable and read-only. This is how to declare a combo:
<title:b:is_editable:width::>
When is_editable is omitted or zero, then the combo is read-only, otherwise it’s editable. Every combo takes two arguments. The first argument is a qstrvec_t * to populate the combo with items, while the second one is either an int * or a qstring * to specify the current selection. If the combo is read-only, then the second argument is an int * specifying an index into the qstrvec_t list, otherwise it’s a qstring * with the current text of the combo. Getting and setting the value of a combo follows the same int/qstring rule.
A multi-line edit is declared in this way:
<title:t::width::>
This control is more complex than the combo and requires a structure as argument:
// Multi line text control: used to embed a text control in a form struct textctrl_info_t { size_t cb; // size of this structure qstring text; // in, out: text control value uint16 flags; #define TXTF_AUTOINDENT 0x0001 // auto-indent on new line #define TXTF_ACCEPTTABS 0x0002 // Tab key inserts 'tabsize' spaces #define TXTF_READONLY 0x0004 // text cannot be edited (but can be selected and copied) #define TXTF_SELECTED 0x0008 // shows the field with its text selected #define TXTF_MODIFIED 0x0010 // gets/sets the modified status #define TXTF_FIXEDFONT 0x0020 // the control uses IDA's fixed font uint16 tabsize; // how many spaces a single tab will indent textctrl_info_t(): cb(sizeof(textctrl_info_t)), flags(0), tabsize(0) { } };
Most of the fields are self-evident, but two flags require an explanation. TXTF_ACCEPTTABS tells the control that the user can use tabs and they will be converted to the number of spaces specified by tabsize. TXTF_AUTOINDENT enables auto-indentation, not only when return is pressed, but also by removing tab block spaces on backspace. This feature is very useful when you want code to be entered for instance. In fact, the new script dialog in IDA 6.2 was created by using it.
Don’t forget the TXTF_FIXEDFONT flag to use a fixed font. Here’s a small sample demonstrating both the combo and edit:
//-------------------------------------------------------------------------- static int idaapi modcb(int fid, form_actions_t &fa) { switch ( fid ) { case 10: { msg("The selection of the combo has changed!\n"); // set the text of edit to the text of the current combo item int sel; if ( fa.get_field_value(10, &sel) ) { qstrvec_t *list = (qstrvec_t *)fa.get_ud(); textctrl_info_t ti; ti.flags = TXTF_SELECTED; ti.text = list->at(sel); fa.set_field_value(11, &ti); } } break; case 11: msg("The multi-line edit text has changed!\n"); break; } return 1; } //-------------------------------------------------------------------------- static void idaapi run(int) { static const char form[] = "Combo - Multi-line Edit sample\n" "%/%*" " \n" " \n" "\n"; qstrvec_t list; static const char *items[] = { "first", "second", "third", "fourth" }; for ( int i = 0; i < qnumber(items); i++ ) list.push_back(items[i]); int sel = 1; textctrl_info_t ti; ti.flags = TXTF_SELECTED; ti.text = "Some text"; if ( AskUsingForm_c(form, modcb, &list, &list, &sel, &ti) > 0 ) { msg("Combo selection: %s\n", list[sel]); msg("Text: %s\n", ti.text.c_str()); } }
In this small sample you can see another small addition to the forms syntax: following the usual %/ to specify the callback of the form there’s a %*. This new sequence makes it possible to specify a user data pointer (&list), which can later on be retrieved from the callback like this:
qstrvec_t *list = (qstrvec_t *)fa.get_ud();
Both the combo box and the multi-line edit can be used in the text version of IDA as well.
Timers are very easy to use and are available in the text version of IDA as well. These are the prototypes of the functions:
qtimer_t register_timer(int interval, int (idaapi *callback)(void *ud), void *ud); bool unregister_timer(qtimer_t t)
Timer functions are thread-safe and the callback is executed in the context of the main thread. The callback can return -1 to unregister the timer, while any other return greater than or equal to 0 defines the new interval of the timer.
// the timer event message will be displayed 5 times int idaapi timer_callback(void *) { static int i = 0; msg("timer event\n"); return ++i == 5 ? -1 : 1000; } // register the timer with a 1-second interval register_timer(1000, timer_callback, NULL);
That’s all! You can download both code viewer plugin samples from here.