Hex Blog
February 5th, 2010, 15:17
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:Global variables can be declared (in a function or in the global scope) with the extern keyword:Functions can be passed around and used as callbacks:Strings can now contain the zero character thus allowing you to use IDC strings like buffers. This is extremely useful when used with Appcall ("http://hexblog.com/2010/01/introducing_the_appcall_featur_1.html") to call functions that expect buffers:Strings can be easily manipulated with slices (Python style):Strings and numbers are always passed by value in IDC, but now it is possible to pass variables by reference (using the ampersand operator):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:User objects can be defined with the class keyword:Which outputs the following when executed:To enumerate all the attributes in an object:If object attribute names are numbers then they can be accessed with the subscript operator:With this knowledge, we can write a simple IDC list class:IDC classes also support inheritance:They also support getattr/setattr hooking like in Python: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:Resulting in the following output: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:This function can be very handy when used to print a variable of any type especially objects and all their nested attributes.</br>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:
http://hexblog.com/ida_pro/pix/idc56_semi.gif
And now the same thing, repeated, without a semicolon would automatically invoke the print() against the returned result, thus:
http://hexblog.com/ida_pro/pix/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 ("http://hexblog.com/ida_pro/files/idc56.idc").
http://hexblog.com/2010/02/new_idc_improvement_in_ida_pro_1.html
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:
Code:
static func1()
{
Message("Hello world\n";
auto s = AskStr("Enter new name", "noname00";
// ...
auto i = 0;
// ....
}
Code:
// 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;
}
Code:
static my_func(a,b)
{
Message("a=%d, b=%d\n", a, b);
}
static main()
{
auto f = my_func;
f(1, 2);
}
Code:
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));
Code:
#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;
Code:
static incr(a)
{
a++;
}
static main()
{
auto i = 1;
incr(&i);
Message("i=%d\n", i);
}
Classes can now be declared in IDC. All classes derive from the built-in base class object:
Code:
auto o = object();
o.ea = here;
o.flag = 0;
Code:
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";
}
Code:
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()
Code:
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));
Code:
auto o = object();
o[0] = "zero";
o[1] = "one";
Code:
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]);
}
Code:
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);
}
}
Code:
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);
}
}
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:
Code:
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);
}
}
Code:
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
Last but not least, we would like to mention two useful IDC debugging tips.
The first (we used it previously) involves the print() function:
Code:
// 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 (...);
To illustrate, we will first use the DecodeInstruction() with a semicolon:
http://hexblog.com/ida_pro/pix/idc56_semi.gif
And now the same thing, repeated, without a semicolon would automatically invoke the print() against the returned result, thus:
http://hexblog.com/ida_pro/pix/idc56_nosemi.gif
Although we said two debugging tips, but here's the third: you can use the peroid key ("."

The script snippets used in this blog entry can be downloaded from here ("http://hexblog.com/ida_pro/files/idc56.idc").
http://hexblog.com/2010/02/new_idc_improvement_in_ida_pro_1.html