Reverse中Angr的利用
记录一下入门Reverse中符号执行的利用
Angr
简介
Angr是一个利用python开发的二进制程序分析框架,我们可以利用这个工具尝试对一些CTF题目进行符号执行来找到正确的解答,即flag。当然,要注意的是符号执行的路径选择问题到现在依旧是一个很大的问题,换句话说也就是当我们的程序存在循环时,因为符号执行会尽量遍历所有的路径,所以每次循环之后会形成至少两个分支,当循环的次数足够多时,就会造成路径爆炸,整个机器的内存会被耗尽。
安装
ubuntu安装教程如下
1 | 安装依赖 |
符号执行
基础实例
1 | import angr |
加载依赖库
auto_load_libs用来设置是否自动载入依赖的库,如果设置为True会自动载入依赖的库,然后分析到库函数调用时也会进入库函数,这样会增加分析的工作量。如果为False,程序调用函数时,会直接返回一个不受约束的符号值
设置SimState
SimState表示程序的状态,包括它的内存、寄存器等等
SimState对象通常有三种:
blank_state(**kwargs)
返回一个未初始化的state,此时需要主动设置入口地址,以及自己想要设置的参数entry_state(**kwargs)
返回程序入口地址的state,通常来说都会使用该状态full_init_state(**kwargs)
和entry_state(**kwargs)
类似,但是调用在执行到达入口点之前应该调用每个初始化函数
我们可以通过 state.regs
和 state.mem
访问寄存器和内存信息。
1 | state.regs.rip |
创建Simulation Managers
p.factory.simulation_manager创建一个simulation_manager进行模拟执行,其中传入一个SimState
SimulationManager一共有三种运行方式:
step()
每次向前运行一个基本块,并返回进行分类run()
运行完所有的基本块,然后会出现deadended的状态,此时我们通常访问最后一个状态来获取我们所需要的信息explore()
根据find和avoid进行基本块的执行,最后会返回found和avoid状态
获取执行结果
simgr.found存储了所有符合条件的分支
help命令
可以使用help命令查看某个函数的详细说明,比如help(state)
clarioy
claripy是一个符号求解引擎,和z3类似,通常用来构造输入,单位为bit
使用claripy.BVS()
创建位向量符号
1 | claripy.BVS("u", 8) |
使用claripy.BVV()
创建位向量值
1 | claripy.BVV(0xab, 0x2 * 8) |
CTF
这个地址是官方关于在CTF利用Angr的详细教程
这个地址是官方给出的在CTF比赛中使用Angr的例子,都是些精髓
此外还有@Badrer师傅在先知也写了很多相关的文章,大家可以学习一下
下面是一些我碰到的一些CTF例子,会不断地进行更新
funnyre
题目源于【WUSTCTF2020】
去花指令
直接用IDA打开,尝试反编译main函数发现有花指令,根据上下文逻辑nop掉图中红框部分,一共有4处
把data强制分析为code,再把修改过的code声明为function,之后就可以正常的反汇编了
main()函数中确定了flag的长度和格式,还有一长串的异或运算
最后与unk_4025C0的数据进行比对
符号执行
到这里就可以有三种解决方法,第一种是直接爆破,时间会很长;第二种是把所有代码逆回去,会很麻烦;第三种,也是最简单的一种,就是使用Angr来进行符号执行约束求解
下面给出符号执行的代码
1 | import angr |
very_success
题目来源于【flareon2015_2】
符号执行
程序主要代码如下,把用户输入存储到byte_402159
中,其中用户输入为37位,sub_401084()
对用户输入进行加密,最后与系统的a1
比较,相同则成功,其中a1
可以通过动态调试得到
所以我们可以把符号执行的初始地址地位在sub_401084()
的开始
选取好了初始地址之后,我们需要给参数进行赋值,这里涉及到对内存进行操控
第1个参数来源于0x401064
,即函数的返回地址
第2个参数来源于0x4010e4
,得追溯到父函数
第3个参数来源于0x402159
,直接是一个静态地址
第4个参数就是输入字符串的长度,直接赋一个大于等于37的数值即可
所以最终脚本如下
1 | import angr |