介绍

angr 是一个多架构二进制分析工具包,具有执行动态符号执行(如 Mayhem、KLEE 等)和对二进制文件进行各种静态分析的能力。

乍一看它真正吸引人的地方是它的符号执行引擎。简言之,符号执行意味着在不实际执行程序的情况下,就可以分析哪些输入经历了哪些代码路径。最常见的例子就是一个程序根据输入的一个字符串跟自身生成的字符串作比较来打印一些东西。符号执行允许我们像对待方程一样分析一个程序,解这个方程然后告诉我们正确的输入。

有很多ctf writeup之类的东西写到它,但是从学习者的角度太看,这些远远不够。

如何安装

建议你使用一个全新的ubuntu20.04虚拟机,直接运行下面的命令安装python3与依赖、虚拟环境组件:

1
sudo apt install python3-dev libffi-dev build-essential virtualenvwrapper

如果安装失败,那么你可以按照下面的顺序从angr的官方仓库依次安装,它们也是angr的主要组成模块:

  1. claripy: 符号求解器的抽象
  2. archinfo: 用于描述各种体系结构
  3. pyvex: VEX IR的Python包装
  4. cle: 二进制文件加载器
  5. angr: 主程序

测试一个最简单的例子

例如有如下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <string.h>

int main(int argc, char** argv) {
char a[10];
scanf("%s", a);

if (a != NULL) {
if (!strcmp(a, "fff")) {
printf("omg!\n");
} else {
printf("Hello, %s!\n", argv[1]);
}
}

return 0;
}

要如何才能让程序打印出”omg”?最笨的方式当然是手动分析程序流程:a要非空且与”fff”相等。

我们使用如下命令编译程序

1
2
# 关闭pie保护
gcc -no-pie ./c.c -o ./c

使用ida反汇编程序:

Snipaste_2022-05-10_15-26-22

可以看到若想打印出omg, 程序需要执行到相应的call _puts汇编指令处.

我们使用angr, 将其汇编指令地址填入print_omg_address变量中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 在您开始之前,这里有一些关于这些夺旗挑战的注意事项。每个二进制文件在运行时都会要求输入密码,密码可以通过stdin输入(在控制台中输入)。许多级别将接受许多不同的密码。您的目标是找到一个适用于每个二进制的密码。如果您输入的密码不正确,程序将打印“重试”。如果您输入正确的密码,程序将显示“干得好”。每个挑战都附带一个类似的文件,名为“scaffoldXX.py”。它将提供指导以及一个可能解决方案的框架。您必须编辑每个文件。在某些情况下,您必须对其进行大量编辑。虽然建议使用这些文件,但如果您发现它们限制太大,也可以不使用它们来编写解决方案。在scaffoldXX.py中,需要进行简单替换的地方将被标记为三个问号(??)。需要更多代码的地方将用省略号(…)标记。注释将记录任何新概念,但对于已经讨论过的概念将省略(您需要使用以前的scaffoldXX.py文件作为参考来解决这些挑战)。如果注释记录了需要更改的部分代码,那么它将在末尾用一个感叹号(!)标记出来。  

import angr

def main():
# 创建一个angr项目。如果希望能够从命令行指向二进制文件,可以使用argv[1]作为参数。然后,你可以像这样从命令行运行脚本:python ./scaffold00.py [binary] (!)
# path_to_binary = argv[1] # :string
project = angr.Project('/home/ylcao/Desktop/angr/c')
# 告诉Angr从哪里开始执行(应该从main()函数开始还是从其他地方开始?)现在,使用入口状态函数来指示Angr从main()函数开始。

initial_state = project.factory.entry_state()
# 创建用启动状态初始化的模拟管理器。它提供了许多有用的工具来搜索和执行二进制文件。
simulation = project.factory.simgr(initial_state)
# 探索二进制文件,试图找到打印“好工作”的地址;您必须找到您想要找到的地址,并将其插入这里。此函数将一直执行,直到找到解决方案或探索了可执行文件中的所有可能路径。(!)
print_omg_address = 0x40121A # :integer (probably in hexadecimal)
simulation.explore(find=print_omg_address)
# 找到了它能找到的符合我们要求它搜索的指令的状态列表。请记住,在Python中,如果列表为空,则将其计算为false,否则为true.
if simulation.found:
# explore方法在找到到达目标地址的单个状态后停止.
for i in simulation.found:
solution_state = i
# 输出Angr写给stdin的字符串以遵循解状态。这是我们的解。
print (solution_state.posix.dumps(0))
else:
# 如果Angr不能找到一个到达打印好的地址的路径,抛出一个错误。也许你把打印好的地址打错了?
print('[!] Could not find the solution!')


if __name__ == '__main__':
main()

运行脚本, 可以看到如下输出:

Snipaste_2022-05-10_15-29-43

可以看到angr已经帮我们找出了程序需要的解.

更多angr学习的练习可以查看此仓库:

jakespringer/angr_ctf
https://github.com/jakespringer/angr_ctf

⬆︎TOP