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

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

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

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

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


1.1 CFG控制流图:

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

性质:

  • 节点基本块,内含程序语句;基本块概念基本块 - 维基百科,自由的百科全书 (wikipedia.org)
  • 边代表控制流,即如何执行

​特征:

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

优点:

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

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

缺点:

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

使用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 angrb = 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 angrb = 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 = 0num_arithmetic = 0num_calls = 0for ins in insns:ins_name = ins.insn_name()   #angr基本块中的block.capstone.insns.insn_name()方法if ins_name in transfer_ins:num_transfer = num_transfer + 1if ins_name in arithmetic_ins:num_arithmetic = num_arithmetic + 1if ins_name in calls_ins:num_calls = num_calls + 1return 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 = 0return [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 + 1function_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] = 0num_offspring = line.count(1) #计算子节点个数function_feature["features"][i][-1] = num_offspringfunction_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. Linux 快照 (snapshot) 原理与实践(二) 快照功能实践

    文章目录 0. 概要 1. 准备演示数据 2. 创建 snapshot-origin 目标 3. 创建 snapshot 目标 4. 验证 COW 操作 4.1 第一次写数据 4.2 第二次写数据 5 ...

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

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

  3. angr原理与实践(三)——Arbiter:一种弥合二进制程序漏洞发现中的静态和动态鸿沟

    转载请说明出处:信安科研人 please subscribe my official wechat :信安科研人 获取更多安全资讯 原文链接:sec22-vadayath.pdf (usenix.or ...

  4. #20155235 《网络攻防》 实验二 后门原理与实践

    20155235 <网络攻防> 实验二 后门原理与实践 实验目的 建立一个后门连接是如此的简单,功能又如此强大.通过亲手实践并了解这一事实,从而提高自己的安全意识 . 实验内容 (1)使用 ...

  5. 图模型在信息流推荐系统中的原理和实践

    猜你喜欢 0.飞猪信息流内容推荐探索1.如何搭建一套个性化推荐系统?2.从零开始搭建创业公司后台技术栈3.全民K歌推荐系统算法.架构及后台实现4.微博推荐算法实践与机器学习平台演进5.腾讯PCG推荐系 ...

  6. 2017-2018-2 20155228 《网络对抗技术》 实验二:后门原理与实践

    2017-2018-2 20155228 <网络对抗技术> 实验二:后门原理与实践 实验内容 使用netcat获取主机操作Shell,cron启动 (0.5分) 使用socat获取主机操作 ...

  7. Sizzle引擎--原理与实践(二)

    主要流程与正则 表达式分块 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['& ...

  8. 西瓜视频稳定性治理体系建设二:Raphael 原理及实践

    摘要 Raphael [1]是西瓜视频基础技术团队开发的一款 native 内存泄漏检测工具,广泛用于字节跳动旗下各大 App 的 native 内存泄漏治理,收益显著.工具现已开源,本文将通过原理. ...

  9. 【RPA】机器人流程自动化(RPA)概念、原理与实践

    多数人每天都会使用到一些机器人流程自动化工具,例如读取邮件和系统,计算,生成文件和报告.而在未来,那些你不想做的枯燥的工作,也许真的可以不做了,重复化.标准化的工作都可以让机器人帮你完成.本期推文特邀 ...

最新文章

  1. ARM函数调用时参数传递规则
  2. http通道连接mysql_通过http tunnel连接mysql
  3. bash mysql 循环输出_Bash实用技巧:同时循环两个列表
  4. What's the QPSK?
  5. iOS 柱状图的定制
  6. DEV 实现CheckBox单选
  7. 从零开始进行Adadelta的梯度下降
  8. openstreetmap地图下载的几种方法
  9. 金万维异速联再出大杀器 掀移动应用普及化年终热浪
  10. passenger多ruby版本共存部署
  11. 软件架构设计杂记: 好作品是改出来的,好的代码是不断重构打磨出来的, 心性是历经艰难困苦修炼出来的
  12. 财富取决于极少的大高潮,幸福取决于较多的小高潮
  13. tpc-e mysql_mysql评测工具TPC-C使用
  14. 两个电脑主机共用一个显示器
  15. IDEA SVN 报 E170013
  16. java汉字转换国标码_GBK/GB2312编码问题分析以及java获取汉字国标码
  17. SAP HANA是否取代BW?
  18. 解决sql注入问题(丛林战争项目)
  19. pythonmt4通讯swot矩阵_SWOT矩阵表
  20. 吴恩达机器学习(一)机器学习的入门介绍

热门文章

  1. 黑莓Q10 bb10 系统 gba等模拟器 retroArch全教程
  2. java 汉字转为GBK编码,再由GBK编码转为汉字
  3. matlab中数组的排序以及转置
  4. CMMI之项目管理类
  5. 点分十进制与unsigned int 32互转
  6. Python调用大漠插件(一)
  7. STM32开源代码——光敏传感器
  8. 超强配置 索爱A5无线蓝牙耳机重磅来袭
  9. KNN的数据插补方法总结
  10. 基于时间轮的定时器HashedWheelTimer