首页> 中国专利> 一种基于树优化的程序依赖关系分析方法及系统

一种基于树优化的程序依赖关系分析方法及系统

摘要

本发明涉及一种基于树优化的程序依赖关系分析方法及系统,该方法包括:步骤1,将函数中的连续指令划分为多个基本块,每个基本块仅有单一入口和单一出口;步骤2,针对每个基本块构建相应的指令依赖树和指令依赖森林;步骤3,分析指令依赖树和指令依赖森林,去除未改变原状态的指令,去除依赖于特殊寄存器的指令依赖树;步骤4,从前一基本块中去除在其后续各基本块中有重复定义但未被使用的变量对应的指令依赖树;步骤5,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置;步骤6,在所有动态插装位置上插装统一化的影子指令。本发明无需对每条指令进行插装,有效提高了程序动态分析的效率。

著录项

  • 公开/公告号CN103793653A

    专利类型发明专利

  • 公开/公告日2014-05-14

    原文格式PDF

  • 申请/专利权人 中国科学院信息工程研究所;

    申请/专利号CN201410055841.2

  • 发明设计人 陈恺;赵险峰;张颖君;

    申请日2014-02-19

  • 分类号G06F21/57;

  • 代理机构北京轻创知识产权代理有限公司;

  • 代理人杨立

  • 地址 100093 北京市海淀区闵庄路甲89号

  • 入库时间 2024-02-20 00:07:10

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2020-02-11

    未缴年费专利权终止 IPC(主分类):G06F21/57 授权公告日:20160629 终止日期:20190219 申请日:20140219

    专利权的终止

  • 2016-06-29

    授权

    授权

  • 2014-06-11

    实质审查的生效 IPC(主分类):G06F21/57 申请日:20140219

    实质审查的生效

  • 2014-05-14

    公开

    公开

说明书

技术领域

本发明涉及程序分析技术领域,特别是涉及一种基于树优化的程序依赖关系分析方法及系统。

背景技术

程序依赖关系分析通常被用来检测变量是否受输入数据影响,也可以用来跟踪输入数据的传播过程。这个方法在检测攻击和漏洞方面应用非常广泛,已经成为软件分析领域的重要方法。

程序依赖关系分析支持静态和动态分析。静态分析方法效率较高,但是精确度不足,因为需要估计间接内存地址和间接跳转/调用的值。动态分析方法在二进制代码分析中较为流行,通常是通过插装指令来实现分析,但通过这种方法每个插装的指令执行都要消耗额外的执行时间,这个时间通常是正常执行的数倍。

分析人员尝试很多方法来提高程序依赖关系分析的效率,主要是基于硬件的方法和基于编译器的方法。

一、对于基于硬件的方法,虽然可极大提高分析的运行性能,但是并不是所有的系统都支持这类硬件。而且,在硬件中修改传播规则也不容易。

二、对于基于编译器的方法,通常依据一些策略从源程序中选择部分语句进行程序依赖关系分析。通过减少需要分析语句的数量,来提高传播速度。但是,这些方法通常会遭遇一个或多个如下的缺陷:a)只有其中一部分与某些预定策略相关的指令会被选择进行依赖关系分析。一旦这些策略发生改变,这类方法需要重新分析整个程序来找到合适的分析语句,缺乏灵活性。b)这些方法通常需要源代码,但是软件提供商,尤其商业的软件提供商,不会公开源代码。c)一些方法需要改变目标程序的原始代码,这是在一些应用程序中是不被允许的。

因此,针对上述问题,本发明提出了一种基于树优化的程序依赖关系分析方法及系统。

发明内容

本发明所要解决的技术问题是提供一种基于树优化的程序依赖关系分析方法及系统,用于解决现有程序依赖关系分析方法效率不高、精度度不足等问题。

本发明解决上述技术问题的技术方案如下:一种基于树优化的程序依赖关系分析方法,包括:

步骤1,以函数为基本单位进行程序依赖关系分析,将函数中的连续指令划分为多个基本块,且保证每个基本块有单一入口和单一出口;

步骤2,针对每个基本块构建相应的指令依赖树和指令依赖森林;

步骤3,分析各基本块的指令依赖树和指令依赖森林,去除未改变原状态的指令,去除依赖于特殊寄存器的指令依赖树;

步骤4,分析各基本块的指令依赖树和指令依赖森林,从前一基本块中去除在其后续各基本块中有重复定义但未被使用的变量对应的指令依赖树;

