IDA脚本语言
IDC语言
介绍
IDC是一种类c语言,但不支持c语言风格的数组、指针、结构体、联合等复杂数据结构
IDC变量
IDC是一种类型松散的语言,它的变量没有明确的类型。IDC主要使用3种数据类型,分别是整型(long)、字符串型和浮点值。虽然变量没有明确的类型,但不能像python一样直接使用,而必须先声明。
auto a; //局部变量a
extern b;//全局变量b
注:在IDA会话过程中首次遇到全局变量时,IDA会对全局变量进行空间分配,只要该会话处于活动状态,那么无论打开或关闭多少数据库,这些变量始终有效。
IDC表达式
IDC几乎支持C语言中所有的算术和逻辑运算符,包括三元运算符。
区别:
1、不支持符合赋值符号如+=、-=。
2、在IDC中所有的整数操作都作为有符号的值处理。
3、在IDC中字符串的连接可以直接用“+”进行操作。还引进了类型python的字符串切片操作。
auto str0 = “this_is”;
auto str1 = str0+ “_test”; //str1=”this_is_test”
auto s0,s1,s2,s3,s4;
s0 = str1[5:7]; //s0=”is”
s1 = str1[:4]; //s1=”this”
s2 = str1[8:]; //s2=”test”
s3 = str1[4]; //s3=”_”
s4 = str1[:-5]; //s4=”this_is”
IDC自定义函数
1、在IDC中可以用static关键字引入用户自定义函数。
2、虽然IDC没有指针这个概念,但在IDA 5.6之后引入了传地址参数传递机制。和c语言一样函数可以用“&”取地址符号来传入到函数里面直接更改地址的值。
3、由于IDC变量的弱类型特点,使得函数声明不会指明该函数是否明确返回一个值,以及在不生成结果时会返回什么类型的值。如果希望函数返回一个值,可以使用return语句返回值。如果不使用,默认情况下任何不显示返回一个值的函数都将返回零值。
IDC程序
如果一个脚本应用程序需要执行大量的IDC语句,那么我们可能需要创建一个独立的IDC程序文件。IDC程序文件要求使用用户定义函数,且至少应该定义一个没有参数的main()函数。另外,主程序文件中必须包含idc.idc头文件。
IDC支持以下C预处理指令。
1、#include<文件>:把指定的文件包含在当前文件中。
2、#define<宏名称>[可选项]:创建宏,可以选择给宏分配指定的值。
3、#ifdef<名称>:测试指定的宏是否存在。若存在,则跳过该语句定义的块。
4、#else:与“#ifdef”指令一起使用。
5、#endif:通过“#ifdef”指令定义终止符。
6、#undef<名称>:删除指定的宏。
常用IDC函数
读取和修改数据的函数
long Byte(long addr):从虚拟地址addr处读取一个字节的值。
long Word(long addr):从虚拟地址addr处读取一个字的值。
long Dword(long addr):从虚拟地址addr处读取一个双字的值。
void PatchByte(long addr,long val):设置虚拟地址addr处一个字节的值为val。
void PatchWord (long addr,long val):设置虚拟地址addr处一个字的值为val。
void PatchDword (long addr,long val):设置虚拟地址addr处一个双字的值为val。
bool isLoaded(long addr):如果虚拟地址addr中包含有效数据则返回1,否则返回0。
用户交互函数
void Message(string format, …):在输出窗口打印格式化消息,方法与c语言中的pringtf函数类似。
void print(…):在输出窗口打印每一个参数的字符串表示形式。
void Warning(string format, …):弹出对话框,显示格式化消息。
string AskStr(string default,string prompt):显示一个输入框,要求用户输入字符串。如果对话框被用户取消则返回0,否则返回输入字符串。
string AskFile(long doSave,string mask,string prompt):显示一个文件选择对话框,简化选择文件的任务。如果需要保持文件,设置“doSave=1”rugosa选择加载现有文件,设置“doSave=0”。“mask”用于过滤显示的文件列表。如果对话框被用户取消则返回0,否则返回选定文件的名称。
long AskYN(long default,string prompt):用一个答案为“是”或“否”的问题提示用户,突出一个默认的答案(1为“是”,0为“否”,-1为“取消”)。返回值是一个表示选定的答案的整数。
long ScreenEA():返回当前光标所在位置的虚拟地址。
bool Jump(long addr):跳转到反汇编窗口的指定地址。
字符串操纵函数
string sprintf(string format,..):返回一个新的字符串,该字符串根据所提供的格式化字符串和值进行格式化,与c语言中的sprintf函数使用方法类似。
string form(string format, …):用法同sprintf函数
long atol(string val):将十进制值val转换为对应的整数类型的值。
long xtol(string val):将十六进制值val转换为对应的整数类型的值。
string ltoa(long val,long radix):以指定的进制(radix)返回val的字符串的值。
long ord(string ch):返回单字符ch的ASCII值。
long strlen(string str):返回字符串str的长度
long strstr(string str,string substr):返回字符串str中子串substr的索引值。如果没有找到子串,返回- 1。
string substr(string str,long start,long end):返回在字符串str中从start索引位置开始到end-1索引结束位置的子字符串,与字符串分片操作的str[start:end]结果一样。
数据库名称操纵函数
string Name(long addr):返回给定地址在IDA数据库中的相关名称。如果该位置没有名称,则返回空字符串。如果名称被标记为局部名称,则不返回用户定义的名称。
string NameEx(long from,long addr):返回与addr有关的名称。如果该位置没有名称,则返回空字符串。
如果from是一个同样包含addr的函数中的地址,则返回用户定义的局部名称。
bool MakeNameEx(long addr,string name,long flags):为指定地址addr分配名称name。该名称使用 flags位掩码中指定的属性创建。
long LocByName(string name):返回指定名称name位置的地址。如果在IDA数据库中没有这个名称,则返回BADADDR(-1)。
long LocByNameEx(long funcaddr,string localname):在包含funcaddr的函数中指定局部名称localname。如果指定函数中没有该名称,则返回BADADDR(-1)。
处理函数的函数
long GetFunctionAttr(long addr,long attrib):获取指定地址的函数的请求属性。
string GetFunctionName(long addr):获取指定地址addr位置的函数的名称。如果指定位置不属于任何一个函数,则返回空字符串。
long PrevFunction(long addr):返回指定地址addr之后的距离最近的函数的起始地址。如果数据库中指定地址之前没有其他函数,则返回-1.
代码交叉引用函数
long Rfirst(long from):指定地址向from转交控制权的第一个位置。如果指定地址没有引用其他地址,则返回BADADDR(-1)。
long Rnext(long from,long current):如果current已经在前一次调用Rfirst()或Rnext()函数时返回,则返回指定地址from转交控制权的下一个位置。如果没有其他交叉引用存在,则返回BADADDR(-1)。
long XrefType():返回某交叉引用查询函数(如Rfirst())返回的最后一个交叉引用的类型,值为一个常量。代码交叉引用返回的常量包括fl_CN(近调用)、fl_JN(近跳转)、fl_CF(远调用)、fl_JF(远跳转)及fl_F(普通顺序流)。
long RfirstB(long to):返回转交控制权到指定地址to的第一个位置。如果不存在对给定地址的交叉引用,则返回BADADDR(-1)。
long RnextB(long to,long current):如果current已经在前一次调用RfirstB()或RnextB()函数时返回,则返回指定地址from转交控制权的下一个位置。如果没有其他交叉引用存在,则返回BADADDR(-1)。
数据交叉引用函数
long Dfirst(long from):返回指定地址from引用一个数据值的第一个位置。如果指定地址没有引用其他地址,则返回BADADDR(-1)。
long Dnext(long from,long current):如果current已经在前一次调用Dfirst()或Dnext()函数时返回,则返回指定地址from向其引用一个数据值的下一个位置。如果没有其他交叉引用存在,则返回BADADDR(-1)。
long XrefType():返回某交叉引用查询函数(如Dfirst())返回的最后一个交叉引用的类型,值为一个常量。数据交叉引用返回的常量包括dr_O(提供的偏移量)、dw_W(数据写入)及dr_R(数据读取)。
long DfirstB(long to):返回指定地址to作为数据引用的第一个位置。如果不存在对给定地址的交叉引用,则返回BADADDR(-1)。
long DnextB(long to,long current):如果current已经在前一次调用DfirstB()或DnextB()函数时返回,则返回将指定地址to作为数据引用的下一个位置。如果没有其他交叉引用存在,则返回BADADDR(-1)。
数据库操纵函数
bool MakeComm(long addr,string comment):在指定地址addr处添加一条常规注释。
void MakeUnkn(long addr,long flags):取消在地址addr处的项的定义。”flags”用于指出是否取消随后的项的定义,以及是否删除任何与取消定义的项有关的名称 。
long MakeCode(long addr):将位于指定地址addr处的字节转换为一条指令。如果操作成功则返回指令长度,否则返回0。
bool MakeByte(long addr):将位于指定地址addr处的字节转换为一条数据字节。类似函数还有MakeWord()和MakeDword()。
bool MakeFunction(long begin,long end):将从begin到end位置的指令转换为一个函数。如果end位置被指定为BADADDR(-1),IDA会尝试通过定位函数的返回指令自动确定该函数的结束地址。
bool MakeStr(long begin,long end):将从begin到end-1位置的所有字节转换为一个字符串类型的字符串。如果end位置被指定为BADADDR,IDA会自动确定字符串的结束位置。
数据库搜索函数
long FindCode(long addr,long flags):从指定地址addr处搜索一条指令。
long FindDate(long addr,long flags):从指定地址addr处搜索一个数据项。
long FindBinary(long addr,long flags,string binary):从指定地址addr处搜索一个字节序列。字符串binary指定一个十六进制的字节序列值。
long FindText(long addr,long flags,long row,long column):在指定地址addr处,从给定行row的给定序列column中搜索字符串text。需要注意的是,某个给定地址的反汇编文本可能会跨越多行,因此我们需要指定搜索应从哪一行开始。
反汇编行组件
string GetDisasm(long addr):返回指定地址addr的反汇编文本。返回的文本中包含注释,但不包含地址 信息。
string GetMnem(long addr):返回指定地址addr的指令的助记符部分。
string GetOpnd(long addr,long opnum):返回指定地址addr的指定操作数的文本形式。
long GetOpType(long addr,long opnum):返回指定地址addr的给定操作数的类型。
long GetOperandValue(long addr,long opnum):返回指定地址addr的给定操作数有关的整数值,其返 回值的性质取决于GetOpType指定的给定操作数类型。
string GetOperandValue(long addr,long opnum):返回指定地址addr的注释文本。“type=0”时返回常规注释文本;“type=1”时,返回可重复注释文本。如果给定地址没有注释,则返回空字符串。
IDC脚本实例
测试代码:
#include <stdio.h>
#include <string.h>
int vul(char *src)
{
char output[20] = {0};
strcpy(output, src);
printf("%sn", output);
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("need more argumentsn");
return 1;
}
vul(argv[1]);
return 0;
}
用IDC编写一个脚本进行自动化分析危险函数,逆向遍历危险函数的所有交叉引用并使用注释对漏洞函数进行标记。
#include <idc.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))
{
if(XrefType()==fl_CN||XrefType()==fl_CF)
{
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");
}
打开ida加载测试样例,然后点开File->Script file
](http://ww1.sinaimg.cn/large/7fb67c86ly1g50vkaxzwqj20qy0hw40l.jpg)k)
可以得到这样的结果
不过这里有一点和书上的不一样,书上的显示的是直接在函数里调用的strcpy,而不是像我这里是显示strcpy函数的位置,不过也没有关系,接着看交叉调用也可以找到被调用的函数。点到函数名右键选Xrefs graph to就可以看到完整的函数调用了
IDAPython
IDAPython在IDA中集成了Python解释器。除了提供Python功能外,使用这个插件还可以编写能够实现IDC脚本语言的所有Python脚本。IDAPython的一个显著优势在于,它可以充分利用Python强大的数据处理能力及所有的Python模块。此外,IDAPython还具有IDA SDK的大部分功能,与IDC相比,使用它可以编写出功能更加强大的脚本。但IDAPython几乎找不到使用文档,只有网上一些人发的博客,这会对学习产生一定的阻碍。
提供一个博客供学习:https://blog.csdn.net/oShuangYue12/article/details/85675751
模块
idaapi.py:负责访问核心IDA API。
Idautils.py:提供大量的使用函数。
Idc.py:负责提供IDC中所有函数的功能。
脚本实践
和前面的测试代码用同一个,这个代码的功能也是寻找交叉引用的危险函数。
from idaapi import *
def getFuncAddr(fname):
return LocByName(fname)
def judgeAduit(addr):
MakeComm(addr,"### AUDIT HERE ###")
SetColor(addr,CIC_ITEM,0X0000ff)
def flagCalls(funcname):
count=0
fAddr=getFuncAddr(funcname)
func=get_func(fAddr)
if not func is None:
fname=Name(func.startEA)
items=FuncItems(func.startEA)
for i in items:
for xref in XrefsTo(i,0):
if xref.type==fl_CN or xref.type==fl_CF:
count+=1
Message("%s[%d] calls 0x%08x from => %08x\n"%(fname,count,xref,frm,i))
else:
Warning("No")
if __name__=='__main__':
flagCalls('strcpy')
操作和上面的一样。
参考链接
《揭秘家用路由器0day漏洞挖掘技术》