11IDAPYTHON---IDA脚本IDAPro(前身为IlfakGuilfanov)以其强大的静态分析功能当之无愧的成为逆向工程的首选。让我们记住它的缔造者,Hex-RaysSA(布鲁塞尔)。IDA如今已经能够在大多数平台上运行,能够分析大部分平台的二进制文件,同时提供了一个内置的调试器。IDA的扩展能力也是极其强大的,提供了IDC(IDA的脚本语言)和SDK(让开发者扩展方便IDA插件)。2004年Gergely和EroCarrera开发了IDAPython插件,将强大的Python和IDA结合起来,使得自动化分析变得异常简单。而如今IDAPython被广泛的使用于各种商业产品(Zynamics的BinNavi)和开源工程(PaiMei和PyEm)中。这一章,我们要学会IDAPython(以IDAPro5.2为目标)的安装以及重要的函数的使用,最后通过几个简单的例子进一步熟悉IDA自动化分析。11.1安装IDAPython从下载我们需要的压缩包。这个版本比较早,建议大家安装idapython-1.2.0_ida5.4_py2.5_win32.zip的版本,这个版本也可以用于ida5.5。下载完后解压缩,将主目录下的python文件夹,复制到IDA的安装目录下(默认为C:\ProgramFiles\IDA),将plugins目录下python.plw复制到IDA的plugins目录下(默认为C:\ProgramFiles\IDA\plugins.)。就当的驱动IDA,随意加载一个可执行文件,一旦初始化分析完成,就会看到底部的输出窗口中包含了IDAPython的信息,记得不加载文件的时候是不会出现的。如图11-1。Figure11-1:IDAPython成功安装之后的IDAPro的初始化信息在文件菜单中将会看到多出两个选项,如图11-2Figure11-2:IDAPython成功安装后的DAPro文件菜单连个新的选项分别是Pythonfile和Pythoncommand,热键也设置好了。如果能够执行一个简单的Python命令,只要单击Pythoncommand选项,就会出现一个窗口,输入命令后,就会IDA的输出窗口中看到结果。Pythonfile选项用于执行独立的IDAPython脚本,这也是这章要重点介绍的。先IDAPython已经成功安装,并且正常工作,接下来让我们了解下常用的DAPython函数。11.2IDAPython函数IDAPython能够访问所有的IDC函数,我们只介绍一些会马上用到,为之后的IDAPython脚本编写做基础。IDC总共有100多个函数,有兴趣的可以研究研究。11.2.1常用函数以下的函数都是在编写脚本的时候经常用到的。ScreenEA()获取IDA调试窗口中,光标指向代码的地址。通过这个函数,我们就能够从一个已知的点运行我们的脚本。GetInputFileMD5()返回IDA加载的二进制文件的MD5值,通过这个值能够判断一个文件的不同版本是否有改变。11.2.2段在IDA中二进制文件被分成了不同的段,这些段根据功能分成了不同的类型(CODE,DATA,BSS,STACK,CONST,XTRN)。以下的函数用于分析获得各种段信息。FirstSeg()访问程序中的第一个段。NextSeg()访问下一个段,如果没有就返回BADADDR。SegByName(stringSegmentName)通过段名字返回段基址,举个例子,如果调用.text作为参数,就会返回程序中代码段的开始位置。SegEnd(longAddress)通过段内的某个地址,获得段尾的地址。SegStart(longAddress)通过段内的某个地址,获得段头的地址。SegName(longAddress)通过段内的某个地址,获得段名。Segments()返回目标程序中的所有段的开始地址。11.2.3函数循环访问程序中的所有函数,确定函数的范围,是脚本编程中会经常碰到的问题。下面的函数对于处理函数非常有用。Functions(longStartAddress,longEndAddress)返回一个列表,包含了从StartAddress到EndAddress之间的所有函数。Chunks(longFunctionAddress)返回一个列表,包含了函数片段。每个列表项都是一个元组(chunkstart,chunkend)LocByName(stringFunctionName)通过函数名返回函数的地址。GetFuncOffset(longAddress)通过任意一个地址,然后得到这个地址所属的函数名,以及给定地址和函数的相对位移。然后把这些信息组成字符串以名字+位移的形式返回。GetFunctionName(longAddress)通过一个地址,返回这个地址所属的函数。11.2.4交叉引用找出代码和数据的交叉引用,在分析文件的执行流程时很重要,尤其是当我们分析感兴趣的代码块的时候,盲目的查找无意义字符会让你有一种想死的冲动,这也是为什么IDA依然会成为逆向工程的王者的原因。IDAPython提供了一大堆函数用于各种交叉引用。最常用的就是下面几种。CodeRefsTo(longAddress,boolFlow)返回一个列表,告诉我们Address处代码被什么地方引用了,Flow告诉IDAPython是否要跟踪这些代码。CodeRefsFrom(longAddress,boolFlow)返回一个列表,告诉我们Address地址上的代码引用何处的代码。DataRefsTo(longAddress)返回一个列表,告诉我们Address处数据被什么地方引用了。常用于跟踪全局变量。DataRefsFrom(longAddress)返回一个列表,告诉我们Address地址上的代码引用何处的数据。11.2.5DebuggerHooksDebuggerHook是IDAPython提供的另一个非常酷的功能,用于Hook住IDA内部的调试器,同时处理各种调试事件。虽然IDA一般不用于调试任务,但是当需要动态调试的时候,调用IDA内部调试器还是比外部的会方便很多。之后我们会用debuggerhooks创建一个代码覆盖率统计工具。使用debuggerhook之前,先要睇你一个一个hook类然后在类里头定义各种不同的处理函数。classDbgHook(DBG_Hooks):#Eventhandlerforwhentheprocessstartsdefdbg_process_start(self,pid,tid,ea,name,base,size)return#Eventhandlerforprocessexitdefdbg_process_exit(self,pid,tid,ea,code):return#Eventhandlerforwhenasharedlibrarygetsloadeddefdbg_library_load(self,pid,tid,ea,name,base,size):return#Breakpointhandlerdefdbg_bpt(self,tid,ea):return这个类包含了我们在创建调试脚本时,会经常用到的几个调试事件处理函数。安装hook的方式如下:debugger=DbgHook()debugger.hook()现在运行调试器,hook会捕捉所有的调试事件,这样就能非常精确的控制IDA调试器。下面的函数在调试的时候非常有用:AddBpt(longAddress)在指定的地点设置软件断点。GetBptQty()返回当前设置的断点数量。GetRegValue(stringRegister)通过寄存器名获得寄存器值。SetRegValue(longValue,stringRegister)设定寄存器的值。11.3脚本例子我们先创建一些在逆向时候会经常用到的脚本。之后,大家可以在此基础上扩展它们,进一步完成功能更强大,针对性更强的脚步。接下来的脚本将展示如何收集危险函数的调用信息,以及用IDA的debuggerhook监视函数的代码覆盖率,还有所有函数的栈的大小。11.3.1收集危险函数的调用信息当一个开发者在寻找软件漏洞bug的时候,首先会找一些常用的而且容易被错误使用的函数。比如危险的字符串拷贝函数(strcpy,sprintf),内存拷贝函数(memcpy)等。在我们审核程序的时候,需要很简单的就找出这些函数。下面的脚本,将跟踪这些危险的函数,找出调用它们的地方,之后在这些地方的背景色设置成不同的颜色,我们在IDA窗口中就能很方便的看出来。#cross_ref.pyfromidaapiimport*danger_funcs=[strcpy,sprintf,strncpy]forfuncindanger_funcs:addr=LocByName(func)ifaddr!=BADADDR:#Grabthecross-referencestothisaddresscross_refs=CodeRefsTo(addr,0)printCrossReferencesto%s%funcprint-------------------------------forrefincross_refs:print%08x%ref#ColorthecallREDSetColor(ref,CIC_ITEM,0x0000ff)我们先获得危险函数的地址,然后测试这些地址的有效性。接着获得这些函数的交叉引用信息,确认什么地方调用了它们,最后把它们打印出来,并在IDA中给它们上色。用之前编译好的war-ftpd.exe做测试目标,将看到如下的输出:CrossReferencestosprintf-------------------------------004043df00404408004044f9004048100040485100404896004052cc0040560d0040565e004057bd004058d7...Listing11-1:cross_ref.py的输出上面这些被列出来的地址都是sprintf被调用的地方,如果在IDA中浏览这些地方会看到它们都被上了色,如图11-3。Figure11-3:sprintf调用通过cross_ref.py上色之后11.3.2函数覆盖率在执行动态分析的时候,明白我们真正进行的操作是由什么代码执行的,非常重要。无论是测试网络程序发送一个数据包,还是使用文档阅读器代开一份文档,代码覆盖率都能帮我们很好的了解,程序做了什么。下面,我们将用IDAPython获取目标程序的所有函数,并且在再每个函数的开始处都设置好断点。之后运行IDA调试器,debuggerhook会把每一次断点触发的情况通知我们。#func_coverage.pyfromidaapiimport*classFuncCoverage(DBG_Hooks):#Ourbreakpointhandlerdefdbg_bpt(self,tid,ea):print[*]Hit:0x%08x%eareturn#Addourfunctioncoveragedebuggerhookdebugger=FuncCoverage()debugger.hook()current_addr=ScreenEA()#FindallfunctionsandaddbreakpointsforfunctioninFunctions(SegStart(current_addr),SegEnd(current_addr)):AddBpt(function)SetBptAttr(function,BPTATTR_FLAGS,0x0)num_breakpoints=GetBptQty()print[*]Set%dbreakpoints.%num_breakp