『工具使用』二进制分析框架 angr

admin 2022年3月29日23:12:52评论422 views字数 7549阅读25分9秒阅读模式

点击蓝字 关注我们



日期: 2022-03-14

作者: Mr-hello

介绍: angr,一款可以自动化分析二进制文件的框架。


0x00 前言

之前一直想学习 angr 这个框架,但是由于自己的懒惰以及各种事情缠身,直到这次准备公众号文章,才不得不逼迫自己去接触这方面的知识。其实在很久之前,自己就知道这个框架,当时在某一次的比赛培训中,听清华大佬第一次提到这个东西,当时听得模模糊糊只是粗浅的认识了一下这个框架,一直没有看相关方面的文章。后来就是听各位大佬们在用这个框架做一些自动化 pwn 的尝试。再到后来的陇剑杯 RHG 人工智能赛道等等。

『工具使用』二进制分析框架 angr

本篇文章,只是自己在学习这个框架的时候,随手记录的一点笔记,稍加润色就发了出来,内容浅显,勿怪勿怪。

0x01 符号执行

其实关于符号执行,网上有很多官方的解释,符号执行是指在不执行实际程序的前提下,把源程序翻译为一种中间语言,用符号值表示程序变量的值,然后基于中间语言模拟程序执行来进行相关分析的技术,它可以分析代码的所有语义信息,也可以只分析部分语义信息。(各位懂了吗?抱歉作者是没懂。)

『工具使用』二进制分析框架 angr

不过话说回来,后面的解释我倒是能理解一点点,程序的路径(path)是程序的一个语句序列,这个语句序列包括程序的一些顺序的代码片段,代码片段之间的连接是由于分支语句导致的控制转移。一个路径是可行的(feasible),是指存在程序输入变量的至少一组值,如果以这组值作为输入,程序将沿着这条路径执行。否则,路径就是不可行的(infeasible)。路径条件(path condition,PC)是针对一个路径的,它是一个关于程序输入变量的符号值的约束,一组输入值使得程序沿着这条路径执行当且仅当这组输入值满足这条路径的路径条件。

『工具使用』二进制分析框架 angr

通俗一点,angr 是帮助我们自动对二进制文件进行分析,遍历探索所有可能分支,然后用户通过添加变量约束,最终得到一条正确路径,并将结果反馈给用户。等一下,怎么感觉有点熟悉,正确路径,探索分支,这不是走迷宫嘛?在不断试错中前进?你可以这么理解吧,其实这种试错中前进的说法并不是很准确,因为有些时候我们为了节约框架分析时间,还是加了人工干预,在添加约束条件时可以直接踢除部分错误路径。

angr 所利用的符号执行操作,在理论上是存在路径爆炸问题的,毕竟在前文中提到符号执行技术会去探索程序中的每一个分支条件语句,每次遇到条件语句,可能都会给当前路径再分出一条新的路径,这个增长速度是“指数级”的,所以该框架需要人工干预,通过修改不同的路径遍历策略,以达到在可接受的时间范围以及空间范围内尽可能多的完成代码检测。但是这些策略都只是部分改进,无法从根本上解决这一问题。

0x02 关于安装

在安装 angr 的过程中,还是踩了一些坑,网上有很多版本的安装介绍, angr 对 Linux 系统兼容最好,也流传着 Mac os 以及 Windows 系统的安装方式。其他系统没试过,回头可以试试 Mac 上安装,但是 angr 模块会对 python 中已安装的 libz3 、 libVEX 模块产生修改,为了防止以后用到上述两个模块时出现另外的问题,这里使用 linux 中的 python 虚拟环境进行安装。

# 首先安装依赖环境sudo apt-get install python-dev libffi-dev build-essential    # python3-dev也可# 安装python虚拟环境
sudo pip install virtualenvwrapper# 安装地点默认在/usr/local/bin/
# virtualenvwrapper初始化
mkdir $HOME/.Env # 创建虚拟环境根目录
vim ~/.bashrc # 配置环境变量
# 将以下内容添加到最后,如果出现找不到文件提示,可使用whereis命令去查找
export WORKON_HOME=$HOME/Envsexport VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3export VIRTUALENVWRAPPER_VIRTUALENV=/usr/bin/virtualenvsource /usr/share/virtualenvwrapper/virtualenvwrapper.shsource ~/.bashrc # 重新加载
# virtualenvwrapper操作命令
mkvirtualenv env1 # 创建环境
workon env1 # 切换环境
workon # 列出已有环境
deactivate # 退出环境
rmvirtualenv # 删除环境
# 在virtualenvwrapper中安装angrsudo pip3 install angr # python2 or python3

0x03 使用方法

顶层接口

Project

使用 angr 进行自动化分析时,第一件事是如何加载二进制文件,在 angr 中,基本上所有的对象操作都依赖于已有的 Project

