IDA 脚本入门
总所皆知,IDA是一个非常好用的逆向静态分析工具,上节课,老师带我们体验到了ida脚本的魅力,我们这次就来了解一波IDA的脚本
Reference:
IDA Pro 脚本系统(idc idaPython)帮助文档索引
idc
IDA Help: Alphabetical list of IDC functions
IDA Python
IDAPython初学者指南:本地版本[here](./IDAPython 初学者指南.pdf)
IDAPython Docs:hex-rays官方文档
我解到了IDA脚本主要有IDA python 和 IDC
IDC脚本语言是一种类似C的脚本语言,和C语言有着类似的语言标记。然而,由于IDC是一种脚本语言,就无法包含指针这样的高级特性了,但是所有的变量类型,脚本解释器都是可以支持的。IDC中的变量是弱类型的,一个变量可以保存任何类型的数据,因此变量声明的时候是不需要指定变量类型的,比如你可以这样进行变量声明: auto myvar;
IDA python 为IDA中集成的python解释器(美中不足的就是目前是python2.7版本),能够调用所有的idc的函数以及所有python脚本,他可以充分利用python的数据处理能力和所有python模块
脚本的用处很多,以现在的我的了解来说,ida脚本最多的用处是用来脱壳和寻找pwn的易被攻击的函数
使用
shift + F2
会弹出一个对话框,可以将脚本直接写在里面,点run运行,即File -> Script Command
- 菜单栏中
File -> Script file
载入idc文件,同样,alt + F7
输出在下面的Output Window里
IDC
IDC脚本语言借用了很多C语言的语法,因此我们很快就能上手
auto
: IDC关键字auto用于引入一个局部变量,如auto var,addr;
- IDC认可使用
/* */
的c语言风格进行多行注释。 - 也使用
//
的C++风格进行行尾注释 - 使用
;
作为终止符(和c语言中一样) - IDC并不支持c风格数组类型,指针,结构体,联合之类,复杂的数据类型
extern
:IDA使用extern
关键字引入全局变量申明
IDC支持c中的所有表达式和逻辑运算符,包括三元运算符(?:)。
支持逗号运算符,但是 不支持 op=(+=,*=,>>=等)复合赋值运算符。
不支持算术移位,如果要移位只能修改最高位和最低位。如下所示:
提供分片运算,使用方括号和其实索引(包括)与结束索引(不包括)来指定至少一个索引。这个就像python数组一样,比如s1 = str[7:9]
、s2 = str[:6]
、s3 = str[10:]
语句以分号结尾,但是不支持复合语句。
引入try/catch
块和相关的throw语句,相当与C++的异常处理。
使用和C语言一样的花括号和语义。在花括号中可以申明变量,申明变量必须位于花括号内的第一条语句。
Message类似于c语言中的print
- .idc文件支持用户定义的函数。
- IDC命令对话框不支持用户定义的函数。
- 使用static声明用户定义的函数。
- 函数的参数列表以逗号隔开。
- 当执行大量的IDC语句的时候,需要创建一个独立的IDC程序文件。
- IDC程序文件,至少定义一个没有参数的main函数。
- IDC文件必须包含**#include<idc.idc> **这个文件,由此获得宏定义。
拿老师给的示例程序来说
|
|
首先在头部引入#include <idc.idc>
,这个是必须的,包含了idc里面的宏定义
FirstSeg()
获取第一个Segment段的地址,然后调用list_ref_function
展示其中的函数,将此过程进行循环每一个segment
list_ref_function
:通过GetFunctionFlags
来判断是否是一个地址是否有函数开始标志,来获取函数的地址和名字,并用Message函数打印出来
IDA Python
IDA Python 由三个分离的模块组成,他们分别是 idc,idautils 和 idaapi。
idc:这是兼容idc函数的模块
idautils:很使用的一个模块,大多数处理都是需要依托于这个模块
idaapi:允许使用者通过类的形式,访问更多底层的数据
IDA Python 的强大之处在于它能遍历所有的指令,所有的交叉引用地址,还有搜索所有的代码和数据。
一些功能
指令处理
- 获取当前指令地址:ea=here() print “0x%x %s”%(ea,ea)
- 获取当前的汇编指令:idc.GetDisasm(ea)
- 获取当前处于的段:idc.SegName()
- 获取该段程序最低和最高地址:hex(MinEA()) hex(MaxEA())
- 获取下(上)一条汇编指令地址:ea = here() ; next_instr = idc.NextHead(ea) PrevHead(ea)
函数操作
-
获取程序所有函数的名称:
for func in idautils.Functions(): print hex(func), idc.GetFunctionName(func)
-
计算当前函数有多少条指令
ea = here() len(list(idautils.FuncItems(ea)))
-
获取当前IDB中记录的所有有名字函数的地址和名称: idautils.Names() 返回的是一个以元组组成的列表,函数的起始地址指向了其plt表
指令操作
- 给定地址,打印指令 idc.GetDisasm(ea)
- 给定函数中一个地址,得到整个函数的指令列表 idautils.FuncItems(here())
- 获取函数的一些flag信息: idc.GetFunctionFlags(func)
- 对一条汇编指令进行拆解: 获取指令的操作:idc.GetMnem(here()) 获取指令的操作数:idc.GetOpType(ea,n) 根据返回的数值,可以判断操作数类型(普通寄存器、常量字符串等)
- 对汇编指令中用到的操作数,求取其引用的地址,也就是双击该操作数后跳转到的地址 hex(idc.GetOperandValue(here(),1))
交叉引用
- 指令从哪个地方来:idautils.CodeRefsTo(here(),0)
- 指令下一步去到哪儿:idautils.CodeRefsFrom(here(),0)
- 数据从哪个地方来:idautils.DataRefsTo(here(),0)
- 数据下一步去到哪儿:idautils.DataRefsFrom(here(),0)
- 较为通用的一种获取xref:idautils.XrefsTo(here(),flag) 其中flag=0时,所有的交叉引用都会被显示