• Home
  • About
    • tearorca photo

      tearorca

      Wishful thinking have to be willing to bet on clothing!

    • Learn More
    • Google+
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

IDA脚本语言

13 Jul 2019

Reading time ~2 minutes

  • IDA脚本语言
    • IDC语言
      • 介绍
        • IDC变量
        • IDC表达式
        • IDC自定义函数
        • IDC程序
      • 常用IDC函数
        • 读取和修改数据的函数
        • 用户交互函数
        • 字符串操纵函数
        • 数据库名称操纵函数
        • 处理函数的函数
        • 代码交叉引用函数
        • 数据交叉引用函数
        • 数据库操纵函数
        • 数据库搜索函数
        • 反汇编行组件
      • IDC脚本实例
    • IDAPython
      • 模块
      • 脚本实践
    • 参考链接

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漏洞挖掘技术》



Share Tweet +1