前言
之前已经把Angr下的所有方法进行了学习了解,但停留于表面等于没学,需要结合相关的题目进行应用才可以正确掌握。Angr在github的源码中以及提供有一个example的文件夹,里面包含了许多难易不一的比赛题目以及基于Angr的解题脚本。
链接在这里。
参考:Angr状态选项列表:https://docs.angr.io/appendix/options
正文
这里以一道简单的题目作为例子进行学习:ais3_crackme,在linux下查看可以看到这个程序是一个64位程序
我们可以直接把程序脱到64位IDA下进行分析:
第一眼看来很简单,只要if中的条件能够达成就可以了,那么重点就在verify。
程序自带的数据:
就是程序自带的数据与指令行的输入进行处理后的结果进行比较,满足条件即可。
如果没有angr我们可以选择爆破,构造对应条件爆破对位字符即可。脚本如下:
import string
key=[ 0xCA, 0x70, 0x93, 0xC8, 0x06, 0x54, 0xD2, 0xD5, 0xDA, 0x6A,
0xD1, 0x59, 0xDE, 0x45, 0xF9, 0xB5, 0xA6, 0x87, 0x19, 0xA5,
0x56, 0x6E, 0x63]
flag=""
for i in range(len(key)):
for a1 in string.printable:
if (((((((ord(a1))&0xff) ^ i) << ((i ^ 9) & 3))&0xff) | ((((( ord(a1))&0xff) ^ i) >> ((8 - ((i ^ 9) & 3))&0xff))&0xff))+ 8)&0xff==key[i]:
flag+=a1
print flag
这里可以选择Angr进行解题。
想要使用Angr,基本的操作就是先要将程序进行加载:
project = angr.Project("./ais3_crackme")
上面已经提到,这里是通过命令行获取参数args,所以我们需要claripy库构造一个符号状态,从IDA中不难看出flag的长度为23,所以设置如下:
u = claripy.BVS("u",23*8)
此时我们需要获取程序的入口状态,并代入参数:
state = project.factory.entry_state(args=["./ais3_crackme",u])
下面要做的就是构造一个模拟器将程序载入进行等候运行,以找寻正确答案:
sm = project.factory.simulation_manager(state)
下面就是运行程序,但是这里我们可以进行一些小小的设置,这个题目的尽头有两种可能,成功提示,错误提示,或者就是没有思路。emmm,那么在尝试的过程中会触碰到多条路径,探索的过程中的限制我们先不讨论,我们可以设置尽头,我们只想要能够通向成功的路径。
所以我们可以在运行的时候加入如下限制,find后面跟的就是正确结果,avoid就是错误结果:
sm.explore(find=0x400602,avoid=0x40060E)
下面使用eval将找到值进行转换得到结果就是flag了(cast_to就是转换的类型,目前只能指出int和str两种):
solution = found.solver.eval(argv1, cast_to=str)
最终跑出结果:
脚本在题目所在目录下已经包含,这里就不贴了。
小结
Angr通俗而言就是爆破,但是在其中包含有许多其他的技术,约束求解、解析程序等。上述题目解答逻辑较为清晰,所以可以自主编写爆破脚本进行解答。但是有的题目设置了比较复杂的算法在其中,这时候编写解题脚本的过程就较为复杂,且不考虑出错的情况。所以Angr可以帮助更快的得到结果。大致的解题过程:
- 对题目进行分析找到正确路径地址,运行中的检验条件,flag长度等一系列信息。
- 加载程序,设置参数(根据分析得到的flag长度进行设置,也可设置一个大致数值,关乎效率),获得入口状态(根据需求,添加参数,添加状态选项等)
- 根据IDA下分析添加约束条件,提高效率。
- 使用模拟器进行符号执行求解。
- 获得运算结果。