步骤5,根据步骤3和步骤4的处理结果,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置;

步骤6,在所有动态插装位置上插装统一化的影子指令,通过影子指令分析指令依赖树和指令依赖森林。

在上述技术方案的基础上,本发明还可以做如下改进。

进一步,所述步骤2中,若指令中定义的一个变量i没出现在指令依赖树的节点集合V中,则将该变量i增加到指令依赖树的节点集合V中,并增加从该节点i到指令所用到的变量相应的边。

进一步,所述步骤2中,若指令中定义的一个变量i出现在指令依赖树的节点集合V中,且其是指令依赖森林的根节点,则删除从节点i到其孩子节点的边,并增加从i到指令所用到的变量集合相应的边;若i是指令依赖森林的叶子节点,则i没有在基本块中重复定义,增加新的节点i1,作为块入口i的初始状态,并用i1取代i,增加从i到相应指令中使用变量的边;否则,增加i的父节点到i的孩子节点的边,并去掉i的父节点到i的边及i到其孩子节点的边。

进一步,所述步骤4具体包括:

步骤41,定义一个记录在一个基本块中被定义但未被使用的变量的集合S;

步骤42,分析函数的最后一个基本块B,如果该基本块的最后一个指令是一个函数调用指令,则清空S;

步骤43,若最后一个基本块是一个分支块,则重新定义S集合为两个分支的定义集合的交集,若存在一个分支未被分析过,则返回分析该分支;

步骤44,如果基本块的非叶节点存在于步骤43执行后的S中,则删除该非叶节点;

步骤45,更新集合S,重复执行步骤41至步骤44,优化基本块B前面的基本块。

进一步,所述步骤5中,若在指令依赖森林中没有间接内存地址,则动态插装位置为基本块中的任意位置。

进一步,所述步骤6中通过影子指令分析指令依赖森林具体包括:影子指令对指令依赖森林中每个根节点n和其孩子节点ci,执行操作T(n)=Ui=[0,m]T(ci),其中T(x)表示返回x的依赖关系状态,且孩子节点ci中i=0,…,m。

对应上述方法,本发明的技术方案还包括一种基于树优化的程序依赖关系分析系统,包括依次连接的基本块划分模块、依赖关系构建模块、基本块内优化模块、基本块间优化模块、插装位置获取模块和影子指令生成模块:

基本块划分模块,其用于以函数为基本单位进行程序依赖关系分析,将函数中的连续指令划分为多个基本块,且保证每个基本块有单一入口和单一出口;

依赖关系构建模块,其用于针对每个基本块构建相应的指令依赖树和指令依赖森林;

基本块内优化模块,其用于分析各基本块的指令依赖树和指令依赖森林,去除未改变原状态的指令,去除依赖于特殊寄存器的指令依赖树;

基本块间优化模块,其用于分析各基本块的指令依赖树和指令依赖森林,从前一基本块中去除在其后续各基本块中有重复定义但未被使用的变量对应的指令依赖树;

插装位置获取模块,其用于根据基本块内优化模块和基本块间优化模块的处理结果,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置;

影子指令生成模块,其用于在所有动态插装位置上生成统一化的影子指令,通过影子指令分析指令依赖树和指令依赖森林。

进一步,所述依赖关系构建模块中,若指令中定义的一个变量i没出现在指令依赖树的节点集合V中,则将该变量i增加到指令依赖树的节点集合V中,并增加从该节点i到指令所用到的变量相应的边。

进一步,所述依赖关系构建模块中,若指令中定义的一个变量i出现在指令依赖树的节点集合V中,且其是指令依赖森林的根节点,则删除从节点i到其孩子节点的边,并增加从i到指令所用到的变量集合相应的边;若i是指令依赖森林的叶子节点,则i没有在基本块中重复定义,增加新的节点i1,作为块入口i的初始状态,并用i1取代i,增加从i到相应指令中使用变量的边;否则,增加i的父节点到i的孩子节点的边,并去掉i父节点到i的边及i到其孩子节点的边。

进一步,若在指令依赖森林中没有间接内存地址,则所述插装位置获取模块将基本块中的任意位置作为动态插装位置。

本发明的有益效果是:本发明一方面采用树优化方法,不需要对二进制程序的每一条指令进行动态插装;另一方面采用程序动态分析,避免了单独使用静态树优化分析不准确的问题,提高了整体程序分析效率。该方法无需对每条指令进行插装,节省了时间和空间。同时,使用基本块为一个单元,通过构建指令依赖树与指令依赖森林对基本块内和基本块间进行分析,有效识别无用的依赖关系传递操作并进行优化,有效提高了程序动态分析过程的效率。最后通过选取合适的位置并产生影子指令来对目标二进制程序进行插装,极大地提高了分析的效率。

