IDA Pro(简称IDA)是DataRescue公司(www.datarescue.com)出品的一款交互式反汇编工具,它功能强大、操作复杂,要完全掌握它,需要很多知识。IDA最主要的特性是交互和多处理器。操作者可以通过对IDA的交互来指导IDA更好地反汇编,IDA并不自动解决程序中的问题,但它会按用户的指令找到可疑之处,用户的工作是通知IDA怎样去做。比如人工指定编译器类型,对变量名、结构定义、数组等定义等。这样的交互能力在反汇编大型软件时显得尤为重要。
IDAPython创建于2004年,这是GergelyErdelyi和Ero Carrera的共同努力。他们的目标是结合强大的python与自动化分析的IDA的类C脚本语言IDC。IDAPython由三个独立模块组成,第一个是idc,它是封装IDA的IDC函数的兼容性模块;第二个模块是idautils,这是IDA里的一个高级实用功能模块;第三个模块是idaapi,它允许访问更加底层的数据。
通过该实验了解IDAPython简单语法,能够编写简单实例。
win7,IP地址:随机分配
辅助工具:IDAPro
测试代码请在实验机内下载使用:http://tools.hetianlab.com/tools/idapython/3.zip
给出的文件名为rabbithole,在kali上用file命令查看可以看到是64位的可执行文件:
我们直接在windows上使用IDApro载入,以此文件为样例,学习IDAPython的用法。
操作数在逆向分析中经常被使用,所以了解所有的操作数对逆向分析非常有帮助。我们可以使用idc.GetOpTye(ea,n)来获取操作数类型,ea是一个地址,n是一个索引,从0开始。操作数一共有八种不同的类型。
O_void:如果指令没有任何操作数,则返回0。
O_reg:如果操作数是寄存器,则返回这种类型,值为1。
O_mem:如果操作数是直接寻址的内存,那么返回该类型,值为2,这种类型对寻找DATA非常有帮助。
O_phrase:如果操作数是利用基址寄存器和变址寄存器的寻址操作的话,返回该类型,值为3。
O_displ:如果操作数利用寄存器和位移的寻址操作的话,返回该类型,值为4,位移指的是类似下图中的0x18,这在获取结构体中的某个数据是非常常见的。
O_imm:如果操作数是一个确定的数值,那么返回该类型,值为5。
O_far:这种返回类型在x86,x86_64的逆向中不常见,它用来判断直接访问远端地址的操作数,值为6。
O_near:这种返回类型在x86,x86_64的逆向中不常见,它用来判断直接访问近端地址的操作数,值为7。
我们可以在IDA中找到对应的类型进行测试。
我这里就演示一个,我们把光标定位到pop rbx:
然后再输出窗口测试:
可以看到返回是1,对应这条指令我们知道其操作数为寄存器,所以确实应该返回1。
接下来我们结合以上所学,来看几个小demo。
在IDA中我们会看到汇编代码的某些指令不会被识别为偏移量,如下所示:
如果我们右键选中后将其更改数据类型,就会看到字符串的偏移量。
比如选中20h,右键→offset qword_20。
结果如下所示:
这样子做一两次也无所谓,不过既然学习了IDAPython我们可以通过脚本将其以offset偏移的形式展现。
import idautils import idaapi min = MinEA() max = MaxEA() for func in idautils.Functions(): flags = idc.GetFunctionFlags(func) if flags & FUNC_LIB or flags & FUNC_THUNK: continue dism_addr = list(idautils.FuncItems(func)) for curr_addr in dism_addr: if idc.GetOpType(curr_addr,0) == 5 and (min < idc.GetOperandValue(curr_addr,0)< max): idc.OpOff(curr_addr,0,0) if idc.GetOpType(curr_addr,1) == 5 and (min < idc.GetOperandValue(curr_addr,1)< max): idc.OpOff(curr_addr,1,0)
运行后如图所示:
代码解释:
一开始通过MinEA()和MaxEA()获取可执行文件的最大地址和最小地址,我们遍历了所有的函数和指令。对于每条指令检查他的操作数类型是否为o_imm(5),o_imm类型的操作数就是一个确定的数值或者偏移,一旦发现是这种类型的操作数,我们就用idc.GetOperandValue(ea,n)来获取它的值,然后检测一下是否在可执行文件的最大地址和最小地址之间。最后利用idc.OpOff(ea,n,base)来讲操作数转换为一个偏移,该函数的第一个参数为地址,第二个参数为操作数的索引,第三个参数为基地址,在代码中我们将其设为0就可以了。
在IDA中,交叉引用是一项非常重要的功能,交叉引用的重要性在于它能够提供某个确定的数据或者某个函数被调用的位置。比如我们要找check_value函数在哪儿被调用,我们手工的话就是定位到check_value,右键→jump to xref to operand可以看到交叉引用。
既然我们现在学了IDAPython,我们就可以使用脚本来完成这项工作。
代码如下:
import idautils checkvalue = idc.LocByName("check_value") for addr in idautils.CodeRefsTo(checkvalue,0): print hex(addr),idc.GetDisasm(addr)
代码解释:首先使用idc.LocByName(str)来获取check_value的地址,str为函数名称,返回地址为该api的地址。使用idautils.CoreRefsFrom(ea,flow)函数来返回所有调用了check_value的地址,该返回的结果可以通过循环进行迭代,ea是用于查找相应交叉引用的地址,flow是一个布尔值,用于标记是否监视一个正常的代码流。对应于idautils.CoreRefsFrom(ea,flow),还有idautils.CoreRefTo(ea,flow),它可以获取一个确切地址引用了哪些地址的数据。用法都是一样的,不再演示。