本文系原创,转载请说明出处

Please Subscribe Wechat Official Account:信安科研人,获取更多的原创安全资讯

上一篇文章介绍了angr的原理,自此篇文章开始,将从一个个小实验的角度,讲述队angr的一些用法。

第二篇从(静态)程序分析必备的基础元素的角度出发,介绍一些图的生成与应用。

一 CFG(控制流图)与CG(调用图)


1.1 CFG控制流图:

概念:控制流图(control-flow graph)简称CFG,是计算机科学中的表示法,利用数学中图的表示方式,标示计算机程序执行过程中所经过的所有路径。控制流图是由法兰·艾伦所建立,他提出Reese T. Prosser(英语:Reese Prosser)曾利用邻接矩阵用在流分析上。

性质:

​特征:

  • 面向过程
  • 显示程序执行期间可以遍历的所有路径
  • 有向图

优点:

        可以轻松封装每个基本块的信息

可以轻松找到程序中无法访问的代码,并且在控制流图中很容易找到循环等语法结构

缺点:

只能表示控制依赖关系,数据依赖关系表示能力较弱

使用angr生成CFG的示例代码:

在第一篇文章中提到angr实现生成CFG的几种算法,方法按结果可以分为:CFGFast(), CFGEmulated。

使用流程一般为:

  1. 加载文件
  2. 调用图方法               

angr调用方法:

import angr
from angrutils import * p = angr.Project(程序路径) #使用快速生成方法生成CFG
cfg = p.analyses.CFGFast() #使用完整生成方法生成CFG
cfg1 = p.analyses.CFGEmulated() #调用angr-utils库可视化
plot_cfg(cfg1, "生成的cfg文件名", asminst=True, remove_imports=True, remove_path_terminator=True)

示例:

​编辑

编辑

1.2 CG 调用图

概念:一种有向图,表示计算机程序中调用和调用子例程之间的关系,用于代码分析。每个节点表示一个过程,每个边 (f, g) 表示过程 f 调用过程 g

特点:

两种CG,一种是动态,一种是静态

静态:静态调用图是用于表示程序的每个可能运行的调用图。确切的静态调用图是一个不可判定的问题,因此静态调用图算法通常是过度的。也就是说,发生的每个调用关系都表示在图中,并且可能还有一些在程序的实际运行中永远不会发生的调用关系。

动态:动态调用图只描述程序的一次运行

代码示例:

angr里比较常见的是函数的CG,需要注意区分概念

p = angr.Project(文件路径)
cfg = p.analyses.CFGFast()
cg = cfg.functions.callgraph
#cg即是函数调用图

​编辑

二 CDG和DDG (依赖图类,后续更新PDG、CPG)


依赖图一般是在CFG的基础上,按照特定的分析需求,构建特定的依赖图。常见的依赖图类包含CDG、DDG、CPG、PDG。angr按照自带的后向切片方法,在CFG上构建了生成CDG和DDG的方法,另外两种需要重构。

CDG(控制流依赖图)

概念:

人话定义:对于CFG中的两个节点X和Y,如果Y受X控制,即如果在程序执行过程中,X能直接影响Y是否执行

规范定义:

  • 对于一个有向图 G = < N , E >,节点n控制依赖于节点m,当且仅当:
  • 存在一条m到n的控制路径,n是该路径上除m之外每个节点的后支配节点,并且n不是m的后支配节点。

本文参考这边博文,即CDG由CFG和FDT(前向支配树构成),d支配(dominate)n,记为d dom n:每一条从流图的入口结点结点n的路径都经过结点d 。在这个定义下每个结点都支配它自己
如下图所示,左侧为流图,右侧为其对应的支配树

编辑

简单的来说如何划分,即如果现在这个节点只有一条入边,那么逆着入边往上看第一个节点直接控制现在这个节点,如2和3;如果现在这个节点有多条入边,逆着入边往上看,以一种合并的方式,找到这些入边往上合并的第一个节点,就是直接控制现在这个节点的节点。

又如如下例子:第一张图是程序示例和对应的CFG,第二张图是CDG和变量X的DDG

​编辑

​编辑

应用:

死代码删除(DCE,Dead Code Elimination)和激进死代码删除(ADCE,Aggressive Dead Code Elimination)是编译中常见的优化pass。相较于DCE,ADCE会删除冗余的分支。

示例代码

import angr

b = angr.Project(文件路径)

cfg = b.analyses.CFGEmulated(keep_state=True,
state_add_options=angr.sim_options.refs,
context_sensitivity_level=2) # 生成控制流依赖图
cdg = b.analyses.CDG(cfg)

DDG (数据依赖图)

定义:

  • 两个句子存在数据依赖:一条语句中一个变量的定义,可以到达另一条语句中对该变量的使用
  • 在编译领域有不同类型的数据依赖,如果s2依赖于s1,可以是:
    1. s1 写内存 s2 读 (RAW)
    2. s1 读内存 s2 写 (WAR)
    3. s1 写内存 s2 写 (WAW)
    4. s1 读内存 s2 读 (RAR)
  • 如果两个语句可能引用相同的内存位置和引用之一,则它们是数据相关的