import angrproj = angr.Project('test')

在载入二进制文件之后,我们可以访问一些基本属性,文件名、架构、入口地址等。

proj.arch    # 架构
proj.entry # 入口地址
proj.filename # 文件名

loader

该接口用于对加载进来的二进制文件进行虚拟地址空间的一些操作,可以通过该接口分析二进制文件的链接库、地址空间、堆栈情况等。

import angrproj = angr.Project('test')proj.loader.shared_objects    # 链接库信息
proj.loader.min_addr # 开始地址
proj.loader.max_addr # 结束地址
proj.loader.main_objectproj.loader.main_object.execstack # 堆栈是否能执行

factory

该接口是最常用的,用来实例化一个对象, angr 大部分的分析模块均在该接口下。下面我们基于该接口下介绍一些 angr 基本概念。

1.blocks

angr 进行代码分析的基本单位,该单位内的代码是一个直线代码序列,不存在分支。proj.factory.block( ) ,它用来从给定的地址提取一个基本的代码块,你将得到一个 Block 对象,在此基础上操作。

block = proj.factory.block(addr)block.pp()    # 生成block的汇编代码展示
block.instructions # 该block对象的汇编代码数量
block.instruction_addrs # 该block对象的每条汇编代码的地址

2.states

使用 Project 创建的只是初始对象,简单理解为静态对象,需要使用 state 属性实例化一个动态对象(SimState)。一个 SimState 包含该对象某时刻的内存,寄存器,文件系统数据等任何会在执行中改变的“实时数据”都会在这个 state 中。

state = proj.factory.entry_state()
state.regs.rip    # rip数值
state.regs.rax    # rax数值
state.regs.rbx    # rbx数值
state.mem[addr].int.resolved    # 某地址上的内存值,转化成 C 中的int类型
3.Simulation Managers(模拟化管理器)
如果一个 state 表示处在某个时间点的程序,那么一定会有方法使它达到下一个时间点。 Simulation Manager 就是用于进行执行程序。
simgr = proj.factory.simulation_manager(state)# 等价于 simgr = proj.factory.simgr(state)
simgr.active # 显示当前有几个分支
simgr.step() # 向前进行一步(前进一个block)

核心模块

loader模块

通过 loader 模块还可以查看 PLT 表信息、预链接基地址和实际装载的内存基地址等信息。

obj = proj.loader.main_object
obj.plt # 打印PLT表信息
obj.linked_base # 预链接基地址
obj.mapped_base # 实际装载地址
proj.loader.find_symbol('malloc') # 直接输出malloc函数地址
malloc = proj.loader.main_object.get_symbol('malloc')# 返回一个symbol对象,后续获取符号名/所属者/链接地址/相对地址等。

Hooking

angr 为工程提供了 hook 机制,可以通过 proj.hook(addr,hook) 设置,还可以通过 proj.hook_symbol(name,hook) 设置。

访问内存

可以通过 state.mem[index] 访问内存,但对于一段连续内存的操作十分不方便,我们也可以使用 state.memory 的 load(addr, size) / .store(addr, val) 接口读写内存, size 以 bytes 为单位。

state = proj.factory.blank_state()
state.memory.load(0x401000, 6)# 读取内存
state.memory.store(0x401000, s.solver.BVV(0x0123456789abcdef, 64))# 写入内存
# 参数 endness 用于设置端序。state.memory.load(0x401000, 4, endness=archinfo.Endness.LE)# LE–小端序 BE–大端序 ME–中间序

explore

通过调用 explore 方法,我们可以探索执行路径,在进行 explore 时,可以设置 find 和 avoid 参数,以便找到符合我们预期的路径。通过 simgr.use_technique(tech)设定探索时不同的策略。

simgr = proj.factory.simgr()    # 建立模式化管理器simgr.explore(find=lambda s: b"Congrats" in s.posix.dumps(1))    # 寻找存在 Congrats 字段的分支s = simgr.found[0]flag = s.posix.dumps(0)print(flag)

solver

通过 state.solver 访问求解引擎,angr 使用位向量进行求解约束。位向量是比特序列,既可以表示具体值,也可以是符号变量。通过 BVV(value,size) 和 BVS(name, size) 接口创建位向量,也可以用 FPV 和 FPS 来创建浮点值和符号。

>>> x = state.solver.BVV(1,64)<BV64 0x1>>>> y = state.solver.BVV(110, 64)<BV64 0x6e> >>> z = state.solver.BVV(10,27)<BV27 0xa>

约束求解

我们可以通过 .add 对 state 对象添加约束,并使用 .eval 接口求解,得到符号变量的可行解。

>>> state.solver.add(x > y)>>> state.solver.add(y > 2)>>> state.solver.add(10 > x)>>> state.solver.eval(x)

