利用angr获取CFG

静态二进制分析中,对于程序控制流图CFG的计算是很基础且重要的一步,很多的分析是要建立在CFG的基础上。angr作为二进制分析工具,当然提供了CFG功能,下面我们就来探索下要如何使用angr计算CFG,以及其中的坑。

angr中的CFG分为2种:CFGFast和CFGAccurate。两者的区别在于前者计算的东西更少,从而也就更快。一般情况下CFGFast就够了,但在研究中若要依靠CFG进一步分析的话可能就需要CFGAccurate了,更精准当然也就更慢,需在两者间加以权衡。

我们就以一个简单的例子开始吧。
在这里插入图片描述

  1. 首先计算CFGFast

在这里插入图片描述
说明:angr中CFG()是CFGFast()的子类,也就是在CFGFast()基础上的一个包装。

这里我们通过angr-utils将cfg绘画出来,看起来更直观:

在这里插入图片描述

这里我们主要关注0x4005bc->0x4005ff和0x4005bc->0x4005ee两条边,也就是源码中从func()函数有2次返回至main()函数的边。由于计算CFGFast()时不会考虑函数调用的上下文关系(在angr中称为context_sensitivity_level),导致单纯从CFG来看我们发现从0x4005f5节点可以走到0x4005ff,从0x4005e4节点也可以走到0x4005ff,而源码表示的含义是若要走到0x4005ff节点,那就必须经过0x4005f5,而从0x4005e4节点只能走到0x4005ee。因此,CFGFast丢失了上下文调用关系,从而导致在后向切片时的不准确性,也就是说当要对0x4005ff进行切片时,0x4005f5和0x4005e4都会在切片结果内,而实际上只有0x4005f5在我们希望的切片范围中。所以,为了保留这种上下文调用关系,angr还提供了一个API – CFGAccurate()。

  1. 计算CFGAccurate

在这里插入图片描述

CFGAccurate()默认的context_sensitivity_level参数=1

在这里插入图片描述

在上图中我们可以看出,有2个相同的func()函数节点,分别从0x4005e4和0x4005f5两个节点可到达。也就是说它把两次与func()函数的调用关系单独计算,从而将这两条路径分离开,从而对于0x4005ff节点进行后向切片时,只有0x4005f5会在切片范围内,表示从0x4005f5->0x4005ff是可达的,而从0x4005e4出发则是不能到达目标节点的,符合实际情况。

因此总结来说,angr计算CFG的过程就是:模拟执行每个基本块,并判断该基本块下一步会走向哪个基本块,从而建立cfg的边关系。然而存在一些困难:一个基本块在不同的上下文中会有不同的表现形式。举例来说,一个函数返回时的基本块(即上面的func()函数),对于不同的调用者而言该基本块的下一跳的地址是不一样的。因此上下文调用关系的保留情况对于CFG构建至关重要。angr中即是通过context_sensitivity_level参数来确定在调用栈中保留多少个函数,用一个官网的例子来解释下这个参数的概念吧:

在这里插入图片描述

这里对于puts函数而言,有4个调用链:main->alpha->puts,main->alpha->error->puts,main->beta->puts,main->beta->error->puts。在这种情况下,angr可以对每条调用链都分别处理,但在实际程序中往往不可行,因此angr提供context_sensitivity_level参数用于设置在调用栈中保存的函数调用者caller的个数。举例说明,对于puts函数而言:

在这里插入图片描述

context_sensitivity_level=1:当从alpha调用puts时,puts函数便会返回到alpha;当从beta调用puts时,puts便会返回到beta;对应上面的例子,当从main1调用func时会返回到main1,从main2处调用func的话会返回到main2。

context_sensitivity_level=0:CFG仅仅表示当从puts函数返回时,会返回到alpha, beta, error这三个函数;对应上面的例子,不管从哪里调用func函数最终返回时会到任何调用func函数的下一基本块,即同时返回到main1和main2。

上面就是context_sensitivity_level的概念和应用场景分析,当然该值越大计算的CFG越准确,但这也会指数级地增加分析的时间,到底是要速度还是要精度就要具体情况具体分析了。

原博客地址:https://joyceqiqi.wordpress.com/2017/04/25/利用angr获取cfg/

### 使用 angr 进行控制流图分析 angr 是一种用于二进制程序分析的强大框架,其核心功能之一就是能够生成和操作控制流图 (Control Flow Graph, CFG)[^1]。以下是关于如何使用 angr 来构建和利用控制流图的相关说明。 #### 安装 angr 在开始之前,需确保已安装 angr 库。可以通过以下命令完成安装: ```bash pip install angr ``` #### 构建基本的控制流图 通过 `angr.Project` 创建项目对象后,可以调用 `project.analyses.CFG()` 方法来生成目标二进制文件的控制流图。下面是一个简单的例子: ```python import angr # 加载二进制文件 binary_path = "./your_binary_file" proj = angr.Project(binary_path, load_options={'auto_load_libs': False}) # 生成控制流图 cfg = proj.analyses.CFG() # 打印所有节点的信息 for node in cfg.graph.nodes(): print(f"Address: {hex(node.addr)}, Size: {node.size}") ``` 上述代码加载了一个指定路径下的可执行文件并禁用了自动加载库的功能[^2]。随后创建了该二进制文件的 CFG 对象,并迭代打印出了其中每个节点的地址及其大小信息。 #### 高级选项配置 为了更精确地生成 CFG 或者提高性能,在初始化 `CFGFast` 分析器时还可以传递额外参数。例如设置内存优化模式或者启用符号执行支持等特性: ```python cfg_fast = proj.analyses.CFGFast( data_references=True, # 启用数据引用解析 normalize=True # 尝试规范化函数结构 ) print(cfg_fast.functions) ``` 这段脚本展示了如何通过开启某些标志位来自定义化我们的 CFG 分析过程[^3]。这里启用了对数据引用的支持以及尝试标准化各个子程序内部逻辑的能力。 #### 可视化控制流图 除了直接处理抽象表示外,我们也可以借助外部工具将这些关系可视化出来。Angr 提供了一种简单的方法导出 dot 文件格式以便于后续绘图软件读取渲染成图形界面形式展示给用户查看理解整个应用程序流程走向情况。 ```python dot_code = cfg.graph_to_dot() with open('cfg.dot', 'w') as f: f.write(dot_code) ``` 此部分演示了怎样把先前建立起来的那个复杂网络转换成为适合Graphviz使用的DOT描述语言版本保存至本地磁盘上等待进一步加工美化呈现效果[^4]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值