BruceFan's Blog

Stay hungry, stay foolish

0%

angr使用说明

angr是一个Python实现的二进制分析框架,实现了程序插桩、符号执行等二进制分析技术,还在安全顶会S&P发表了一篇论文
该项目的Github: https://github.com/angr/angr

安装angr

我的环境是Ubuntu 16.04,首先安装依赖库:

1
sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper

virtualenvwrapper是一个Python虚拟环境,使用虚拟环境的主要原因是angr会修改libz3和libVEX,可能会影响其他程序的正常使用。
新建一个Python虚拟机环境:

1
2
3
4
$ export WORKON_HOME=~/Envs
$ mkdir -p $WORKON_HOME
$ source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
$ mkvirtualenv angr

用pip安装angr:

1
$ pip install angr

使用案例

这个简单的例子说明了angr的用法。虽然通过逆向分析也很容易解决,但是为了介绍angr的基本用法,还是尝试用angr来解决。
下面是例子的源代码:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
char *sneaky = "SOSNEAKY";

int authenticate(char *username, char *password)
{
char stored_pw[9];
stored_pw[8] = 0;
int pwfile;

// evil back d00r
if (strcmp(password, sneaky) == 0) return 1;

pwfile = open(username, O_RDONLY);
read(pwfile, stored_pw, 8);

if (strcmp(password, stored_pw) == 0) return 1;
return 0;

}

int accepted()
{
printf("Welcome to the admin console, trusted user!\n");
}

int rejected()
{
printf("Go away!");
exit(1);
}

int main(int argc, char **argv)
{
char username[9];
char password[9];
int authed;

username[8] = 0;
password[8] = 0;

printf("Username: \n");
read(0, username, 8);
read(0, &authed, 1);
printf("Password: \n");
read(0, password, 8);
read(0, &authed, 1);

authed = authenticate(username, password);
if (authed) accepted();
else rejected();
}

程序需要输入用户名密码,authenticate()函数进行验证,失败则调用rejected()函数,成功则调用accepted()函数。
分析后,利用angr写出如下脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python

import angr
def basic_symbolic_execution():
p = angr.Project('fauxware')
state = p.factory.entry_state()

path = p.factory.path(state)

pathgroup = p.factory.path_group(path)
pathgroup.step(until=lambda lpg: len(lpg.active) > 1)
for i in range(len(pathgroup.active)):
print "possible %d: " % i, pathgroup.active[i].state.posix.dumps(0)

if __name__ == '__main__':
print basic_symbolic_execution()

1.新建一个angr工程,Project()中是目标二进制程序的路径

1
p = angr.Project('fauxware')

2.新建一个SimState对象

1
state = p.factory.entry_state()

SimState对象在angr其中的一个子模块SimuVEX中,它追踪且记录着符号信息、符号对应的内存信息和符号对应的寄存器信息,以及打开的文件信息等。可以通过Project.factory这个容器中的任何一个方法来获取SimState对象。这个factory有多个构造函数,如:block、entry_state等。这里使用entry_state返回一个初始化到二进制entry point的SimState对象。
3.使用factory.path获取state的起点path对象

1
path = p.factory.path(state)

相当于path的起点,之后将沿着这个起点向后进行。
4.根据前面获取的函数入口点的path对象,利用path_group获取沿着path起点下面将会执行的path列表

1
pathgroup = p.factory.path_group(path)

5.接下来就让pathgroup对象一直执行下去,直到执行到可选择的路径个数大于一个,即产生选择分支的时候,再停止。对应例子程序中authenticate()函数的if (strcmp(password, sneaky) == 0)这个条件判断语句

1
pathgroup.step(until = lambda lpg: len(lpg.active) > 1)

6.打印所有分支

1
2
for i in range(len(pathgroup.active)):
print "possible %d: " % i, pathgroup.active[i].state.posix.dumps(0)

总结

这里只是对angr有一个初步的认识,后面还要进一步学习它的其他用法和实现原理。
reference
https://github.com/angr/angr-doc