用比较官方的话来说,Angr 是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力和多种静态分析能力。
什么是符号执行呢,维基百科说是“通过采用抽象的符号代替精确值作为程序输入变量,得出每个路径抽象的输出结果。”在我看来就相当于暴力破解,符号执行可以尽可能的遍历每一条路径,从而得到满足程序的输入。
不过,“事实上,大多数程序不接受符号输入。在实现符号执行时,经典符号执行会通过程序分析技术得出程序的控制流程图,然后根据流程图得出符号执行结果。”也就是说,符号执行只是通过机器码来构建 CFG 图,然后模拟执行,并不是真的执行,所以在 Windows 环境下对 ELF 文件进行分析也是可以的。换句话说,“符号执行引擎是通过按行读取的方式模拟执行每条机器码,并更新对应变量,最后在通过约束求解的方式去逆推输入初值的。”
Angr 理论上是安全分析工具,这里只讨论其在 CTF-RE 解题中的应用,对于更深层的用法我反正是不会。
先举个例子,现在有一道题,伪代码如图所示:
常规做法就是把题目的密文和加密逻辑搞明白,然后逆向推出明文。有了 Angr ,就可以直接无视题目的加密逻辑,写脚本直接爆破:
这些代码都是什么意思下文再说,总之终端里会得到爆破结果:
对于秒杀一些签到题或是加密逻辑复杂的题目很有帮助。
通常使用 Angr 的步骤大概如下:
- 创建 project 并设置 state
- 新建符号量/位向量 并在内存或其它地方设置其存储位置
- 设置 Simulation Managers
- 运行,探索满足需要的路径
- 约束求解,获取执行结果
关于 Angr 在 CTF 中的应用,其实有一系列官方的例题在 GitHub 仓库,这里直接给出其他师傅编译好的题目文件链接:ZERO-A-ONE/AngrCTF_FITM: Angr CTF From introduction to mastery (github.com)。
使用 Angr 的第一步是新建一个工程,
import angr
proj = angr.Project('/bin/true') #这里填文件目录或名称
程序加载时会将二进制文件和共享库映射到虚拟地址中,通常我们在创建工程时选择关闭 auto_load_libs
以避免 Angr 加载共享库:
p = angr.Project('/bin/true', auto_load_libs=False)
project.factory
提供了很多类对二进制文件进行分析,它提供了几个方便的构造函数。
project.factory.block()
用于从给定地址解析一个 basic block;
程序的执行需要初始化一个 SimState
对象:
state = proj.factory.entry_state() #括号里填希望符号执行开始的地址
该对象包含了程序的内存、寄存器、文件系统数据等模拟运行时动态变化的数据,例如:
>>> state.regs # 寄存器名对象
<angr.state_plugins.view.SimRegNameView object at 0x7f126fdfe810>
>>> state.regs.rip # BV64 对象
<BV64 0x401370>
>>> state.regs.rsp
<BV64 0x7fffffffffeff98>
>>> state.regs.rsp.length # BV 对象都有 .length 属性
64
>>> state.regs.rdi
<BV64 reg_48_0_64{UNINITIALIZED}> # BV64 对象,符号变量
>>> state.mem[proj.entry].int.resolved # 将入口点的内存解释为 C 语言的 int 类型
<BV32 0x8949ed31>
这里的 BV,即 bitvectors,用于表示 Angr 里的 CPU 数据。使用 bitvectors 来设置寄存器和内存的值,当直接传入 python int 时,Angr 会自动将其转换成 bitvectors 。
来看一道例题:
程序上来就要我们输入正确的 flag 。把程序拖进 IDA ,看到以下伪代码:
逻辑很简单,对输入加密然后和已知密文进行比对。至于 LABEL_6 是对输入内容进行的极其繁琐的加密,一共有一千多行代码,直接逆是没辙,可以试试 Angr 。 成功得到 flag 。
关于 Angr 的学习先这么多,等深入了再接着记录。
参考链接:先知社区 - 深入浅出Angr(一) [原创] Angr 使用技巧速通笔记(一)-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com 先知社区 - angr 系列教程(一)核心概念及模块解读 angr入门及angr技术在CTF题目中的应用 - 吾爱破解论坛)