我们可以根据输出和限制得到输入值,这个类似之前写过的关于 z3 那篇文章,其中的约束求解类似,举个例子:

state = proj.factory.entry_state()input = state.solver.BVS('input', 64)op = (((input + 4) * 8) >> 3) + inputout = 220state.solver.add(op == out)state.solver.eval(input)

0x04 代码演示

Hook

前文中提到,我们可以在 angr 中使用 hook 来把指定地址的二进制代码替换为 python 代码。angr 在模拟执行程序时,执行每一条指令前会检测该地址处是否已经被 hook ,如果是就不执行这条语句,转而执行 hook 时指定的 python 处理代码。

import angrdef hook_fun(state):    state.regs.edx = 0x89abcdef    state.regs.ecx = 12proj = angr.Project('add')proj.hook(addr=0x080491CA, hook=hook_fun, length=2)state = proj.factory.entry_state()sim = proj.factory.simgr(state)sim.explore(find=0x080491D0)st = sim.found[0]print (hex(st.solver.eval(st.regs.edx)))

解释一下上述代码,首先我们使用 angr.Project 载入文件,然后 使用 proj.hook 把 0x080491CA 处的 2 字节的指令 为 hook_fun,之后执行 0x080491CA 就会去执行 hook_fun。然后开始探索路径,为了做示范,然后 设置 edx 为0x89abcdef, 因为后续不会用到 edx , 修改它可以在路径探索完后查看这个值是否符合预期。

『工具使用』二进制分析框架 angr

IDA 汇编代码如上,这里用的 add 文件是自己编译的一个简单的两数相加,并输出问题进行简单演示。源代码如下:
//gcc -m32 -z execstack -z norelro -fno-stack-protector -no-pie -o add add.c#include <stdio.h>int main(){    int x = 8;    int y = 9;    printf("num is %dn",x+y);    return 0;}

通过上述 hook 代码,可以发现在程序探索完所有路径之后,edx 寄存器中的值更改为 0x89abcdef

『工具使用』二进制分析框架 angr

explore

调用 explore 方法,我们可以探索执行路径,先看写的 demo 程序。以及其 IDA 分析情况。

//gcc -m32 -z execstack -z norelro -fno-stack-protector -no-pie -o test test.c#include <stdio.h>#include <string.h>#include <unistd.h>
char *sneaky = "YOUCANNOTGUESS";int authenticate(char *username, char *password){ char stored_pw[9]; stored_pw[8] = 0; int pwfile; if (strcmp(password, sneaky) == 0) return 1; return 0;}int accepted(){ printf("Welcome to the admin console!n");}int rejected(){ printf("Go away!"); exit(1);}int main(int argc, char **argv){ char username[9]; char password[15]; int authed = 0; username[8] = 0; password[14] = 0; printf("Username: n"); read(0, username, 8); printf("Password: n"); read(0, password, 14); authed = authenticate(username, password); if (authed) accepted(); else rejected();}


『工具使用』二进制分析框架 angr

可以发现,0x08049352 为正确分支的地址,我们可以使用 explore 进行路径探索,代码如下:

import angrproj = angr.Project('test')state = proj.factory.entry_state()po = proj.factory.simgr(state)po.explore(find=0x08049352)st = po.found[0]print (str(st.posix.dumps(0), encoding="utf-8"))

同样,我们可以基于分支去进行探索,从而产生了另外一种解法。所用到的代码如下:

#!/usr/bin/env python#_*_ coding:utf-8 _*_import angr
def basic_exec(): p = angr.Project('test') state = p.factory.entry_state() sim = p.factory.simulation_manager(state) sim.step(until=lambda lpg: len(lpg.active) > 1)#接下来就让sim对象一直执行下去,直到执行到可选择的路径个数大于一个,即产生选择分支的时候,再停止。
#对应在上述的简单程序中authenticate函数的 if (strcmp(password, sneaky) == 0)这个条件判断语句 input_0 = sim.active[0].posix.dumps(0) #dump出所有分支的内容,看看哪个答案应该是最可能的 return input_0
if __name__ == '__main__': print (str(basic_exec(), encoding="utf-8"))

两份代码会产生同一种结果。

『工具使用』二进制分析框架 angr

0x05 结语

本篇文章有点草率,但是通过整理这篇文章也算是进去看了看 angr 相关的知识。也摸清了部分语法含义,后期当然还有更深层次的相关知识需要学习。有时间去复现一下之前在 CTF 比赛中遇到的可以用这个框架进行解题的情况。


免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

『工具使用』二进制分析框架 angr

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

原文始发于微信公众号(鸿鹄实验室):『工具使用』二进制分析框架 angr

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月29日23:12:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   『工具使用』二进制分析框架 angrhttp://cn-sec.com/archives/852269.html

发表评论

匿名网友 填写信息