前言
静态分析是指:直接阅读二进制编码。静态分析的特点是:覆盖全面。但是,静态分析有它的缺点。如前面所说,二进制文件本身并不好理解,甚至像天书一样。因此,静态分析想得到精确的结果,则需要进行长时间的分析,使分析时间变长。
态分析不阅读二进制代码,而是直接把二进制文件扔到实际环境下执行,但是,由于设备的输入信息可以是任意的,因此动态分析不可能做到面面俱到。也就是说,分析结果虽然非常准确,但不能全面覆盖。
打CTF的Re题的时候,程序一般的流程就是接收输入,然后执行一个验证过程,如果程序通过认证,跳出一个flag,否则跳出一个error。而我们一般的思路是逆向这个认证过程,这就考察一个人的逆向思维了。
不妨换一个思维,不找中间的认证过程,而是直接寻找认证通过状态
符号执行
符号执行(Symbolic Execution)是一种程序分析技术。其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。
什么是Angr
angr是一个用于分析二进制文件的python框架。它专注于静态和符号分析,使其适用于各种任务。他寄生于2层环境中,第一层为angr环境,是一个利用virtualenvwrapper软件模拟出来的python环境,用于解决环境的不兼容问题,第二层是真实的python环境,也就是说我们需要执行python后才能使用import angr导入模块。
一般属性
首先,我们需要知道文件的基本属性,例如架构,文件名,入口点之类的。
装载器
从二进制文件加载到在虚拟地址空间中非常复杂!我们有一个叫CLE的模块来处理这个问题。称为加载程序,位于项目的.loader属性。
工厂 {The factory}
angr里面含有很多可用于实例化的类,其中factory类是二进制分析中最常用的类。
Project对象只代表程序的“初始化映像”。利用编程语言的角度来解释就是,Project只是一个对象(类),并没有实例化,是抽象的。但是,当执行angr的时候,使用的是一个代表模拟程序状态的特定对象,需要使用state()方法来实例化对象。一个SimState包含一个程序的内存,寄存器,文件系统数据…任何会在执行中改变的“实时数据”都会在这个state中。
模拟化管理器 {Simulation Managers}
simgr适用于程序模拟执行的接口,state只是表示程序执行过程中的一个状态,那么利用simgr可以使得程序执行到下一个状态。
安装angr
首先使用命令sudo apt-get install python-dev libffi-dev build-essential
安装python-dev,libffi-dev等工具。linux发行版通常会把类库的头文件和相关的pkg-config分拆成一个单独的xxx-dev,以python为例,如果你需要安装一个第三方的库,这个库里面含有调用C++(c)的API,或者程序需要连接静态库(.so)则需要安装python-dev。
接着使用pip install virtualenvwrapper
安装virtualenvwrapper。 python virtual enviroment是一个python环境管理工具,该工具能够在真实的系统中创建一个虚拟的python环境,以防止软件安装过程中对真实环境的影响,同时也能方便解决python中不同版本不兼容的问题。
使用pip的方法安装,则安装路径是/usrname[需要替换成你的]/local/bin/
,如果在后面还是找不到的话,使用find / -name virtualenvwrapper.sh
查找
接着设置环境变量export WORKON_HOME=$HOME/Python-workhome
,HOME/Python-workhome就是准备放置虚拟环境的地址,比如说选择/tmp
然后使用source /usrname[需要替换成你的]/local/bin/virtualenvwrapper.sh
执行脚本。
利用mkvirtualenv angr
命令建立angr的虚拟python环境,并启动。这一步前提是执行了上一步的脚本,否则报找不到命令的错误
最后使用sudo pip install angr
安装angr,如果报错,安装sudo pip install cffi
。
使用angr
一般性流程
一般常见的有以下命令:
新建一个angr工程
- proj = angr.Project(‘./CrakeMe’,auto_load_libs=false)
新建一个SimState对象
- state = p.factory.entry_state()
SimState对象在angr其中的一个子模块SimuVEX中,它追踪且记录着符号信息、符号对应的内存信息和符号对应的寄存器信息,以及打开的文件信息等。可以通过Project.factory这个容器中的任何一个方法来获取SimState对象。这个factory有多个构造函数,如:block、entry_state等。这里使用entry_state返回一个初始化到二进制entry point的SimState对象。
创建一个simgr对象,创建一个模拟器用来模拟程序执行
- simgr = proj.factory.simgr(state)
利用IDA查找两个分支,然后去探索,用explore执行模拟器,find和avoid用来作为约束条件
- simgr.explore(find=0x300602,avoid=0x40060E)
打印爆破出来的结果
- print simgr.found[0].solver.eval(argv1)123456789import angrimport claripy # 接收命令行参数proj = angr.Project('./ais3_crackme',auto_load_libs=False) # 创建项目argv1 = claripy.BVS('argv1',50*8) # 设置输入state = proj.factory.entry_state(args=['./ais3_crackme',argv1]) # 设置入口点simgr = proj.factory.simgr(state) # 初始化运行器simgr.explore(find=0x300602,avoid=0x40060E) # 探索print simgr.found[0].solver.eval(argv1) # 打印结果print simgr.found[0].solver.eval(agrv1,cast_to=str) # 转化为asscii
- print simgr.found[0].solver.eval(argv1)
获取输入
- 无输入
- 命令行输入
当程序要求命令行参数时,我们首先需要使用claripy这个模块来定义抽象的数据。
laripy的BVS函数可以创建一个指定长度的抽象数据,BVS函数要求两个参数,第一个参数为变量名,第二个参数为变量长度。
这样,我们就创建好了一个命令行参数,我们现在可以将程序运行到程序入口处,并获得当前的一个状态。
- 标准输入
当程序需要从标准输入处读取数据时,需要使用read_from()函数,要注意,这个函数位于状态中,并且我们可以对输入进行一些约束以减少符号执行遍历的路径
获取输出
获取程序的当前输出
1print simgr.found[0].posix.dumps(0)命令行参数
1print simgr.found[0].solver.eval(argv1,cast_to=str)标注输出
12res = simgr.founf[0].posix.files[0].all_bytes()print simgr.found[0].solver.eval(res,cast_to=str)
CTF例子
第一个Demo
利用如下代码:编译成一个64位的elf文件,在编译的过程中使用如下编译选项以关闭PIE选项: gcc -no-pie Test1.cpp -o test1
,参考自:github.com/firmianay/CTF-All-In-One/blob/master/doc/4.4_gcc_sec.md
利用checksec检查文件属性,checksec利用sudo pip install pwntools
安装的
在IDA中查看反汇编代码如下:存在两个分支。
使用如下angr脚本
结果如下:
第二个Demo
题目来源:https://github.com/angr/angr-doc/tree/master/examples/ais3_crackme
执行一下发现命令行选项作为输入,所有在使用angr的时候需要导入import charipy
利用IDA看一下,发现如下分支,确定约束条件
编写angr脚本
结果为:
第三个Demo
这是一个标准输入的例子,但是我的ubuntu运行不起来。