Latest available version: IDA and decompilers v8.4.240320 see all releases
Hex-Rays logo State-of-the-art binary code analysis tools
email icon

Scripting with IDA Pro has always been a very handy feature, not only when used in scripts but also in expressions, breakpoint conditions, form fields, etc…
In IDA Pro 5.6 we improved the IDC language and made it more convenient to use by adding objects, exceptions, support for strings with embedded zeroes, string slicing and references.

General language improvements

Local variables can now be declared and initialized anywhere within a function:

static func1()
{
  Message("Hello world\n");
  auto s = AskStr("Enter new name", "noname00");
  // ...
  auto i = 0;
  // ....
}

Global variables can be declared (in a function or in the global scope) with the extern keyword:

// Global scope
extern g_count; // Global variables cannot be initialized during declaration
static main()
{
  extern g_another_var;
  g_another_var = 123;
  g_count = 1;
}

Functions can be passed around and used as callbacks:

static my_func(a,b)
{
  Message("a=%d, b=%d\n", a, b);
}
static main()
{
  auto f = my_func;
  f(1, 2);
}

Strings can now contain the zero character thus allowing you to use IDC strings like buffers. This is extremely useful when used with Appcall to call functions that expect buffers:

auto s = "\x83\xF9\x00\x74\x10";
Message("len=%d\n", strlen(s));
// Construct a buffer with strfill()
s = strfill('!', 100);
Message("len=%d\n", strlen(s));

Strings can be easily manipulated with slices (Python style):

#define QASSERT(x) if (!(x)) { Warning("ASSERT: " #x); }
auto x = "abcdefgh";
// get string slice
QASSERT(x[1] == "b");
QASSERT(x[2:] == "cdefgh");
QASSERT(x[:3] == "abc");
QASSERT(x[4:6] == "ef");
// set string slice
x[0]   = "A";           QASSERT(x == "Abcdefgh");
x[1:3] = "BC";          QASSERT(x == "ABCdefgh");
// delete part of a string
x[4:5] = "";            QASSERT(x == "ABCdfgh");
// patch part of the string with numbers
x[0:4] = 0x11223344;

Strings and numbers are always passed by value in IDC, but now it is possible to pass variables by reference (using the ampersand operator):

static incr(a)
{
  a++;
}
static main()
{
  auto i = 1;
  incr(&i);
  Message("i=%d\n", i);
}

Note that objects (described below) are always passed by reference.

IDC classes


Classes can now be declared in IDC. All classes derive from the built-in base class object:

auto o = object();
o.ea = here;
o.flag = 0;

User objects can be defined with the class keyword:

class testclass
{
  testclass(name)
  {
    Message("constructing: %s\n", name);
    this.name = name;
  }
  ~testclass()
  {
    Message("destructing: %s\n", this.name);
  }
  set_name(n)
  {
    Message("testclass.set_name -> old=%s new=%s\n", this.name, n);
    this.name = n;
  }
  get_name()
  {
    return this.name;
  }
}
static f1(n)
{
  auto o1 = testclass("object in f1()");
  o1.set_name(n);
}
static main()
{
  auto o2 = testclass("object2 in main()");
  Message("calling f1()\n");
  f1("new object1 name");
  Message("returned from f1()\n");
}

Which outputs the following when executed:

constructing: object2 in main()
calling f1()
constructing: object in f1()
testclass.set_name -> old=object in f1() new=new object1 name
destructing: new object1 name
returned from f1()
destructing: object2 in main()

To enumerate all the attributes in an object:

auto attr_name;
auto o = object();
o.attr1 = "value1";
o.attr2 = "value2";
for ( attr_name=firstattr(o); attr_name != 0; attr_name=nextattr(o, attr_name) )
  Message("->%s: %s\n", attr_name, getattr(o, attr_name));

If object attribute names are numbers then they can be accessed with the subscript operator:

class list
{
  list()
  {
    this.__count = 0;
  }
  size()
  {
    return this.__count;
  }
  add(e)
  {
    this[this.__count++] = e;
  }
}
static main()
{
  auto a = list();
  a.add("hello");
  a.add("world");
  a.add(5);
  auto i;
  for (i=a.size()-1;i>=0;i--)
    print(a[i]);
}

IDC classes also support inheritance:

class testclass_extender: testclass
{
  testclass_extender(id): testclass('asdf')
  {
    this.id = id;
  }
  // Override a method and then call the base version
  set_name(n)
  {
    Message("testclass_extender-> %s\n", n);
    testclass::set_name(this, n);
  }
}

They also support getattr/setattr hooking like in Python:

class attr_hook
{
  attr_hook()
  {
    this.id = 1;
  }
  // setattr will trigger for every attribute assignment
  __setattr__(attr, value)
  {
    Message("setattr: %s->", attr);
    print(value);
    setattr(this, attr, value);
  }
  // getattr will only trigger for non-existing attributes
  __getattr__(attr)
  {
    Message("getattr: '%s'\n", attr);
    if ( attr == "magic" )
      return 0x5f8103;
    // Ofcourse this will cause an exception since
    // we try to fetch a non-existing attribute
    return getattr(this, attr);
  }
}

Exceptions

Normally when a runtime error occurs, the script will abort and the interpret will display the runtime error message. With the use of exception handling, one can catch runtime errors:

static test_exceptions()
{
  // variable to hold the exception information
  auto e;
  try
  {
    auto a = object();
    // Try to read an invalid attribute:
    Message("a.name=%s\n", a.name);
  }
  catch ( e )
  {
    Message("Exception occured. Exception dump follows:\n");
    print(e);
  }
}

Resulting in the following output:

Executing function 'main'...
Exception occured. Exception dump follows:
object
description: "No such attribute: object.name"
file: "C:\\Temp\\ida56.idc"
func: "test_exceptions"
line:          91.       5Bh
pc:          31.       1Fh
qerrno:        1538.      602h

IDC debugging tips

Last but not least, we would like to mention two useful IDC debugging tips.

The first (we used it previously) involves the print() function:

// Print variables in the message window
// This function print text representation of all its arguments to the output window.
// This function can be used to debug IDC scripts
void    print           (...);

This function can be very handy when used to print a variable of any type especially objects and all their nested attributes.

And the second tip involves the use of the command window to evaluate commands. The trick is to type an IDC statement without a terminating semicolon.
To illustrate, we will first use the DecodeInstruction() with a semicolon:

idc56_semi.gif

And now the same thing, repeated, without a semicolon would automatically invoke the print() against the returned result, thus:

idc56_nosemi.gif

Although we said two debugging tips, but here’s the third: you can use the peroid key (“.”) to jump from an IDA View to the command window and the escape key to return to the IDA View.
The script snippets used in this blog entry can be downloaded from here.