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:
使用案例
这个简单的例子说明了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;
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
|
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