代码示例:

import angr

b = angr.Project(文件路径)

cfg = b.analyses.CFGEmulated(keep_state=True,
state_add_options=angr.sim_options.refs,
context_sensitivity_level=2) # 生成数据流依赖图
ddg = b.analyses.DDG(cfg)

ACFG 属性控制流图


在很多代码相似性检测的工作中,常常需要二进制程序的结构和语义信息,ACFG诞生于二进制程序漏洞检测的工作GENIUS中,其定义了ACFG属性控制流图,以获取二进制程序的基本块的内部特征,以及外部的结构特征,从而将其嵌入向量空间,使用机器学习按照代码相似性的技术,进行漏洞检测。

Scalable Graph-based Bug Search for Firmware Images | Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security

定义:

一个有向图G = <V,E,α>,V为基本块集合,E是边集合,α代表基本块的特征标签集合,一般定义特征集合如下(参考此博文

​编辑

最后形成的ACFG如下:

编辑

代码示例:

首先导入库函数

import angr
import argparse
import os
import json
from angrutils import *

接着,从基本块中获取指令的统计特征,指令的种类需要手工枚举,数量有限,问题不大:

def calc_ins(insns):
"""
统计基本块中指令类型的数量
参数insns为angr调用cfg中的基本块
"""
transfer_ins = ['MOV', 'PUSH', 'POP', 'XCHG', 'IN', 'OUT', 'XLAT', 'LEA', 'LDS', 'LES', 'LAHF', 'SAHF', 'PUSHF','POPF'] arithmetic_ins = ['ADD', 'SUB', 'MUL', 'DIV', 'XOR', 'INC', 'DEC', 'IMUL', 'IDIV','OR', 'NOT', 'SLL', 'SRL'] calls_ins = ['CALL'] num_transfer = 0
num_arithmetic = 0
num_calls = 0 for ins in insns:
ins_name = ins.insn_name() #angr基本块中的block.capstone.insns.insn_name()方法 if ins_name in transfer_ins:
num_transfer = num_transfer + 1 if ins_name in arithmetic_ins:
num_arithmetic = num_arithmetic + 1 if ins_name in calls_ins:
num_calls = num_calls + 1 return num_transfer, num_calls, num_arithmetic

然后,获取基本块的其他特征,如字符串常量的个数、数值常量的个数、指令总数:

def calc_block(block, num_str):
"""
统计每个基本的特征
"""
# 字符串常量个数初始化,通过传入外部的angr字符串个数计数方法
num_string = num_str # 数字常量的个数
num_numeric = len(block.vex.constants) # 指令的总数
num_instructions = block.instructions # 指令集区分并计数
num_transfer, num_calls, num_arithmetic = handle_ins(block.capstone.insns) # 基本块子节点个数
num_offspring = 0 return [num_string, num_numeric, num_transfer, num_calls, num_instructions, num_arithmetic, num_offspring]

接着,在定义好统计基本块内部的特征后,以一个函数为单位,统计基本块的外部结构特征,如连接的节点个数,子节点个数,使用邻接矩阵记录这些节点关系:

def calc_function(function, func_addr):
"""
提取每个函数的特征
""" function_feature = dict()
function_feature["func_addr"] = func_addr #函数的位置
function_feature["function_name"] = function.name #函数名
function_feature["features"] = [] #函数内基本块的总特征
function_feature["adj"] = [] #函数内的基本块结构 try:
# 函数中字符串常量的个数
f_num_string = len(function.string_references())
block_cnt = 0 # 提取函数内每个基本块的属性并统计基本块的个数
for blk in function.blocks:
function_feature["features"].append(calc_block(blk, f_num_string))
block_cnt = block_cnt + 1
function_feature["block"] = block_cnt # CFG图的节点数目为0时直接返回
if 0 == len(function.graph):
return # 将函数中的基本块结构图转为邻接矩阵
matrix = nx.adjacency_matrix(function.graph).todense().tolist() for i, line in enumerate(matrix):
# 当前节点到自己无边
line[i] = 0
num_offspring = line.count(1) #计算子节点个数 function_feature["features"][i][-1] = num_offspring
function_feature["adj"].append(line) print("*************************************")
print(function_feature) except Exception as e:
print("Exception->", e)

这里比较难理解的操作是邻接矩阵这块,把它打印出来看会更容易理解:

编辑

上左图是一个函数的具体信息和邻接矩阵(最后一行),右图是可视化CFG后该函数对应的部分,可以看到0x4005a0基本块节点到本身无边,因此为0,到第二个0x4005a0和第三个0x4005b0都有边,所以,最后为[0,1,1];第二个[0,0,0]按顺序为0x4005b2基本块,可以看到,其到其他两个都没有指向边,因此全为0;最后一个同理。

所以,对于每个line,如下图,就是一个基本块的节点连接情况,计算line.count(1)就是计算子节点的个数

编辑

最后,对每个文件进行方法调用即可:

​编辑

angr原理与实践(二)—— 各类图的生成(CFG CG ACFG DDG等)的更多相关文章

  1. WebSocket原理与实践(二)---WebSocket协议

    WebSocket原理与实践(二)---WebSocket协议 WebSocket协议是为了解决web即时应用中服务器与客户端浏览器全双工通信问题而设计的.协议定义ws和wss协议,分别为普通请求和基 ...

  2. angr原理与实践(一)——原理

    ​ 1本文系原创,转载请说明出处 关注微信公众号 信安科研人,获取更多的原创安全资讯 ​ 编辑 网上已经有很多介绍angr的官方文档的博客,但是怎么去用angr做一次有意义且成就感满满的分析的教程很少 ...

  3. [从Paxos到ZooKeeper][分布式一致性原理与实践]<二>一致性协议[Paxos算法]

    Overview 在<一>有介绍到,一个分布式系统的架构设计,往往会在系统的可用性和数据一致性之间进行反复的权衡,于是产生了一系列的一致性协议. 为解决分布式一致性问题,在长期的探索过程中 ...

  4. RPC原理与实践(二)----Thrift分层模型

    这一节我们从一下几个方面来讲一下Thrift的分层架构,按照官方的定义这是Thrift的网络栈,其中网络栈中分为一下几个部分,(由栈顶到栈底)server,processor,protocol,tra ...

  5. 2018-2019 2 20165203 《网络对抗技术》 Exp3 免杀原理与实践

    2018-2019 2 20165203 <网络对抗技术> Exp3 免杀原理与实践 免杀原理与实践说明及基础问答部分 实验任务 正确使用msf编码器(0.5分),msfvenom生成如j ...

  6. 20155218《网络对抗》Exp3 免杀原理与实践

    20155218<网络对抗>Exp3 免杀原理与实践 一.使用msf生成后门程序的检测 (1)将上周msf生成的后门文件放在virscan.org中进行扫描,截图如下: (2)使用msf时 ...

  7. 2017-2018 Exp3 MAL_免杀原理与实践 20155214

    目录 Exp3 MAL_免杀原理与实践 实验内容 对msf生成后门程序的检测 Veil-Evasion应用 Visual Studio2017 + shellcode生成后门 主要思路 知识点 最后的 ...

  8. kafka原理和实践(二)spring-kafka简单实践

    系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...

  9. 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解

    深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...

随机推荐

  1. jdbc连接数据库问题

    ### Error querying database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Fail ...

  2. RPA应用场景-报税机器人

    场景概述 报税机器人 所涉系统名称 税务网站 人工操作(时间/次) 53分钟 所涉人工数量 60 操作频率 每月 场景流程 1.通过RPA自动将财税信息从对应系统中导出 2.RPA根据不同的税务报表规 ...

  3. resultMap自定义映射(一对多)

    collection:处理一对多和多对多的关系 1) POJO中的属性可能会是一个集合对象,我们可以使用联合查询,并以级联属性的方式封装对象.使用collection标签定义对象的封装规则 publi ...

  4. Java8 函数式【1】:一文读懂逆变

    Java8 函数式[1]:一文读懂逆变 禁止转载 pure function 协变 逆变 Java8 引入了函数式接口,从此方法传参可以传递函数了,有人说: 不就是传一个方法吗,语法糖! lambda ...

  5. 从零开始制作【立体键盘】,画UI免写CSS,【盲打练习】的交互逻辑只用了10来行表达式!

    手把手教你从空白页面开始通过拖拉拽可视化的方式制作[立体键盘]的静态页面,不用手写一行CSS代码,全程只用10来行表达式就完成了[盲打练习]的交互逻辑. 整个过程在众触应用平台进行,快速直观. 最终U ...

  6. Redis基础课程讲义

    Redis基础 课程内容 Redis入门 Redis数据类型 Redis常用命令 在Java中操作Redis 1. 前言 1.1 什么是Redis Redis是一个基于内存的key-value结构数据 ...

  7. N皇后的位运算有感

    N皇后很明显是一个NP-Hard问题,如果n足够大的话,在有限较短的时间内是很难得出答案的,但是注意到N皇后(笔者认为这类问题称为棋盘问题更为贴切),在n*n棋盘之上,每个点有且只有两种状态,这与电脑 ...

  8. 用VS Code搞Qt6:编译源代码与基本配置

    先说明一下,本水文老周仅讨论新版的 Qt 6,旧版的 Qt 不讨论. 尽管 Qt 有自己的开发环境,但老周必须说句不装逼的话:真的不好用.说起写代码,当然了,用记事本也能写.但是,有个高逼格的工具,写 ...

  9. Collection集合和Collection的常用功能

    boolean add(E e); 向集合里添加元素 boolean remove(E e); 删除集合中的某个元素 void clear(); 清空集合的所有元素 boolean contains( ...

  10. Java开发学习(十八)----AOP通知获取数据(参数、返回值、异常)

    前面的博客我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从获取参数.获取返回值和获取异常三个方面来研究切入点的相关信息. 前面我们介绍通知类型的时候总共讲 ...