IDC
IDA中支持进行脚本运行,可以通过编写脚本通过脚本对文件进行处理,甚至可以通过编写IDC脚本编写自动化漏洞审计的脚本。
IDC是ida中支持的一门与C语言类似的语言,但是它是解释型的,并不是编译型的,于此同时IDC还融合了一些python中的元素以方便一些内容的处理。
执行脚本
执行IDC脚本一共有三种方式
- idc命令行(菜单栏file->idc command)
- 脚本文件 (菜单栏file->script file)
- python命令行(菜单栏file->python command)
ida语言基础
注释
idc中使用C++风格的“//”进行单行注释;
采用c风格的“/* */”进行多行注释。
帮助系统
ida为用户提供了一个很完备的帮助系统,可以使用F1快捷键打开帮助系统,其中点击“index of idc functions”可以看到对应一些idc的函数列表。
idc变量
idc在一个语句中可以生命多个变量,但是idc不支持c语言风格的数组、指针、结构体、联合等复杂的数据结构。
idc是一种松散的语言,变量没有明确的类型,其主要使用三种数据类型:整形(long)、字符串型、浮点值。
idc支持全局变量和局部变量:
- 局部变量(auto)
auto add,reg,val; //多个变量同时声明,未初始化
auto valinit=0; //声明同时初始化
- 全局变量(extern)
extern outval;
extern illeval="wrong" //非法定义,声明全局变量时不能进行初始化
static main(){
extern insideval;
outval="global string" //为全局变量赋值
insideval=1;
}
There are two kinds of variables in IDC:
- local variables: they are created at the function entry and destroyed at the exit - global variables: they are created at the compilation time and destroyed when the database is closed
A variable can contain:
- LONG: a 32-bit signed long integer (64-bit in 64-bit version of IDA) - INT64: a 64-bit signed long integer - STR: a character string - FLOAT: a floating point number (extra precision, up to 25 decimal digits) - OBJECT: an object with attributes and methods (a concept very close to C++ class) more - REF: a reference to another variable - FUNC: a function reference
A local variable is declared this way:
auto var1; auto var2 = <expr>;
Global variables are declared like this:
extern var;
Global variables can be redefined many times. IDA will silently ignore subsequent declarations. Please note that global variables cannot be initialized at the declaration time.
All C and C++ keywords are reserved and cannot be used as a variable name.
While it is possible to declare a variable anywhere in the function body, all variables are initialized at the function entry and all of them are destroyed only at the exit. So, a variable declared in a loop body will not be reinitialized at each loop iteration, unless explicitly specified with an assignment operator.
If a variable or function name cannot be recognized, IDA tries to resolve them using the names from the disassembled application. In it succeeds, the name is replaced by its value in the disassembly listing. For example:
.data:00413060 errtable dd 1 ; oscode .data:00413060 dd 16h ; errnocode msg("address is: %x\n", _errtable);
will print 413060. If the label denotes a structure, it is possible to refer to its fields:
msg("address is: %x\n", _errtable.errnocode);
will print 413064. Please note that IDA does not try to read the data but just returns the address of the structure field. The field address can also be calculated using the get_field_ea function.
NOTE: The processor register names can be used in the IDC scripts when the debugger is active. Reading from such a variable return the corresponding register value. Writing to such a variable modifies the register value in the debugged process. Such variables are accessible only when the application is in the suspended mode.
NOTE: another way to emulate global scope variables is to use array functions and create global persistent arrays.
idc函数
idc中也可以自定义函数,其声明方式为:
static func(arg1,arg2,arg3)
{
statements ...
}
用户定义函数不需要进行指定特定的参数类型,因为在需要的时候程序会自动进行转化。如果需要函数返回指定的值需要使用return进行指定,否则默认不显示返回一个值的函数都将返回零值。
An IDC function always returns a value. There are 2 kinds of functions:
- built-in functions - user-defined functions
A user-defined function is declared this way:
static func(arg1,arg2,arg3) { statements ... }
It is not necessary to specify the parameter types because all necessary type conversions are performed automatically.
By default all function arguments are passed by value, except:
- objects are always passed by reference - functions are always passed by reference - it is possible to pass a variable by reference using the & operator
If the function to call does not exist, IDA tries to resolve the name using the debugged program labels. If it succeeds, an dbg_appcall is performed.
idc语句
idc中支持C中的语句,除了switch。
expression; (expression-statement)
if (expression) statement
if (expression) statement else statement
for ( expr1; expr2; expr3 ) statement
while (expression) statement
do statement while (expression);
break;
continue;
return <expr>;
return; the same as 'return 0;'
{ statements... }
try statement catch ( var ) statement
throw <expr>;
; (empty statement)
In IDC there are the following statements:
expression; (expression-statement) if (expression) statement if (expression) statement else statement for ( expr1; expr2; expr3 ) statement while (expression) statement do statement while (expression); break; continue; return <expr>; return; the same as 'return 0;' { statements... } try statement catch ( var ) statement throw <expr>; ; (empty statement)
Please note that the ‘switch’ statement is not supported.
idc表达式
idc几乎都能支持C语言中的操作运算表达(加减乘除、判等家族),但是明确说明不支持+=
。
在进行操作运算的时候,只有操作中存在64bit的操作,那么其他操作也会编程64bit的。
IDC: Expressions
In the IDC expressions you can use almost all C operations except:
complex assignment operations as '+='
Constants are defined more or less like in C, with some minor differences.
There are four type conversion operations:
long(expr) floating point numbers are truncated during conversion char(expr) float(expr) __int64(expr)
However, explicit type conversions are rarely required because all type conversions are made automatically:
- addition: if both operands are strings, string addition is performed (strings are concatenated); if both operands are objects, object combination is performed (a new object is created) if floating point operand exists, both operands are converted to floats; otherwise both operands are converted to longs; - subtraction/multiplication/division: if floating point operand exists, both operands are converted to floats; if both operands are objects and the operation is subtraction, object subtraction is performed (a new object is created) otherwise both operands are converted to longs; - comparisons (==,!=, etc): if both operands are strings, string comparison is performed; if floating point operand exists, both operands are converted to floats; otherwise both operands are converted to numbers; - all other operations: operand(s) are converted to longs;
If any of the long operands is 64bit, the other operand is converted to 64bit too.
There is one notable exception concerning type conversions: if one operand is a string and the other is zero (0), then a string operation is performed. Zero is converted to an empty string in this case.
The & operator is used to take a reference to a variable. References themselves cannot be modified once created. Any assignment to them will modify the target variable. For example:
auto x, r; r = &x; r = 1; // x is equal to 1 now
References to references are immediately resolved:
auto x, r1, r2; r1 = &x; r2 = &r1; // r2 points to x
Since all non-object arguments are passed to functions by value, references are a good way to pass arguments by reference.
idc预定义符号
idc有一些符号是提前定义好了的,其内容和含义如下。
_NT_ IDA is running under MS Windows
_LINUX_ IDA is running under Linux
_MAC_ IDA is running under Mac OS X
_UNIX_ IDA is running under Unix (linux or mac)
_EA64_ 64-bit version IDA
_QT_ GUI version of IDA (Qt)
_GUI GUI version of IDA
_TXT_ Text version of IDA
_IDA_VERSION_ The current IDA version. For example: "7.5"
_IDAVER_ The current, numerical IDA version. For example: "750" means v7.5
idc字符串操作(切片)
idc中对于字符串的操作应该是借鉴了python,其string类型的操作支持切片操作(slices)。
str[i1:i2] - substring from i1 to i2. i2 is excluded,If i1 >= i2, empty string is returned.
str[idx] - one character substring at 'idx'.
this is equivalent to str[idx:idx+1]
str[:idx] - substring from the beginning of the string to idx
this is equivalent to str[0:idx]
str[idx:] - substring from idx to the end of the string
this is equivalent to str[idx:0x7fffffff]
IDC: Slices
The slice operator can be applied IDC objects are strings.
For strings, the slice operator denotes a substring:
str[i1:i2] - substring from i1 to i2. i2 is excluded str[idx] - one character substring at 'idx'. this is equivalent to str[idx:idx+1] str[:idx] - substring from the beginning of the string to idx this is equivalent to str[0:idx] str[idx:] - substring from idx to the end of the string this is equivalent to str[idx:0x7fffffff]
Any indexes that are out of bounds are silently adjusted to correct values. If i1 >= i2, empty string is returned. Negative indexes are used to denote positions counting from the end of the string.
String slices can be used on the right side of an assignment. For example:
str[0:2] = "abc";
will replace 2 characters at the beginning of the string by “abc”.
For objects, the slice operator denotes a subset of attributes. It can be used to emulate arrays:
auto x = object(); x[0] = value1; x[1] = "value2";
x[i1:i2] denotes all attributes with numeric values between i1 and i2 (i2 is excluded).
Any non-numeric attributes are ignored by the slice operator.
idc异常处理
idc异常处理中,可以使用的表达语句:
auto e;
try
{
... some statements that cause a runtime error...
}
catch ( e )
{
// e holds the exception information
// it is an instance of the exception class
}
throw xx; #抛出
IDC: Exceptions
Any runtime error generates an exception. Exceptions terminate the execution. It is possible to catch an exception instead of terminating the execution:
auto e; try { ... some statements that cause a runtime error... } catch ( e ) { // e holds the exception information // it is an instance of the exception class }
The try/catch blocks can be nested. If the current function has no try/catch blocks, the calling function will be examined, and so on, until we find a try/catch block or exit the main function. If no try/catch block is found, an unhandled exception is reported.
It is also possible to throw an exception explicitly. Any object can be thrown. For example:
throw 5;
will throw value ‘5’.
idc程序
如果只是需要进行简单的查询或者查看,可以直接编写个别行的函数完成编写,但是如果一个脚本应用需要执行大量的IDC程序,并且还可能会在很多场景下需要重复使用,那么我们可能需要创建一个独立的IDC程序文件。
IDC程序文件要求用户使用用户定义的函数,并且至少定义一个没有参数的main函数,此外主程序文件中必须包含idc.idc头文件。
#idc程序文件基本结构
#Include <idc.idc>
static main(){
Message("this is a IDC scipt file");
}
IDC支持如下C预处理指令:
#include <文件> ;将指定的文件包含在当前文件中
#define <宏名称>[可选项] ;创建宏,可以选择给宏分配指定的值
#ifdef <名称>; 测试指定的宏是否存在
#else 与ifdef一起使用
#endif 通过ifdef指定定义终止符
#undef <名称> ;删除指定的宏
常用idc函数
ida中对于idc函数的命名都很有规律,以下列举了一些特定用途的函数,但是这些并不是全部dic中的函数,还有更多的可以前往ida帮助文档查找。
读取和修改数据的函数
用户交互函数
idc脚本没有任何调试工具,所以可以依靠输出函数实现调试。
字符串操纵函数
数据库名称操纵函数
处理函数的函数
代码交叉引用函数
数据交叉引用函数
数据库操纵函数
数据库搜索函数
反汇编行组件
IDC脚本示例
示例来自《解密家用路由器0day漏洞挖掘技术》
//枚举危险函数 scanvuln.idc
#include <idc.idc>
static flagCalls(fname) //定义了一个函数
{
auto count=0;
auto func,xref;
func =LocByName(fname);
if(func!=BADADDR)
{ for(xref=RfirstB(func);xref!=BADADDR;xref=RnextB(func,xref)) //使用代码交叉方式遍历危险函数
{
Message("%x,%x\n",xref,func);
if(XrefType()==fl_CN || XrefType()==fl_CF) //该判断语句判定,RfirstB或RnextB返回的交叉引用类型是近调用还是远调用
{
MakeComm(xref,"*** AUDIT HERE ***");
Message('Function%d:0x%x==>%s\n',++count,xref,fname);
}
}
/* //使用数据交叉引用的方式去搜索危险函数
for(xref=DfirstB(func);xref!=BADADDR;xref=DnextB(func,xref))
{
if(XrefType()==dr_O)
{
MakeComm(xref,"*** AUDIT HERE ***");
Message("Function%d:0x%x==> %s\n",++count,xref,fname);
}
}
*/
}
}
static main()
{
Message("---------------------\n");
flagCalls("strcpy");
flagCalls("sprintf");
Message("---------------------\n");
}