附图说明

图1为本发明的程序依赖关系分析方法的流程示意图;

图2为本发明的程序依赖关系分析系统的结构示意图;

图3为本发明实施例中含4个基本块的函数示例图;

图4为本发明实施例中对如图3所示的函数进行基本块优化的示例图。

具体实施方式

以下结合附图对本发明的原理和特征进行描述,所举实例只用于解释本发明,并非用于限定本发明的范围。

如图1所示,实施例一是一种基于树优化的程序依赖关系分析方法,具体包括以下步骤:

步骤1,以函数为基本单位进行程序依赖关系分析,将函数中的连续指令划分为多个基本块,且保证每个基本块有单一入口和单一出口;

步骤2,针对每个基本块构建相应的指令依赖树和指令依赖森林;

步骤3,分析各基本块的指令依赖树和指令依赖森林,去除未改变原状态的指令,去除依赖于特殊寄存器的指令依赖树;

步骤4,分析各基本块的指令依赖树和指令依赖森林,从前一基本块中去除在其后续各基本块中有重复定义但未被使用的变量对应的指令依赖树;

步骤5,根据步骤3和步骤4的处理结果,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置;

步骤6,在所有动态插装位置上插装统一化的影子指令,通过影子指令分析指令依赖树和指令依赖森林。

对应地,如图2所示,本实施例还给出了一种基于树优化的程序依赖关系分析系统,包括依次连接的基本块划分模块、依赖关系构建模块、基本块内优化模块、基本块间优化模块、插装位置获取模块和影子指令生成模块:

基本块划分模块,其用于以函数为基本单位进行程序依赖关系分析,将函数中的连续指令划分为多个基本块,且保证每个基本块有单一入口和单一出口;

依赖关系构建模块,其用于针对每个基本块构建相应的指令依赖树和指令依赖森林;

基本块内优化模块,其用于分析各基本块的指令依赖树和指令依赖森林,去除未改变原状态的指令,去除依赖于特殊寄存器的指令依赖树;

基本块间优化模块,其用于分析各基本块的指令依赖树和指令依赖森林,从前一基本块中去除在其后续各基本块中有重复定义但未被使用的变量对应的指令依赖树;

插装位置获取模块,其用于根据基本块内优化模块和基本块间优化模块的处理结果,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置;

影子指令生成模块,其用于在所有动态插装位置上生成统一化的影子指令,通过影子指令分析指令依赖树和指令依赖森林。

对于上述六个步骤及六个处理模块,其实施的具体过程分为以下几个部分。

一、基本块划分

基本块划分原则依据通用的方法,将函数中的连续指令划分为多个基本块,且保证每个基本块有单一入口和单一出口。如图3所示,示意了一段分为4个基本块的函数,图中push指令是将寄存器edi中的内容放入内存“栈”中,“栈”由esp指针表示,[esp]表示该指针指向的位置;call指令表示程序调用,在调用前需要保存现在程序的位置(由eip表示)到栈中,具体位置是当前栈偏移4个字节的内存(由[esp-4]表示)。

基本块中的指令无需逐一进行分析,这里定义T(x)表示返回x是否依赖于外部输入的状态,其中x可以是一块内存或寄存器。如图3中第一个基本块中对应指令可知“T([esp])←T(edi)”,“T(edi)←T(ecx)”,“T([esp-4])←T(eip)”(call指令操作)。其中,“T([esp-4])←T(eip)”表示如果eip依赖于外部输入,则寄存器esp-4指向的内存也依赖于外部输入;“T(edi)←T(ecx)”表示如果ecx依赖于外部输入,则寄存器edi也依赖于外部输入。这个过程中,无需分析指令“lea ecx,[edi+04h]”,因为它没有改变ecx的状态。

二、指令依赖树与指令依赖森林构建

在每个基本块中构建指令依赖树T(V,E)或指令依赖森林FB,,其中V表示指令依赖树的节点集合,E表示指令依赖树中的各边。构建指令依赖树的过程主要依据如下规则:

1)如果指令中定义的一个变量i没出现在指令依赖树T的节点集合V中,则将其增加到节点集合V中,并增加从该节点i到指令所用到的变量u相应的边e。

