├── INSTALL.md ├── README.md ├── SUMMARY.md └── docs ├── ir.md ├── loading.md ├── overview.md └── toplevel.md /INSTALL.md: -------------------------------------------------------------------------------- 1 | # 安装angr 2 | 3 | angr是一个python库,所以要使用它必须把它安装进你的python环境中。 4 | 它为python2而打造:对Py3k的支持在未来是可行的,但是我们现在有些犹豫现在是否要做出那样的承诺(欢迎pull requests!)。 5 | 6 | 我们强烈推荐使用[python虚拟环境](https://virtualenvwrapper.readthedocs.org/en/latest/)来安装和使用angr。 7 | 几种不同的angr依赖(z3,pyvex)需要从它们的原始库中fork原生代码库,如果你已经安装libz3或者libVEX,你肯定不想使用我们的覆盖官方的共享库。 8 | 总的来说,不要期待对从非虚拟环境中安装angr产生的问题的支持。 9 | 10 | ## 依赖 11 | 12 | 所有的python依赖都应该通过pip或者setup.py脚本处理。 13 | 但是你将会需要编译一些C代码从这里直到结束,所以你需要一个良好的编译环境以及python开发头文件。 14 | 在依赖安装过程中,你需要安装python库cffi,但是(至少在linux上)你还需要安装了你操作系统的libffi包它才可以运行。 15 | 16 | 在Ubuntu上,你需要:`sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper` 17 | 18 | 19 | ## 多数操作系统,全部\*nix系统 20 | 21 | `mkvirtualenv angr && pip install angr` 在大多数情况下用来安装angr应该足够了, 因为angr被发布在Python包索引中。 22 | 23 | Fish(shell)用户也可以使用[virtualfish](https://github.com/adambrenecki/virtualfish) 或者[virtualenv](https://pypi.python.org/pypi/virtualenv)包。 24 | `vf new angr && vf activate angr && pip install angr` 25 | 26 | 如果使用这些失败了,你可以按照顺序通过安装下面来自https://github.com/angr的仓库(以及在它们的requirements.txt 文件中列出来的依赖包): 27 | 28 | - [claripy](https://github.com/angr/claripy) 29 | - [archinfo](https://github.com/angr/archinfo) 30 | - [pyvex](https://github.com/angr/pyvex) 31 | - [cle](https://github.com/angr/cle) 32 | - [simuvex](https://github.com/angr/simuvex) 33 | - [angr](https://github.com/angr/angr) 34 | 35 | ## Mac OS X 36 | 37 | 在你使用`pip install angr`之前,你需要使用`pip install -I --no-use-wheel angr-only-z3-custom`重新编译我们fork来的z3库。 38 | 39 | ## Windows 40 | 41 | 在windows上你无法通过pip安装angr。 42 | 你必须单独安装它的所有组件。 43 | 44 | Capstone在windows上很难安装。 45 | 你可能需要手动操作wheel文件来安装它,但是有时候它会使用一个不同于“capstone”的名字安装,所以如果如果发生了这样的事,你只需要(在安装它之后)在angr和archinfo的requirements.txt文件中移除Capstone就可以了。 46 | 47 | 如果你有足够的l33t编译环境可以在windows上编译Z3. 48 | 如果你没有,你需要从网上下个wheel文件。 49 | 在可以下载预编译好的windows的wheel文件。 50 | 51 | 如果你从源代码编译z3,请确保你在使用包含了浮点支持的不稳定版本的z3。并且确保 `Z3PATH=path/to/libz3.dll`在你的环境变量里。 52 | 53 | ## 开发安装 54 | 55 | 我们使用脚本创建了一个仓库来使一切对于angr的开发者变得更容易。 56 | 你可以把angr设置为开发模式通过: 57 | 58 | ```bash 59 | git clone https://github.com/angr/angr-dev 60 | cd angr-dev 61 | mkvirtualenv angr 62 | ./setup.sh 63 | ``` 64 | 65 | 这克隆了所有的仓库并且把它们安装成了可编辑模式。 66 | `setup.sh` 甚至可以为你创建一个PyPy虚拟环境,这使得其有了更快的性能和更低的内存占用。 67 | 68 | 你可以对不同的模块进行创建分支/编辑/重新编译操作,产生的变化会自动映射到你的虚拟环境中。 69 | 70 | ## 使用Docker安装 71 | 72 | 为了方便,我们只做了一个Docker镜像并且保证99%的可用性。 73 | 你可以通过docker来安装: 74 | 75 | ```bash 76 | # install docker 77 | curl -sSL https://get.docker.com/ | sudo sh 78 | 79 | # pull the docker image 80 | sudo docker pull angr/angr 81 | 82 | # run it 83 | sudo docker run -it angr 84 | ``` 85 | 86 | docker内外的文件同步作为留给使用者的一个练习(提示:看看 `docker -v`) 87 | 88 | # 疑难解决 89 | 90 | ## libgomp.so.1: version `GOMP_4.0' not found 91 | 92 | 这个错误表明在预编译版本的`angr-only-z3-custom`和安装版本的`libgomp`存在不兼容问题。需要重新编译Z3.你可以执行: 93 | 94 | ```bash 95 | pip install -I --no-use-wheel angr-only-z3-custom 96 | ``` 97 | ## Can't import mulpyplexer 98 | 99 | 在安装mulpyplexer有时会有一些问题。执行`pip install --upgrade 'git+https://github.com/zardus/mulpyplexer'` 可以解决这个问题。 100 | 101 | ## Can't import angr because of capstone 102 | 103 | 有时capstone没有被正确安装。重新安装capstone可以解决这个问题: 104 | 105 | ```bash 106 | pip install -I --no-use-wheel capstone 107 | ``` 108 | 109 | ## Claripy and z3 110 | 111 | Z3编译起来很奇怪。有时它会无缘无故的完全失败,提示由于一些文件或文件夹不存在而无法创建对象文件。你只需要重新编译: 112 | 113 | ```bash 114 | pip install -I --no-use-wheel angr-only-z3-custom 115 | ``` 116 | 117 | ## No such file or directory: 'pyvex_c' 118 | 119 | 你在使用12.04吗?如果是,请升级吧! 120 | 你也可以试试升级pip(`pip install -U pip`),也许也可以解决这个问题。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 如何使用angr 2 | 3 | 这是一份对angr文档的收集。通过阅读这份文档,你可以成为一个angr专业人员并且可以随意操作二进制文件。 4 | 5 | 我们尽力让angr易于上手——我们的目标是创建一个对用户友好的二进制分析工具套件,让用户可以在iPython中通过一系列命令操作二进制文件。但是遗憾的是二进制分析是一件复杂的事情,这使得angr也变得复杂。这篇文档致力于通过提供对angr及其设计的解释和探索来解决这个问题。 6 | 7 | # 开始 8 | 9 | 安装说明可以看[这里](./INSTALL.md)。 10 | 11 | 为了深入探索angr的能力,你可以从它的[顶层方法](./docs/toplevel.md)入手,或者阅读[这篇总览](./docs/overview.md)。 12 | 13 | 这篇文档支持搜索功能的html版本放在[docs.angr.io](http://docs.angr.io/),HTML API可以查看[angr.io/api-doc](http://angr.io/api-doc/)。 14 | 15 | # 引用angr 16 | 17 | 如果你想要在学术工作中使用angr,请引用这些论文: 18 | 19 | ``` 20 | @article{shoshitaishvili2016state, 21 | title={SoK: (State of) The Art of War: Offensive Techniques in Binary Analysis}, 22 | author={Shoshitaishvili, Yan and Wang, Ruoyu and Salls, Christopher and Stephens, Nick and Polino, Mario and Dutcher, Andrew and Grosen, John and Feng, Siji and Hauser, Christophe and Kruegel, Christopher and Vigna, Giovanni}, 23 | booktitle={IEEE Symposium on Security and Privacy}, 24 | year={2016} 25 | } 26 | 27 | @article{stephens2016driller, 28 | title={Driller: Augmenting Fuzzing Through Selective Symbolic Execution}, 29 | author={Stephens, Nick and Grosen, John and Salls, Christopher and Dutcher, Andrew and Wang, Ruoyu and Corbetta, Jacopo and Shoshitaishvili, Yan and Kruegel, Christopher and Vigna, Giovanni}, 30 | booktitle={NDSS}, 31 | year={2016} 32 | } 33 | 34 | @article{shoshitaishvili2015firmalice, 35 | title={Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware}, 36 | author={Shoshitaishvili, Yan and Wang, Ruoyu and Hauser, Christophe and Kruegel, Christopher and Vigna, Giovanni}, 37 | booktitle={NDSS}, 38 | year={2015} 39 | } 40 | ``` 41 | 42 | # 支持 43 | 44 | 如果想要获取关于angr的帮助,你可以通过: 45 | 46 | - 邮件: angr@lists.cs.ucsb.edu 47 | - IRC频道([freenode](https://freenode.net/)):**#angr** 48 | - 在github对应仓库上打开一个issue 49 | 50 | # 深入阅读 51 | 这篇[论文](https://www.cs.ucsb.edu/~vigna/publications/2016_SP_angrSoK.pdf)解释了angr的一些内部原理,算法和使用到的技术,你可以阅读它来进一步理解在angr运行中会发生什么。 52 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 3 | * [介绍](README.md) 4 | * [总览](docs/overview.md) 5 | * [安装](INSTALL.md) 6 | * [如何贡献](HACKING.md) 7 | * [贡献什么](HELPWANTED.md) 8 | * [顶层接口](docs/toplevel.md) 9 | * [装载二进制文件](docs/loading.md) 10 | * [中间描述](docs/ir.md) 11 | * [求解引擎](docs/claripy.md) 12 | * [程序状态](docs/states.md) 13 | * [符号执行](docs/symbolic.md) 14 | * [执行引擎](docs/simuvex.md) 15 | * [控制执行](docs/paths.md) 16 | * [大量执行-路径组](docs/pathgroups.md) 17 | * [大量执行-测量者](docs/surveyors.md) 18 | * [用数据及惯例工作](docs/structured_data.md) 19 | * [分析报告](docs/analyses.md) 20 | * [控制流图](docs/analyses/cfg_accurate.md) 21 | * [后向分片](docs/analyses/backward_slice.md) 22 | * [样例](docs/examples.md) 23 | * [常见问题](docs/faq.md) 24 | * [陷阱](docs/gotchas.md) 25 | * [变更日志](CHANGELOG.md) 26 | 27 | -------------------------------------------------------------------------------- /docs/ir.md: -------------------------------------------------------------------------------- 1 | # 中间语言 2 | 3 | 由于angr需要处理很多不同的架构,所以它必须选择一种中间语言(IR)来进行它的分析。我们使用Valgrind的中间语言,VEX来完成这方面的内容。VEX中间语言抽象了几种不同架构间的区别,允许在他们之上进行统一的分析: 4 | 5 | - **寄存器名**。在不同架构间的寄存器数量和名字是不一样的,但是现代的各CPU设计有通用之处:每一种CPU包含几个通用寄存器,一个寄存器装载栈指针,一系列寄存器装载状态标志等等。中间语言提供了一个统一的、抽象的对于不同平台的寄存器接口。VEX模型将寄存器作为一个独立的内存空间,使用偏移来访问它们(比如,AMD64的rax寄存器在这个内存空间的偏移16的地址上)。 6 | - **内存访问**。不同的架构使用不同的方式访问内存。比如ARM既可以通过小端序也可以通过大端序来访问内存。中间语言必须能够抽象分离出其中的差异。 7 | - **内存分段**。一些架构,比如x86,通过使用特殊的段寄存器实现内存的分段。中间语言能够理解这样 的内存访问机制。 8 | - **指令的副作用**。大多数的指令有产生一些影响。比如,ARM中Thumb模式下的大多数操作会更新状态标志,栈上的push/pop操作更新栈指针。在分析中通过*ad hoc* 的方式来跟踪这些影响是愚蠢的,所以中间语言使这些影响很清晰直接。 9 | 10 | 对我们来说中间语言有很多选择,我们选择了VEX,因为将二进制代码转换为VEX已经有了很好的支持。VEX是一种支持大量目标机器语言的架构无关、无副作用的语言。它抽象了机器指令到中间表达来使程序更易于分析。这一中间语言有四个主要的对象类: 11 | 12 | - **表达式(Expressions)**。IR表达式代表了一个计算出的数值或者常量。这包括了内存装载,读寄存器以及算数计算的结果。 13 | - **操作(Operations)**。IR操作描述了对IR表达式的修改。这包括了整形的运算,浮点型的运算,位运算等等。一个IR操作应用于IR表达式会产生一个IR表达式作为结果。 14 | - **临时变量(Temporary variables)**。VEX使用临时变量作为内部寄存器:IR表达式在使用过程中存储在临时变量中。临时变量的值可以通过IR表达式重新获取。这些临时变量被从t0开始编号,且是强类型的(比如64位的整形或者32位的浮点型)。 15 | - **语句(Statements)**。IR语句模型根据目标机器而改变,比如内存存储和写寄存器产生的效果,IR语句使用IR表达式获取可能用到的值。比如,一个内存存储操作的IR语句使用IR表达式作为要写入的目标地址,使用另一个IR表达式作为要写入的内容。 16 | - **块(Blocks)**。一个IR块是一系列IR语句的集合,代表了目标架构上的一个扩展块(术语为“IR超级块(IR Super Block)”或者“IRSB”)。一个超级块可以有多个出口。在基本块中间有条件退出时,会使用特殊的*退出*IR语句。一个IR表达式被用来代表在块的最后无条件退出时的目标指向。 17 | 18 | 在VEX仓库中,VEX IR在`libvex_ir.h`文件中有很好的文档说明(https://github.com/angr/vex/blob/master/pub/libvex_ir.h)。我们在这里详细列出部分你可能会频繁使用的一个IR表达式: 19 | 20 | | IR 表达式 | 评估值 | VEX 输出示例 | 21 | | --------------- | ---------------------------------------- | ------------------- | 22 | | Constant | 一个常量值 | 0x4:I32 | 23 | | Read Temp | 存储在VEX临时变量中的值。 | RdTmp(t10) | 24 | | Get Register | 存储在一个寄存器中的值。 | GET:I32(16) | 25 | | Load Memory | 存储在内存中的值,通过另一个IR表达式来指定地址。 | LDle:I32 / LDbe:I64 | 26 | | Operation | IR操作的结果,作为指定的IR表达式的参数。 | Add32 | 27 | | If-Then-Else | 如果给定的IR表达式值为0,返回一个IR表达式,否则返回另一个。 | ITE | 28 | | Helper Function | VEX使用C的帮助函数执行确定的操作,比如计算确定架构上的状态标志寄存器。这些函数返回IR表达式。 | function\_name() | 29 | 30 | 这些表达式使用于IR语句中。这些是一些常见的IR语句: 31 | 32 | | IR 语句 | 含义 | VEX 输出示例 | 33 | | ------------ | -------------------------------------- | ---------------------------------------- | 34 | | Write Temp | 使用给定的IR表达式设置一个VEX临时变量。 | WrTmp(t1) = (IR Expression) | 35 | | Put Register | 使用给定的IR表达式更新寄存器。 | PUT(16) = (IR Expression) | 36 | | Store Memory | 使用给定的IR表达式作为地址,另一个IR表达式作为值,更新目标内存。 | STle(0x1000) = (IR Expression) | 37 | | Exit | 从基本块中的条件退出,使用IR表达式指定跳转目标。其条件也由IR表达式指定。 | if (condition) goto (Boring) 0x4000A00:I32 | 38 | 39 | 下面是将ARM架构上的指令转换成VEX IR的一个例子。在这个例子中,减操作被转换成一个由5个IR语句组成的IR块,每一个语句包含至少一个IR表达式(虽然在实际应用中,一个IR块基本都不止一条指令)。寄存器名被转换成大量的索引传递给GET表达式和PUT表达式。精明的读者将会注意到实际的减法操作由前4个IR语句完成,将程序指针指向下一个指令(在这个例子中应该是`0x59FC8`)由最后一条指令完成。 40 | 41 | 下面是ARM指令: 42 | 43 | ``` 44 | subs R2, R2, #8 45 | ``` 46 | 47 | 转换为VEX IR: 48 | 49 | ``` 50 | t0 = GET:I32(16) 51 | t1 = 0x8:I32 52 | t3 = Sub32(t0,t1) 53 | PUT(16) = t3 54 | PUT(68) = 0x59FC8:I32 55 | ``` 56 | 57 | 既然你已经懂得了VEX,你可以动手试一试angr中的VEX:我们使用一个叫做PyVEX的库(https://github.com/angr/pyvex)作为VEX和python的接口。PyVEX实现了它自己的友好输出,所以它可以显示寄存器名而不是使用在PUT和GET操作中的寄存器偏移。 58 | 59 | PyVEX可以通过 `Project.factory.block`接口来访问。有很多种不同的对象可以用来来访问一个块的属性,但是他们在分析特定的字节序列的时候具有共通特性。通过`factory.block` 构造器,你可以得到一个能够轻松转换成几种不同代表的 `Block`对象。尝试`.vex` 来获取PyVEX的IRSB,或者`.capstone`获取Capstone块。 60 | 61 | 让我们来使用PyVEX: 62 | 63 | ```python 64 | >>> import angr 65 | 66 | # 装载二进制程序 67 | >>> b = angr.Project("/bin/true") 68 | 69 | # 转换入口点为基本块 70 | >>> irsb = b.factory.block(b.entry).vex 71 | >>> irsb.pp() 72 | 73 | # 转换特定地址为基本块 74 | >>> irsb = b.factory.block(0x401340).vex 75 | >>> irsb.pp() 76 | 77 | # 这是代表了这一基本块的最后无条件退出时的跳转目标的IR表达式 78 | >>> print irsb.next 79 | 80 | # 这一无条件退出的类型(比如,一个函数调用,或者从一个函数返回,或者是系统调用等等) 81 | >>> print irsb.jumpkind 82 | 83 | # 你也可以将它以良好的可读方式打印出来 84 | >>> irsb.next.pp() 85 | 86 | # 遍历每一个语句并且将它们打印出来 87 | >>> for stmt in irsb.statements: 88 | ... stmt.pp() 89 | 90 | # 打印代表了数据的IR表达式以及其被对应的存储语句存储下来的类型 91 | >>> import pyvex 92 | >>> for stmt in irsb.statements: 93 | ... if isinstance(stmt, pyvex.IRStmt.Store): 94 | ... print "Data:", 95 | ... stmt.data.pp() 96 | ... print "" 97 | ... print "Type:", 98 | ... print stmt.data.result_type 99 | ... print "" 100 | 101 | # 打印基本块中每一个条件退出的条件和跳转目标 102 | ... for stmt in irsb.statements: 103 | ... if isinstance(stmt, pyvex.IRStmt.Exit): 104 | ... print "Condition:", 105 | ... stmt.guard.pp() 106 | ... print "" 107 | ... print "Target:", 108 | ... stmt.dst.pp() 109 | ... print "" 110 | 111 | # 这些是在IRSB中的每一个临时变量的类型 112 | >>> print irsb.tyenv.types 113 | 114 | # 这是获取第0个临时变量的类型的一种方法 115 | >>> print irsb.tyenv.types[0] 116 | ``` 117 | 118 | 记住这是一个基本块的句法表达。也就是说,它会告诉你这个块表示什么,但是你没有任何上下文可以联系,比如说一个存储指令究竟存储了什么数据。我们会在以后进一步理解这一点。 -------------------------------------------------------------------------------- /docs/loading.md: -------------------------------------------------------------------------------- 1 | # 装载二进制文件 - CLE和angr Project 2 | 3 | angr的二进制装载组件是CLE,它负责装载二进制对象(以及它依赖的任何库)和把这个对象以易于操作的方式交给angr的其他组件。 4 | 5 | CLE的主要目标是使用强大的方式来装载二进制文件,类似于真实的装载器(比如GNU LD装载ELF文件那样)来装载它们。这意味着二进制文件中的部分信息将会被CLE忽略,因为这些信息可能是被删减过的或是有意无意地破坏过的,这在嵌入式世界中很常见。 6 | 7 | angr将这些包含在*Project*类中。一个Project类是代表了你的二进制文件的实体。你与angr的大部分操作都会经过它。 8 | 9 | 使用angr装载一个二进制文件(比如说,“/bin/true”),你需要这样做: 10 | 11 | ```python 12 | >>> import angr 13 | 14 | >>> b = angr.Project("/bin/true") 15 | ``` 16 | 17 | 这样操作之后,b就是你的主二进制文件以及它依赖的所有库的代表。这时,即便你没有进一步关于angr的知识,你也可以做一些基础的事情比如: 18 | 19 | ```python 20 | # 这是二进制文件的入口点 21 | >>> print b.entry 22 | 23 | # 这些是二进制文件内存空间中的最小地址和最大地址 24 | >>> print b.loader.min_addr(), b.loader.max_addr() 25 | 26 | # 这些是文件的全名 27 | >>> print b.filename 28 | ``` 29 | 30 | CLE通过这个装载类来呈现二进制文件的信息。CLE装载器(cle.Loader)代表了已经装载了的和映射到内存空间中的CLE二进制对象。每一种二进制对象都由一种可以处理这种文件类型的后端装载器(cle.Backend的子类)装载。比如cle.ELF用来装载ELF文件。 31 | 32 | CLE可以像下面这样来交互: 33 | 34 | ```python 35 | # 这是一个CLE装载器对象 36 | >>> print b.loader 37 | 38 | # 这是一个dict,包含着已经作为二进制文件的一部分而装载的对象(它们的种类取决于后端装载器) 39 | >>> print b.loader.shared_objects 40 | 41 | # 这是装载后的进程的内存空间。它包含具体地址与该地址上的值 42 | >>> print b.loader.memory[b.loader.min_addr()] 43 | 44 | # 这是主要的二进制对象(种类取决于后端装载器) 45 | >>> print b.loader.main_bin 46 | 47 | # 它们取回映射在指定地址的二进制对象 48 | >>> print b.loader.addr_belongs_to_object(b.loader.max_addr()) 49 | 50 | # 获取指定符号的GOT条目(在主二进制对象中) 51 | >>> print b.loader.find_symbol_got_entry('__libc_start_main') 52 | ``` 53 | 54 | 你也可以直接与独立的二进制对象交互: 55 | 56 | ```python 57 | # 这是程序依赖的库名的list 58 | # 通过读取Elf文件的dynamic section的DT_NEEDED域获取 59 | >>> print b.loader.main_bin.deps 60 | 61 | # 这是关于主二进制对象的内存内容的dict 62 | >>> print b.loader.main_bin.memory 63 | 64 | # 这是一个装载的libc所需的导入条目的dict(name->ELFRelocation) 65 | >>> b.loader.shared_objects['libc.so.6'].imports 66 | 67 | # 这是一个主二进制对象所需的导入条目的dict(name->ELFRelocation),其地址通常是0(请看下面的“杂项”一节) 68 | >>> print b.loader.main_bin.imports 69 | ``` 70 | 71 | ## 装载依赖项 72 | 73 | CLE将会默认地尝试装载主二进制文件所需的所有依赖(比如libc.so.6,ld-linux.so.2等),除非装载选项中的`auto_load_libs`设置为`False`。当装载库文件的时候,如果无法找到,装载器会默认忽略产生的错误并标记所有关于那个库的依赖是为解决的。你也可以改变装载器的这一行为。 74 | 75 | ## 装载选项 76 | 77 | 装载选项可以传递给Project(它会传递给CLE)。 78 | 79 | CLE需要一个参数的dict。需要应用到库而不是目标主二进制的需要通过lib_opts参数来传递: 80 | 81 | ```python 82 | load_options = {'main_opts':{options0}, 'lib_opts': {libname1:{options1}, path2:{options2}, ...}} 83 | 84 | # 或者以更易读的方式 85 | load_options = {} 86 | load_options['main_opts'] = {k1:v1, k2:v2 ...} 87 | load_options['lib_opts'] = {} 88 | load_options['lib_opts'][path1] = {k1:v1, k2:v2, ...} 89 | load_options['lib_opts'][path2] = {k1:v1, k2:v2, ...} 90 | etc. 91 | ``` 92 | 93 | 94 | 95 | ### 有效的选项 96 | 97 | ```python 98 | >>> load_options = {} 99 | 100 | # 是否需要装载动态链接库 101 | >>> load_options['auto_load_libs'] = False 102 | 103 | # 无论是否是目标二进制文件所需要的,强制装载的库的list 104 | >>> load_options['force_load_libs'] = ['libleet.so'] 105 | 106 | # 需要跳过的库的list 107 | >>> load_options['skip_libs'] = ['libc.so.6'] 108 | 109 | # 装载主二进制文件时的选项 110 | >>> load_options['main_opts'] = {'backend': 'elf'} 111 | 112 | # 映射库名到其装载时需要使用的选项dict的dict 113 | >>> load_options['lib_opts'] = {'libc.so.6': {'custom_base_addr': 0x13370000}} 114 | 115 | # 可以进行额外搜索的路径list 116 | >>> load_options['custom_ld_path'] = ['/my/fav/libs'] 117 | 118 | # 是否将文件名中版本号不同的库视作相同的,比如libc.so.6和libc.so.0 119 | >>> load_options['ignore_import_version_numbers'] = False 120 | 121 | # 在重定位共享对象的基址的时候需要使用的对齐值 122 | >>> load_options['rebase_granularity'] = 0x1000 123 | 124 | # 如果找不到一个库,抛出一个异常(默认行为是忽略未找到的库) 125 | >>> load_options['except_missing_libs'] = True 126 | ``` 127 | 128 | 接下来的选项被应用于每一个对象并且覆盖CLE的自动检测。 129 | 130 | 它们可以通过main_opts或者lib_opts来应用。 131 | 132 | ```python 133 | # 装载二进制文件的基址 134 | >>> load_options['main_opts'] = {'custom_base_addr':0x4000} 135 | 136 | # 指定对象的后端装载器(下面有相关的讨论) 137 | >>> load_options['main_opts'] = {'backend': 'elf'} 138 | ``` 139 | 140 | 对同一二进制文件使用多选项的例子: 141 | 142 | ```python 143 | >>> load_options['main_opts'] = {'backend':'elf', 'custom_base_addr': 0x10000} 144 | ``` 145 | 146 | ## 后端 147 | 148 | CLE现在有对于ELF、PE、CGC和ELF核心转储文件的后端支持,像IDA装载二进制文件一样将文件装载到平坦的地址空间中。在大部分时间中,CLE会自动检测需要使用的正确后端,所以你不需要指定后端类型除非你在处理一些奇怪的东西。 149 | 150 | 你可以通过在选项中包含一个关键字的方式来指定后端。如果你需要强制指定目标文件的架构而不是自动检测,你可以通过custom_arch关键字。这个关键字不需要完全匹配上具体的架构列表,angr能够通过其所支持的架构的几乎所有常见的标识符来识别出你给出的架构。 151 | 152 | ```python 153 | >>> load_options = {} 154 | >>> load_options['main_opts'] = {'backend': 'elf', 'custom_arch': 'i386'} 155 | >>> load_options['lib_opts'] = {'libc.so.6': {'backend': 'elf'}} 156 | ``` 157 | 158 | | 后端关键字 | 描述 | 需要 `custom_arch`? | 159 | | --------- | --------------------------- | ----------------- | 160 | | elf | 基于PyELFTools的ELF装载器 | no | 161 | | pe | 基于PEFile的PE装载器 | no | 162 | | cgc | Cyber Grand Challenge文件的装载器 | no | 163 | | backedcgc | 支持指定内存和寄存器支持的CGC文件装载器 | no | 164 | | elfcore | ELF核心转储文件的装载器 | no | 165 | | ida | 启动IDA来解析文件 | yes | 166 | | blob | 装载文件到内存中作为一个平坦的镜像 | yes | 167 | 168 | 既然你已经装载了一个二进制文件,你已经可以通过`b.loader.main_bin`来获取一些有意思的信息。比如,共享库依赖,导入的库、内存、符号以及其他的list。充分使用IPython的tab补全来查看有趣的函数和选项吧。 169 | 170 | 现在是时候看看[IR支持](./ir.md)了。 171 | 172 | ## 杂项 173 | 174 | ### 导入项 175 | 176 | 接下来的是和ELF相关的。 177 | 178 | 在大多数架构上,导入项,比如一些符号引用自二进制文件之外(共享库)中的函数或者全局变量会出现在符号表中,它们的地址往往都是0。在一些架构比如MIPS中,它包含了函数的PLT内容的地址(在代码段中)。 179 | 180 | 如果你在寻找某一符号的GOT条目(在数据段中),可以看看jmprel。它是一个dict(符号->GOT地址)。 181 | 182 | 无论你是在找PLT条目还是GOT条目,都依赖于架构。架构相关的内容定义在Archinfo仓库的一个类中。我们对不同架构下函数的绝对地址的处理定义在这个类的got_section_name属性中。 183 | 184 | 有关ELF装载和架构相关的进一步细节,你可以参阅[可执行文件和可链接文件格式文档](http://www.cs.northwestern.edu/~pdinda/icsclass/doc/elf.pdf)以及每一个架构([MIPS](http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf), [PPC64](http://math-atlas.sourceforge.net/devel/assembly/PPC-elf64abi-1.7.pdf), [AMD64](http://www.x86-64.org/documentation/abi.pdf))的ABI实现。 185 | 186 | ```python 187 | >>> rel = b.loader.main_bin.jmprel 188 | ``` 189 | 190 | ### 符号分析 191 | 192 | Project默认尝试替换对库函数的外部调用,通过使用在[符号总结](./todo.md)中标明的SimProcedures(是关于函数如何影响state的总结)。 193 | 194 | 当指定函数没有相关总结的时候: 195 | 196 | - 如果`auto_load_libs`是`True`(默认值),真正的库函数会被执行。这可能正是也可能不是你想要的,取决于具体的函数。比如说一些libc的函数分析起来过于复杂并且很有可能引起[path](./paths.md)对其的尝试执行过程中的state数量的爆炸增长。 197 | - 如果`auto_load_libs`是`False`,且外部函数是无法找到的,并且Project会将它们引用到一个通用的叫做`ReturnUnconstrained`的`SimProcedure`上去,它就像它的名字所说的那样:它返回一个不受约束的值。 198 | - 如果`use_sim_procedures`(这是一个传递给angr.Project的参数,不是给cle.Loader的)是`False`的话(默认是True),那么除了`ReturnUnconstrained`意外没有`SimProcedure`会被使用。 199 | - 你可以指定一些符号不被`SimProcedures`替换,这通过传递给`angr.Project`的`exclude_sim_procedures_list`和`exclude_sim_procedures_func`来完成。 200 | - 通过参阅`angr.Project._use_sim_procedures`的源码来查看具体的算法。 201 | 202 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | # angr是什么 2 | 3 | angr是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力(例如Mayhem,KLEE等)和多种静态分析能力。 4 | 5 | 大概看来,要做到这些必须要克服一些问题: 6 | 7 | - 装载二进制文件到到分析平台 8 | - 转换二进制文件为中间语言(intermediate representation)(IR) 9 | - 转换IR为语义描述(即它做什么而不是它是什么) 10 | - 执行真正的分析,这包括: 11 | - 部分或者全部的静态分析(即依赖分析,程序分片) 12 | - 对程序状态空间的符号探索(比如“我们能否一直执行它直到我们找到了一个溢出?”) 13 | - 对上述的情况的一些混合(比如“让我们执行一部分可导致内存写的内存切片,来发现一个溢出”) 14 | 15 | angr拥有的组件能满足所有这些挑战。 16 | 这本书将会向你解释每一个组件是如何工作的,以及如何使用它们来完成你的邪恶目标(原文:accomplish your evil goals)。 17 | 18 | # 装载二进制程序 19 | 20 | angr安装完成后,你就可以装载二进制程序进行分析了。这个过程以及支持它的angr组件CLE的详细描述在[这里](./loading.md)。 21 | 22 | # 中间语言(Intermediate Representation) 23 | 24 | angr使用中间语言(具体来说,VEX)在不同的架构上分析二进制文件。关于IR的详细说明在[这里](./ir.md)。 25 | 26 | # 求解引擎(Solver Engine) 27 | 28 | angr的子模块Claripy解决约束求解和一些其它计算性的需求。大多数的angr使用者不需要知道Claripy的任何事,但我们仍然提供文档以防万一。其详细说明可以看[这里](./claripy.md)。 29 | 30 | # 程序状态(Program States) 31 | 32 | angr提供模拟的程序状态的接口。理解它是成功使用angr的关键。详细说明可以看[这里](./states.md)。 33 | 34 | # 程序路径(Program Paths) 35 | 36 | 程序可以通过其所有可能的执行路径来分析。angr会揭露关于路径如何被执行的信息。[这一节](./paths.md)给出了如何使用angr这一能力的总览。 37 | 38 | # 语义表达(Semantic Representation) 39 | 40 | angr的一个强大的能力就是通过基本块对于程序状态的影响来描述它们。换句话说,angr可以推断出基本块做什么,而不仅仅是它们是什么。这通过SimuVEX模块来完成,详细描述可以看[这里](./simuvex.md)。 41 | 42 | # 符号执行(Symbolic Execution) 43 | 44 | angr提供了强大的符号执行引擎。这个引擎的接口以及使用方法在[这里](./surveyors.md)。 45 | 46 | # 完整的程序分析 47 | 48 | 上面描述的所有模块组合起来使得angr可以进行复杂的、完全的程序分析。关于进行和记录分析的机制的详细说明可以看[这里](./analyses.md)。 49 | 50 | # 例子 51 | 52 | 我们准备了一些使用angr的例子!你可以在[这里](./examples.md)找到它们。 -------------------------------------------------------------------------------- /docs/toplevel.md: -------------------------------------------------------------------------------- 1 | 2 | 顶层接口 3 | ==================== 4 | 5 | 当你已经装载了一个工程,下面该做什么呢? 6 | 7 | 这篇文档解释了可以直接从`angr.Project`中得到的所有的属性。 8 | 9 | ## 基础属性 10 | 11 | ```python 12 | >>> import angr, monkeyhex, claripy 13 | >>> b = angr.Project('/bin/true') 14 | 15 | >>> b.arch 16 | 17 | >>> b.entry 18 | 0x401410 19 | >>> b.filename 20 | '/bin/true' 21 | >>> b.loader 22 | 23 | ``` 24 | 25 | - *arch*是`archinfo.Arch`对象的一个实例,代表了目标程序是为了哪种架构而编译的。更多信息在[这里](https://github.com/angr/archinfo/blob/master/archinfo/arch_amd64.py)。你经常会用到的是`arch.bits`,`arch.bytes` (它在[`Arch`类](https://github.com/angr/archinfo/blob/master/archinfo/arch.py)中使用`@property` 声明),`arch.name`, 和`arch.memory_endness`. 26 | - *entry*是二进制程序的入口点。 27 | - *filename*是程序的绝对路径。 28 | - *loader*是[cle.Loader](https://github.com/angr/cle/blob/master/cle/loader.py)的关于这个程序的一个实例。详细使用方法可以看[这里](./loading.md)。 29 | 30 | ## 分析与探索者 31 | 32 | ```python 33 | >>> b.analyses 34 | 35 | >>> b.surveyors 36 | 37 | 38 | >>> filter(lambda x: '_' not in x, dir(b.analyses)) 39 | ['BackwardSlice', 40 | 'BinDiff', 41 | 'BoyScout', 42 | 'BufferOverflowDetection', 43 | 'CDG', 44 | 'CFG', 45 | 'DDG', 46 | 'GirlScout', 47 | 'SleakMeta', 48 | 'Sleakslice', 49 | 'VFG', 50 | 'Veritesting', 51 | 'XSleak'] 52 | >>> filter(lambda x: '_' not in x, dir(b.surveyors)) 53 | ['Caller', 'Escaper', 'Executor', 'Explorer', 'Slicecutor', 'started'] 54 | ``` 55 | 56 | `analyses` and `surveyors` 是Project中装载了所有分析者(Analyses)或者探索者对象(Surveyors)的容器。 57 | 58 | 分析者(Analyses)是可以从程序中获取一些信息的自定义的分析行为。 59 | 最常见的两种是`CFG`和`VFG`,`CFG`组织控制流图,`VFG`进行值-集合的分析。在[这里](./analyses.md)查看它们的详细用法以及如何完成你自己的分析者。 60 | 61 | 探索者(surveyors)是进行符号执行的的基础工具。 62 | 最常见的是`Explorer`,它可以搜索目标地址,同时避开你指定的地址。 63 | 关于探索者的详细说明可以看[这里](./surveyors.md)。 64 | 但是已经有了用于取代探索者的路径群(Path Groups)工具。 65 | 66 | ## 工厂对象 67 | 68 | `b.factory`,像`b.analyses`和`b.surveyors`一样,是一个包含了很多有意思的东西的容器对象。它不是java概念中的工厂,它是所有能够产生重要angr类对象的函数的容器对象,是Project对象的一员。 69 | 70 | ```python 71 | >>> import claripy # used later 72 | 73 | >>> block = b.factory.block(addr=b.entry) 74 | >>> block = b.factory.block(addr=b.entry, insn_bytes='\xc3') 75 | >>> block = b.factory.block(addr=b.entry, num_inst=1) 76 | 77 | >>> state = b.factory.blank_state(addr=b.entry) 78 | >>> state = b.factory.entry_state(args=['./program', claripy.BVS('arg1', 20*8)]) 79 | >>> state = b.factory.call_state(0x1000, "hello", "world") 80 | >>> state = b.factory.full_init_state(args=['./program', claripy.BVS('arg1', 20*8)]) 81 | 82 | >>> path = b.factory.path() 83 | >>> path = b.factory.path(state) 84 | 85 | >>> group = b.factory.path_group() 86 | >>> group = b.factory.path_group(path) 87 | >>> group = b.factory.path_group([path, state]) 88 | 89 | >>> strlen_addr = b.loader.main_bin.plt['strlen'] 90 | >>> strlen = b.factory.callable(strlen_addr) 91 | >>> assert claripy.is_true(strlen("hello") == 5) 92 | 93 | >>> cc = b.factory.cc() 94 | ``` 95 | 96 | - *factory.block* 是angr的lifter。给它传递一个地址将会举出程序在那个地址的基本代码块,并且返回一个angr的Block对象,可以用于获取对这个block的多种描述。下面会有进一步描述。 97 | - *factory.blank_state* 返回一个除了应用传递给它的参数外,只进行了一些极少的初始化操作的SimState对象。关于程序的状态(state)在[这里](states.md)有进一步讨论。 98 | - *factory.entry_state* 返回一个初始化在程序入口点状态的SimState对象。 99 | - *factory.call_state* 返回一个初始化在使用给定的参数完成指定地址处的函数调用的SimState对象。 100 | - *factory.full_init_state* 返回一个相似于`entry_state`的SimState对象。但是和入口点不同的是,程序计数器(PC)指向一个服务于动态装载器的SimProcedure并且在跳到入口点之前会调用每一个共享库的初始化器。 101 | - *factory.path* 返回一个Path对象。既然Path只是对SimStates的轻量包装,你可以使用state对象作为一个参数调用`path`然后得到一个对这一state包装好的Path对象。在简单情况中,你传递给`path`的关键字参数将会传给`entry_state` 来创建一个用来包装的state对象。[这里](paths.md)有进一步讨论。 102 | - *factory.path_group* 创建一个路径群!根本上来说,路径群是Path的智能列表,所以你可以传给它Path对象,State对象或者是Path或State的list作为参数。[这里](pathgroups.md)有进一步讨论。 103 | - *factory.callable* 是非常酷的工具。Callables是可以调用任何二进制代码的FFI(foreign functions interface,远程函数接口)。[这里](structured_data.md)有进一步讨论。 104 | - *factory.cc* 初始化一个调用惯例对象。它可以使用不同的参数或者甚至函数原型来初始化,并且可以作为参数传递给factory.callable或者factory.call_state来自定义参数和返回值以及返回地址在内存中的布局。[这里](structured_data.md)有进一步讨论。 105 | 106 | ### Lifter 107 | 通过*factory.block*来获取lifter。 108 | 这个方法有大量的可选参数,你可以阅读这份[文档](http://angr.io/api-doc/angr.html#module-angr.lifter)。 109 | 最下面一行是`block()`,给了你一个与基本代码块的接口。 110 | 你可以从块中得到像`.size`一样的属性,但是如果你想做一些有意思的事情,你需要更特殊点的描述。 111 | 使用`.vex`来得到[PyVEX IRSB](http://angr.io/api-doc/pyvex.html#pyvex.block.IRSB)或者`.capstone`来得到一个[Capstone block](http://www.capstone-engine.org/lang_python.html)。 112 | 113 | ### 文件系统选项 114 | 有一些参数可以传递给“状态”初始化函数,这将会影响文件系统的使用。包括`fs`, `concrete_fs`和`chroot`参数。 115 | 116 | `fs`选项允许你传递一个文件名的字典作为参数来预先配置SimFile对象。这使得你可以做一些事情,比如设置一个文件大小的具体限制。 117 | 118 | 将`concrete_fs` 选项设置为`True`将会使angr谨慎使用磁盘上的文件。比如,当`concrete_fs`设置为`False`(默认值)时,如果模拟程序运行期间试图打开'banner.txt',一个有符号内存支持的SimFile对象将会被创建并且模拟运行将会继续,就好像这个文件真实存在一样。当`concrete_fs`模式设置为`True`时,如果'banner.txt'存在一个有具体支持的SimFile对象将会被创建,减少了可能因为操作完全符号文件而导致的结果状态的激增。同时在`concrete_fs`模式下如果'banner.txt'不存在,如果试图打开'banner.txt',SimFile对象不会被创建并且会返回一个错误码。但是,如果试图打开路径名以'/dev/'开头的文件,即便`concrete_fs`设置为`True`也不会成功打开具体的文件。 119 | 120 | `chroot`选项允许你指定一个可选的根目录来使用`concrete_fs`选项。如果你正分析的程序使用绝对路径引用了其他文件,那这个选项会使事情变得很方便。比如,如果你正分析的程序试图打开'/etc/passwd',你可以设置其chroot到你当前的工作路径,使得对'/etc/passwd'的访问将会读取'$CWD/etc/passwd'。 121 | 122 | 123 | ```python 124 | >>> import simuvex 125 | >>> files = {'/dev/stdin': simuvex.storage.file.SimFile("/dev/stdin", "r", size=30)} 126 | >>> s = b.factory.entry_state(fs=files, concrete_fs=True, chroot="angr-chroot/") 127 | ``` 128 | 129 | 这个例子将会创建一个限制了从stdin中最多读入30个符号字节的state,并且将会使所有对文件的引用都在新的root目录`angr-chroot`下被具体地解决。 130 | 131 | 在初始版本里值得注意的是: 132 | 在`entry_state` 和 `full_init_state`中起作用的`args` 和 `env`关键字参数分别是strings 或者 [claripy](./claripy.md) BV 对象的一个list和一个dict,它们可以代表一种具体和符号的字符。请阅读源码如果你想知道更多的话。 133 | 134 | 135 | ## 钩子 136 | 137 | ```python 138 | >>> def set_rax(state): 139 | ... state.regs.rax = 10 140 | 141 | >>> b.hook(0x10000, set_rax, length=5) 142 | >>> b.is_hooked(0x10000) 143 | True 144 | >>> b.unhook(0x10000) 145 | >>> b.hook_symbol('strlen', simuvex.SimProcedures['stubs']['ReturnUnconstrained']) 146 | ``` 147 | 148 | 钩子(Hook)可以对程序如何执行进行一些修改。 149 | 当你在确定的地址钩住了程序,那么不管程序在什么时候执行到这个地址,它都会执行你提供的python代码。 150 | 并且程序还会跳过`length`字节的原有指令再继续执行。 151 | 你可以不提供`length`参数来使程序从被钩住的地址继续执行而不跳过任何指令。 152 | 153 | 除了基础的函数,你可以使用一个`SimProcedure`对象来钩住地址,它是一个可以对程序执行进行更细粒度的控制的更复杂的系统。 154 | 提供`simuvex.SimProcedure`的子类(不是一个实例!)给相同的`hook`函数来实现这个功能。 155 | 156 | `is_hooked`和`unhook`方法应该不用多说了。 157 | 158 | `hook_symbol`是一个为目的不同的函数。你传递给它一个二进制文件中导入的函数名作为参数,而不是一个地址作为参数。 159 | 指向目标函数的内部指针(GOT)将会被指向SimProcedure或者你通过第三个参数指定的钩子函数替换。你也可以传递一个整形值来替换这个指针。 160 | --------------------------------------------------------------------------------