New IDC improvement in IDA Pro 5.6
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.
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:static func1() { Message("Hello world\n"); auto s = AskStr("Enter new name", "noname00"); // ... auto i = 0; // .... }
Functions can be passed around and used as callbacks:// 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; }
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:static my_func(a,b) { Message("a=%d, b=%d\n", a, b); } static main() { auto f = my_func; f(1, 2); }
Strings can be easily manipulated with slices (Python style):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 and numbers are always passed by value in IDC, but now it is possible to pass variables by reference (using the ampersand operator):#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;
Note that objects (described below) are always passed by reference.static incr(a) { a++; } static main() { auto i = 1; incr(&i); Message("i=%d\n", i); }
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:auto o = object(); o.ea = here; o.flag = 0;
Which outputs the following when executed: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"); }
To enumerate all the attributes in an object: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()
If object attribute names are numbers then they can be accessed with the subscript operator: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));
With this knowledge, we can write a simple IDC list class:auto o = object(); o[0] = "zero"; o[1] = "one";
IDC classes also support inheritance: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]); }
They also support getattr/setattr hooking like in Python: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); } }
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:Resulting in the following output: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); } }
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:This function can be very handy when used to print a variable of any type especially objects and all their nested attributes.// 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 (...);
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:

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

The script snippets used in this blog entry can be downloaded from here.

The IDA Pro book
Comments
Guys, if you are looking for a new target to disassemble/decompile, I have a suggestion. There exist .NET assemblies that are hybrids. They have both managed, and unmanaged code in them. They call them "Mixed mode assemblies". In these assemblies, if you tell IDA that it's a .NET assembly, you just get what is in essence, the function prototype, and no body. If you choose the x86 option, you get code, but no function names.
IMHO, it would be really cool if you could process both globs of data, and fill in the function bodies with the x86 code that belongs there.
Just my $.02.
Posted by: Some user | February 6, 2010 04:32 AM
Support in IDA for disassembling mixed managed + native code would be great (or even just a way to disassemble the native code parts and match those functions to where they get called from the managed parts)
Posted by: Jonathan Wilson | February 16, 2010 05:59 AM
Thanks guys for your comments and suggestions.
We may look into making such an addition but nothing is definite yet.
Posted by: Elias Bachaalany
|
February 18, 2010 01:48 PM