2)如果指令中定义的一个变量i出现在指令依赖树T的节点集合V中,如果i是FB的根节点,则删除从i到其孩子节点的边,并增加从i到指令所用到的变量集合相应的边;如果i是叶子节点,即i没有在块中重复定义,则增加新的节点i1,作为块入口i的初始状态,并用i1取代i,增加从i到相应指令中使用变量的边;否则,对于指令依赖树中非根、非叶的其他节点,增加i的父节点到i的孩子节点的边,并去掉i的父节点到i的边及i到其孩子节点的边。

举例说明,如图4所示,对于图4中的基本块1,首先根据push edi指令,建立依赖树[esp]←edi,然后指令mov edi,ecx将ecx的值赋给edi,即重新定义了edi,此时将[esp]←edi中的edi改变为edi1,便于区分edi在基本块入口的时候的值(采用edi1表示)和指令mov edi,ecx执行后edi的值(采用edi表示)。

3)指令依赖森林FB是指令依赖树的联合,其中包含多个根节点。

三、基本块内优化

优化包括两部分:一是舍弃未改变原状态的指令,如基本块1中lea ecx,[edi+04h]的依赖关系分析过程可以舍弃(详见第一部分基本块划分);二是特殊寄存器优化,如程序指针寄存器eip和栈寄存器esp,实际中并不关注从寄存器eip和esp流出的数据,而是关注eip和esp是否依赖于外部输入,因为一旦eip或esp依赖于外部输入,程序即进入错误状态。如图4所示,基本块3中即进行了优化,优化部分用虚线圆圈表示。

四、基本块间优化

这一部分主要是通过在基本块之间进行分析,减少指令依赖森林中节点的数量。例如,图4中基本块2中T(ebx)=T([esp])并不是必须的,因为ebx在后续基本块3和4中会被重新定义,从而可进行优化。

举例说明,基本块间优化的具体程序流程如下:

结合上述分析程序,首先分析最后一个基本块B,如果基本块的最后一个指令是一个函数调用(call指令),设计算法清空定义的集合S,这是因为不知道被调用的函数将用到哪些变量。如果基本块B是一个分支块,则重新定义S集合为两个分支的定义集合的交集,如有存在一个分支未被分析过,则程序返回(待从另外一个分支遍历自下而上分析的时候会再次到此基本块)。通过这种方式,S记录了在一个基本块中被定义但未被使用的变量。如果基本块B的非叶节点存在于S,则可以删除该节点进行优化。待优化完成后,更新集合S,并以相同方式优化B前面的基本块。

可知,虽然采用的算法是循环的,但只分析每个基本块一次,因此效率得到了提高。

五、插装位置查找

在构建指令依赖森林后,要找到一个合适的位置来插装目标程序(即后面的影子指令)。传统的动态依赖关系分析方法通常对每个指令均进行插装,该过程非常耗时,因此本实施例进行在基本块中合并执行多个插装指令的操作,选取内存索引中不能静态计算寄存器值的指令所在的位置,将该指令位置之前的位置作为动态插装位置。

如果在指令依赖森林FB中没有间接内存地址(例如“[esp]”指esp指向的内存,但esp本身的值无法静态获取),插装指令在基本块中的位置是任意的。为了简化,通过插装在基本块的第一个指令前。但是,如果使用了间接内存索引,则无法获知间接索引中使用的每个寄存器的值。而获取寄存器值的最简单的方法是插装指令,即利用被插装的指令动态获取间接内存索引的值。因此,只需要插装内存索引中不能被静态计算寄存器值的指令。

六、影子指令生成

动态插装代码被称为影子指令,传统的方法通常插装对每个指令均插入影子指令,因此需要使用不同类型的影子指令。例如,一个二进制操作的影子指令是与数据移动操作不同的。这个过程增加了分析的复杂度,因为程序需要选择合适的影子指令。假设影子指令类型的数量为n,选择合适的影子指令需要的时间是O(log(n))。而本实施例中可以产生一个统一的影子指令。

本实施例的影子指令很简单,读每个基本块的指令依赖森林FB,并执行依赖关系分析。对于FB中每个节点n和它的孩子节点ci(i∈0,…,m),影子指令只执行如下操作:T(n)=Ui=[0,m]T(ci)。其中T(x)表示返回x的依赖关系状态。因此只有一类影子指令,从而选择影子指令过程的时间复杂度从O(log(n))降为O(l)。

以上所述仅为本发明的较佳实施例,并不用以限制本发明,凡在本发明的精神和原则之内,所作的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号