angr分析程序的控制流图CFG

angr分析程序的CFG

概述

在二进制上实现基本的分析一般是通过控制流图。一个CFG是一个基本块为节点,jump/ret/call为边的图。

在angr里,主要有两种CFG,一种是静态的CFG(CFGFast)和动态的CFG(CFGEmulated)

CFGFast使用静态分析来产生CFG。生成的速度很快,但是理论上有些控制流的转换只在执行的时候确定。这个和其他逆向工具差不多,结果也匹配输出。

CFGEmulated使用符号执行来去获取CFG。理论上更精准,但是比较慢。目前还不是完整的,因为模拟执行时的准确性不是很好。

可以用下面的代码来构建CFG:

>>> import angr
# load your project
>>> p = angr.Project('/bin/true', load_options={'auto_load_libs': False})

# Generate a static CFG
>>> cfg = p.analyses.CFGFast()

# generate a dynamic CFG
>>> cfg = p.analyses.CFGEmulated(keep_state=True)

使用CFG

CFG是基于NetworkX di-graph实现的。意味着NetworkX 的API都可以用。

>>> print("This is the graph:", cfg.graph)
>>> print("It has %d nodes and %d edges" % (len(cfg.graph.nodes()), len(cfg.graph.edges())))

CFG图的节点是CFGNode类的实例。由于上下文的敏感性,一个给定的基本块可以有多个节点在图中。

# this grabs *any* node at a given location:
>>> entry_node = cfg.get_any_node(p.entry)

# on the other hand, this grabs all of the nodes
>>> print("There were %d contexts for the entry block" % len(cfg.get_all_nodes(p.entry)))

# we can also look up predecessors and successors
>>> print("Predecessors of the entry point:", entry_node.predecessors)
>>> print("Successors of the entry point:", entry_node.successors)
>>> print("Successors (and type of jump) of the entry point:", [ jumpkind + " to " + str(node.addr) for node,jumpkind in cfg.get_successors_and_jumpkind(entry_node) ])

查看CFG

控制流图render是一个复杂的问题。angr不会去修改CFG分析的输出,而是用传统的图修改库,比如matplotlib。

要可视化CFG可以在这里查看:axt’s angr-utils repository

共享库

angr默认情况下会自动分析加载的共享库。这有可能导致分析需要好几天的时间。如果要加载一个没有共享库的二进制,需要在Project那边加个参数。

load_options={'auto_load_libs': False}

函数管理器

cfg提供一个对象叫Function Manager,这个对象可以通过cfg.kb.functions来访问。这个对象最常用的使用方法是通过字典来访问。它会映射地址到Function对象,也就能告诉你函数的属性。

>>> entry_func = cfg.kb.functions[p.entry]

函数有几个重要的属性:

  • entry_func.block_addrs:属于函数开始的基本块的地址
  • entry_func.blocks:属于这个函数基本块的集合。
  • entry_func.string_references:列出函数里引用的常量字符串
  • entry_func.returning:bool值,看函数是否有返回值
  • entry_func.callable:与函数相关的可调用对象
  • entry_func.transition_graph
  • entry_func.name:函数名
  • entry_func.has_unresolved_calls:用来检测CFG的不准确性
  • entry_func.has_unresolved_jumps:用来检测CFG的不准确性
  • entry_func.get_call_sites:返回一个列表,包含所有基本块的地址的结束是call的
  • entry_func.get_call_target(callsite_addr):给定callsite_addr,返回callsite的目标
  • entry_func.get_call_return(callsite_addr):给定callsite_addr,返回callsite要return回去的地方

上面都是文档的内容,实际上自己去用angr去构造控制流图,抽取信息,还需要看network X和angr的源码来获取相关的接口。

下面讲个具体的例子。就比如你要获取cfg里所有基本块的跳转的目的基本块地址。可以用下面的代码实现:

import angr
result = {}
proj = angr.Project("hello",load_options={"auto_load_libs":False})
cfg = proj.analyses.CFGFast()
#获取cfg里的所有节点,放在一个列表里,列表每个元素都是CFGNode对象
cfg_node_list = list(cfg.graph.nodes)
for cfg_node in cfg_node_list:
    src_addr = hex(cfg_node.addr)
    #初始化保存结果的字典
    result[src_addr]=[]
    #获取当前节点的后继及跳转属性,这个successors_and_jumpkinds()函数在angr源码的CFGNode.py有提及
    succ_list = cfg_node.successors_and_jumpkinds()
    for succ_item in succ_list:
        #将后继基本块的地址保存在字典里
        result[src_addr].append(hex(succ_item[0].addr))
print(result)

要可视化CFG图,可以用这个开源工具实现:https://github.com/axt/angr-utils

只需要一行代码就能生成cfg的png文件了。

plot_cfg(cfg, "ais3_cfg", asminst=True, remove_imports=True, remove_path_terminator=True)  

总结

angr文档虽然已经写得很详细了,但实际去应用angr写点什么东西(程序分析,逆向,利用等等),还需要去看他源码怎么写的,看他对象有哪些属性,方法可以调用。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

破落之实

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值