基于动态影子栈的栈缓冲区溢出攻击防御方法及系统

文档序号:29437162发布日期:2022-03-30 09:08阅读:265来源:国知局
基于动态影子栈的栈缓冲区溢出攻击防御方法及系统

1.本发明属于栈缓冲区溢出攻击防御技术领域,特别涉及一种基于动态影子栈的栈缓冲区溢出攻击防御方法及系统。


背景技术:

2.由于栈缓冲区溢出攻击需要覆盖栈内的内容,所以检测栈缓冲区溢出攻击直接方法就是查验栈中内容是否被篡改,代表方法为stackguard。该方法是在栈中植入一段特殊数据作为“哨兵”,而“哨兵”位于攻击发生点与返回地址存储位置之间。这样当攻击者通过持续注入非法数据覆盖栈内内容时,也会覆盖“哨兵”。所以,通过检查“哨兵”是否被篡改,就可以获得返回地址是否被篡改。但是攻击者在获知“哨兵”存在以后,仍然可以绕开“哨兵”而直接修改返回地址。
3.既然攻击者需要篡改返回地址,更直接防御方法是直接检查栈中返回地址是否被篡改。典型的工作如parallel shadow stack。它们的核心思想就是构造一个影子栈,在函数调用时在影子栈内存储返回地址的相关信息,在函数返回时用影子栈内的数据检查返回地址的完整性。这种方法更为直接彻底,较parallel shadow stack而言,更难以绕开。
4.虽然影子栈可以保护栈中返回地址,但是影子栈自身的安全性难以得到保障。当影子栈地址被攻击者所发现,就存在攻击者绕开影子栈安全机制的可能。以parallel shadow stack为例:作为影子栈重要实现方式,平行影子栈的位置与正常栈之间相隔一个固定偏移,当正常栈的地址泄露后,影子栈的位置也被暴露,与返回地址一样存在被攻击者篡改的可能。如何提高影子栈的安全性,是基于影子栈保护返回地址,阻止栈缓冲区溢出攻击的重要挑战。


技术实现要素:

5.针对现有技术中存在的问题,本发明提出一种基于动态影子栈的栈缓冲区溢出攻击防御方法及系统,使影子栈地址随机化,攻击者难以查找且破坏,进而提高返回地址的安全性,使栈缓冲区溢出攻击失效。
6.为了实现上述目的,本发明采用以下的技术方案:
7.本发明提供了一种基于动态影子栈的栈缓冲区溢出攻击防御方法,包含以下步骤:
8.在进程地址空间,申请足够大的存储空间,用来存放影子栈内容;
9.采用秘密共享的方法,将影子栈的内容分割成若干份额分散存储;
10.基于软件自身产生的随机因子,并基于移位反馈寄存器生成随机数流,在每次函数调用的时候,生成随机变化的影子栈的存储地址,即生成随机变化的影子栈份额的存储地址,将各份额分散存储到存储空间的指定位置。
11.进一步地,所述申请足够大的存储空间包括:将所有函数的影子栈都放在一个存储空间中;通过程序编译时“插桩”,在软件代码指定位置插入分配存储空间指令,该分配存
储空间指令在软件启动后,为进程分配一个足够大的连续空间存放影子栈内容。
12.进一步地,所述采用秘密共享的方法,将影子栈的内容分割成若干份额分散存储,包括:将影子栈作为秘密,采用(k,n)门限方案,将影子栈的内容秘密分割成若干份额,影子栈的内容即是函数调用的返回地址,将返回地址拆分成若干份额。
13.进一步地,所述生成随机变化的影子栈的存储地址包括以下步骤:
14.采用依赖软件自身产生随机因子;
15.将随机因子作为移位反馈寄存器lfsr的初始值,由lfsr产生一序列随机数;
16.将随机数映射为存储空间内有效地址,这将是份额的存储地址;
17.将lfsr种子作为地址索引存储在存储空间中。
18.进一步地,所述采用依赖软件自身产生随机因子,包括:在软件内部植入随机因子发生器,只基于处理器支持,采用多线程竞争的方式,并引入攻击不可控的因素,产生不被攻击者所影响的随机因子,具体方法如下:
19.首先,创建三个线程、一个开关变量和循环体;开关变量的状态只有打开和关闭两种,线程1用于打开开关变量,线程2用于关闭开关变量,线程3用于通过判断开关变量的状态输出一系列由0和1构成的随机数;执行循环体的目的是引入不可控因素,使攻击者无法预判线程的竞争结果;其中线程1和线程2执行同样的循环运算,线程3执行等待时间将若干倍于线程1和线程2的执行等待时间;
20.然后,同时开启三个线程,线程1执行循环体后,判断开关变量的状态,如果开关变量是关闭则进行打开操作,否则不进行任何操作,然后重新执行循环体;线程2执行循环体后,判断开关变量的状态,如果开关变量是打开则进行关闭操作,否则不进行任何操作,然后重新执行循环体;线程3执行循环体后,判断开关变量的状态,如果开关变量是打开则输出随机数1,否则输出随机数0,然后重新执行循环体;线程3反复输出0/1随机序列,直至随机序列长度满足需要,关闭线程1、2和3,此随机序列作为随机因子。
21.进一步地,所述随机数生成的方法为:选择一个异或门外接移位反馈寄存器,即ie型的lfsr;一个n阶的lfsr由n个触发器d和若干个异或门组成,lfsr中的初始值为上述随机因子,在随机因子中随机抽取n个连续的序列作为种子,lfsr下一刻输出为qn,下一刻输入是由整个移位反馈寄存器值的某些位做异或运算的结果,即其中gn为反馈系数,取值为0或1,取为0时表明不存在该反馈之路,取为1时表明存在该反馈之路。
22.进一步地,所述将随机数映射为存储空间内有效地址包括:将存储空间分配成多个地址,对存储空间内所有有效地址进行标号,对所产生随机数进行模运算,为每个计算结果匹配一个有效地址。
23.进一步地,在存储空间中分配一处独立存储空间,将lfsr种子作为有效地址的索引存储在此空间中,该空间称之为种子空间;所述种子空间包括环形存储空间和独立存储空间,所述环形存储空间用于缓存每个函数调用所对应lfsr使用的lfsr种子,所述独立存储空间用于存储lfsr最新的lfsr种子;所述种子空间的使用过程如下:
24.当函数调用时,将当前lfsr的状态作为种子从独立存储空间取出导入lfsr,lfsr产生随机数,并将该种子存储于环形存储空间;
25.当函数返回时,将当前lfsr的状态作为种子存储于独立存储空间,并从环形存储
空间检索到对应的种子,导入lfsr用于恢复存储地址;
26.当继续函数返回时,将不再备份lfsr种子,直接从环形存储空间检索对应的种子,导入lfsr用于恢复存储地址;当后续操作是函数调用时,从独立存储空间取出种子,lfsr继续产生随机数。
27.进一步地,所述函数调用的变化包括:(1)获取返回地址,(2)根据秘密共享生成份额,(3)获取各个份额的地址,(4)完成影子栈存储;所述函数返回的变化包括:(1)获取各个份额的地址;(2)获取份额,(3)还原影子栈内容,(4)检验返回地址完整性。
28.本发明还提供了一种基于动态影子栈的栈缓冲区溢出攻击防御系统,包括:
29.存储空间分配模块,用于在进程地址空间,申请足够大的存储空间,用来存放影子栈内容;
30.影子栈内容分割模块,用于采用秘密共享的方法,将影子栈的内容分割成若干份额分散存储;
31.地址发生器模块,用于基于软件自身产生的随机因子,并基于移位反馈寄存器生成随机数流,在每次函数调用的时候,生成随机变化的影子栈的存储地址,即生成随机变化的影子栈份额的存储地址,将各份额分散存储到存储空间的指定位置。
32.与现有技术相比,本发明具有以下优点:
33.影子栈是一种常见的防范栈缓冲区溢出攻击的方法,然而影子栈自身的安全性往往被忽略。在现有影子栈的解决方案中,往往都是假设影子栈是安全的,但现实并非都是如此。在一些影子栈方案中,影子栈甚至与栈本身存在一定的关联,例如地址存在相对固定的偏移,这就意味着攻击者可以利用栈来寻找影子栈,为篡改影子栈内容提供了机会。基于此,本发明提出一种基于动态影子栈的栈缓冲区溢出攻击防御方法,将影子栈的内容分割成若干份额分散存储,只有获取一定数量份额才能秘密还原出影子栈的内容(即返回地址),所以攻击者只有获取足够多的份额才能破坏影子栈,提高了攻击的难度。再者,移位反馈寄存器产生随机数,并将随机数映射为存储空间内有效的随机地址,将该随机地址作为影子栈份额的存储地址,实现对影子栈内容的随机存储;由于随机地址是在每次函数调用时产生,所以攻击者难以猜测到影子栈的存储地址,进一步增加攻击的难度。本发明主要是通过影子栈内容分散存储和影子栈地址随机化,实现对影子栈的双重保护,大大增加了攻击难度,从而提高影子栈的安全性。
附图说明
34.为了更清楚地说明本发明实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本发明的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动的前提下,还可以根据这些附图获得其他的附图。
35.图1是本发明实施例的基于动态影子栈的栈缓冲区溢出攻击防御方法的原理示意图;
36.图2是本发明实施例的影子栈地址生成的流程示意图;
37.图3是本发明实施例的ie型的lfsr的原理示意图;
38.图4是本发明实施例的还原影子栈内容的示例图,图中(a)是f(x)的还原图,(b)是f′
(x)的还原图。
具体实施方式
39.下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。
40.为了便于理解本发明,首先对以下三个名词进行如下解释:
41.1.lfsr:线性反馈移位寄存器(linear feedback shift register,lfsr)是指,给定前一状态的输出,将该输出的线性函数再用作输入的移位寄存器。异或运算是最常见的单比特线性函数:对寄存器的某些位进行异或操作后作为输入,再对寄存器中的各比特进行整体移位。
42.2.程序插桩:程序插桩的基本原理是在不破坏被测试程序原有逻辑完整性的前提下,在程序的相应位置上插入一些探针。这些探针本质上就是进行信息采集的代码段,可以是赋值语句或采集覆盖信息的函数调用。通过探针的执行并输出程序的运行特征数据。基于对这些特征数据的分析,揭示程序的内部行为和特征。
43.3.lbr:intel处理器的一种硬件特性,本质是用处理器的内部寄存器来记录处理器最近执行的跳转地址信息(主要是源地址和目的地址)。
44.如图1所示,本实施例的一种基于动态影子栈的栈缓冲区溢出攻击防御方法,包含以下步骤:
45.步骤s11,在进程地址空间,申请足够大的存储空间,用来存放影子栈内容;
46.步骤s12,采用秘密共享的方法,将影子栈的内容分割成若干份额分散存储;
47.步骤s13,基于软件自身产生的随机因子,并基于移位反馈寄存器生成随机数流,在每次函数调用的时候,生成随机变化的影子栈的存储地址,即生成随机变化的影子栈份额的存储地址,将各份额分散存储到存储空间的指定位置。
48.具体的,为影子栈分配空间:
49.本实施例要求为存储影子栈申请一块足够大的存储空间。该存储空间应该足够大,这样才能为攻击者带来攻击难度。由于影子栈存储于该空间,一旦该空间不够大,攻击者极易利用穷举法进行暴力破解,找到影子栈的位置实现攻击。同时,该空间也不能太大,否则会占用过多内存,增加不必要的消耗。所以,在设定申请该存储区域大小时,用户需要在安全性和效率之间,找到一个平衡点。
50.本实施例要求软件在启动时,为影子栈分配存储空间。同一软件所有的函数调用,使用同一个存储空间。为影子栈分配存储空间是软件原有操作之外的操作,分配过多存储空间,不仅会影响进程的进度,同时会消耗过多内存。为避免每一个影子栈对应一个单独空间而造成内存的过度消耗,本实施例将所有函数的影子栈都放在一个存储空间中。
51.本实施例通过程序编译时“插桩”,在软件代码指定位置插入分配存储空间指令,在软件启动后,使用malloc函数动态为进程分配一个足够大的连续空间存放影子栈内容。只有当该连续空间足够大时,影子栈可能存储的位置足够多,才能实现影子栈位置的真正随机,难以被攻击者猜测到,为影子栈地址的随机化提供保障,需要说明的是,本实施例主
要针对采用c语言编写的程序源码,对于其它源码编写的程序源码,仍然有类似的函数库支撑。这些指令通常在用户编写函数之前执行,以确保这些函数调用时可以使用影子栈,而系统默认的函数,如库函数则不在此列。例如在main函数中首先执行分配存储空间的指令,然后再执行开发者编写的函数。对于支持软件运行的库函数,可以采用本实施例的机制重新编译,之后这些库函数才能使用动态影子栈机制。
52.由于所有函数调用存储影子栈使用同一个存储空间,可能会造成份额之间相互重叠的情况。举例而言,影子栈1的一个份额a1和影子栈2的一个份额b1发生了重叠现象(提示:实际存储的影子栈的份额,而不是影子栈本身)。影子栈1先完成了所有份额在堆中的存储,a1占用了1号地址。在进行影子栈2的份额存储时,地址发生器计算出b1的随机地址也是1号地址。这样就发生了地址重叠。针对此类情况,本实施例的处理原则是:视最后一次存储为有效。在此例中,只保证b1的有效性,将a1视为无效份额。由于地址重叠会影响影子栈还原的效率,同时为减少某个影子栈因所有份额均被丢弃而无法还原的现象发生,本实施例创建一个足够大的存储空间来减少这种地址重叠情况的发生。本实施例默认为每个进程创建一个5m的存储空间:由于每个地址占4字节,可以存储超过130,0000个影子栈地址,而软件内部的函数数量一般不超过1000个,即使每个影子栈分拆为10个份额,冲突概率也不超过1%。
53.具体的,为了增加攻击难度,将影子栈的内容分割成若干份额分散存储,包括:将影子栈作为秘密,采用(k,n)门限方案,将影子栈的内容秘密分割成若干份额,影子栈的内容即是函数调用的返回地址,将返回地址拆分成若干份额,地址发生器为每个份额分配一个堆内地址,这些份额被分散存储于堆内。攻击者要想篡改返回地址,就必须篡改这些分散存储的份额。
54.为了防止影子栈的地址固定,本实施例在每次函数调用的时候,生成随机变化的影子栈份额的存储地址。如图2所示,本实施例使用地址发生器生成存储地址,包含以下步骤:
55.步骤s311,采用依赖软件自身产生随机因子。
56.传统方法一般是以系统时间作为随机因子,但是这种方法易被攻击者猜测到。更优的方法是基于硬件随机数发生器,但是这种方法需要专有硬件的支持,并不普遍适用于所有计算机系统。为了提高安全性,又不对硬件作特殊要求,本实施例采用依赖软件自身产生随机因子的方法,在软件内部植入随机因子发生器,只基于处理器支持,采用多线程竞争的方式,并引入攻击不可控的因素,产生尽可能不被攻击者所影响的随机因子,具体方法如下:同时创建三个线程和一个开关变量;开关变量的状态只有打开和关闭两种,线程1的工作是打开开关变量,线程2的工作是关闭开关变量,线程3的工作是通过判断开关变量的状态输出一系列由0和1构成的随机数。线程内部除上述工作,还执行特定的循环体,执行循环体的目的是引入不可控因素,使攻击者无法预判线程的竞争结果。线程1和线程2将执行同样的循环运算,线程3执行等待时间将若干倍于线程1和线程2的执行等待时间。举例而言,线程1和线程2需要0.1s运行一次,线程1和线程2的运行次数将是线程3的10倍左右。
57.构造循环体的方法如下:(1)构造一个for循环,循环次数不同,消耗时间不同,所以线程3的循环次数应该远小于线程1和线程2;(2)以rand()产生系统随机数,或者以文本内容作为输入,产生一系列0-128之间的数据流(数据流可以被攻击者所探知,不影响系统
安全);(3)如果数据流大于64,执行数据加操作,如果小于或等于64,执行数据减操作。伪码如下所述。
[0058][0059]
由于在这一过程中,循环过程中存在处理器分支预测失败等异常存在,循环体消耗时间存在不可控制的随机因素存在,攻击者无法完全控制随机数流的产生,所以提高了安全性。
[0060]
下面分别说明程序运行各个线程时程序所进行的操作:(1)同时开启三个线程;(2)线程1执行循环体后,然后判断开关变量的状态,如果开关变量是关闭则进行打开操作,否则不进行任何操作,然后重新执行循环体;(3)线程2执行循环体后,然后判断开关变量的状态,如果开关变量是打开则进行关闭操作,否则不进行任何操作,然后重新执行循环体;(4)线程3执行循环体后,判断开关变量的状态,如果开关变量是打开则输出随机数1,否则输出随机数0,然后重新执行循环体;(5)线程3反复输出0/1随机序列,直至随机序列长度满足需要,关闭线程1、2和3,此随机序列即作为随机因子。
[0061]
步骤s312,将随机因子作为移位反馈寄存器lfsr的初始值,由lfsr产生一序列随机数。
[0062]
虽然采用多线程竞争方法产生的随机序列更加安全,但通过此方法产生随机数存在耗时长,占用内存多,效率低等问题。针对此问题,本实施例进一步改进,即利用产生的随机序列作为随机因子,与移位反馈寄存器相结合,产生所需要的随机数。具体方法如下:如图3所示,选择一个异或门外接移位反馈寄存器,即ie型的lfsr。一个n阶的lfsr由n个触发器和若干个异或门组成。图3中d为触发器,gn为反馈系数,取值只能为0或1,取为0时表明不存在该反馈之路,取为1时表明存在该反馈之路,这里的反馈系数决定了产生随机数的算法的不同。lfsr中的初始值即为步骤s311所产生的随机因子。lfsr下一刻输出为qn,下一时刻输入是由整个移位反馈寄存器值的某些位做异或运算的结果,即
[0063]
随机因子的选择决定了输出的伪随机序列的不同。本实施例将随机因子中的一部分数作为寄存器的种子,即在随机因子中随机抽取n个连续的序列作为种子。在n的选择上,n个d触发器最多可以提供2
(n-1)
个状态,即产生一个长度为2
(n-1)
的伪随机序列,而当n为32时,则寄存器可以产生一个周期为4 294 967295位的伪随机序列。这样就解决了采用多线程竞争的方式产生的随机数效率低的问题。同时也可以存储少量种子来还原存储地址。
[0064]
步骤s313,将随机数映射为存储空间内有效地址,这将是份额真实的存储地址(提示:存储的不是影子栈而是影子栈的份额)。
[0065]
假设存储空间可以存放n个地址,按顺序将其从1到n进行标号。本实施例利用步骤
s312产生的随机数进行模n运算,所得结果决定存储地址。例如所得结果为100,则堆的初始地址就是标号为100的地址。通过n次模运算得到了n个地址。
[0066]
可能会出现无效地址的情况。由于随机数的随机性,n个地址中可能会出现相同的地址。例如n为1000,随机数2034和1034进行模运算以后,地址均为34,这将产生冲突。为避免无效地址,本实施例对这n个地址进行排序处理。进行排序操作时,一旦出现重复地址直接舍弃处理,同时记录有效地址数量;当本次排序操作完毕之后,如果有效地址数量不满足条件,再次启动随机数发生流程,产生新的地址,直至有效地址数量满足条件。
[0067]
步骤s314,将lfsr种子作为地址索引存储在存储空间中。
[0068]
lfsr持续工作,产生随机数流。lfsr输入一个种子后,连续输出n组地址,作为份额地址。即一个种子对应一组返回地址。lfsr需要不断调用新的种子才能产生新的返回地址。为保证lfsr能产生所有返回地址,进行如下操作:在存储空间中分配一处独立存储空间,称之为种子空间,将所有返回地址对应的种子存储在种子空间中。
[0069]
为了支持多线程共同使用种子空间。种子空间的存储单元为二元组(返回地址,种子),返回地址即函数调用的返回地址,种子为lfsr种子。在检索过程中,将根据正常栈的返回地址检索lfsr种子,用来还原份额的地址。函数调用时产生地址所用的lfsr种子,将始终作为特殊元素独立存储。所以种子空间包括环形存储空间和独立存储空间,环形存储空间用于缓存每个函数调用所对应lfsr使用的lfsr种子,独立存储空间用于存储lfsr最新的lfsr种子。提示:之所以这样,是因为一个软件共享一个lfsr。为简化设计,环形存储空间采用环形存储,过期覆盖的存储原则。本实施例默认设计1m大小的环形存储空间,理论上不会出现函数返回时,无法找到地址的情况(当然,可能存在一种极端情况,该函数长期得不到调用,所以其在环形空间内的种子被覆盖。由于1m大小能够存储足够多的种子信息,本实施例认为这种情况不会出现)。
[0070]
种子空间的使用过程为:(1)当函数调用时,将当前lfsr的状态作为种子从独立存储空间取出导入lfsr,lfsr产生随机数,并将该种子存储于环形存储空间;(2)当函数返回时,将当前lfsr的状态作为种子存储于独立存储空间,并从环形存储空间检索到对应的种子,导入lfsr用于恢复存储地址;(3)当继续函数返回时,将不再备份lfsr种子,直接从环形存储空间检索对应的种子,导入lfsr用于恢复存储地址;当后续操作是函数调用时,从独立存储空间取出种子,lfsr继续产生随机数。总之,产生新的随机数时,需要独立存储空间的种子;还原存储地址时,需要环形存储空间的种子。
[0071]
本实施例提到的函数调用的变化包括:(1)获取返回地址,(2)根据秘密共享生成份额,(3)获取各个份额的地址,(4)完成影子栈存储。
[0072]
(1)获取返回地址;
[0073]
影子栈内的内容就是返回地址的副本。获取返回地址的方法是:在函数调用指令前,插入若干指令,包括获取返回地址指令;利用获取返回地址指令与函数调用指令地址的偏移量,计算得到返回地址。设获取返回地址指令为x,函数调用指令地址为y,返回地址为z,而存在关系z=y+函数调用指令的长度,同时由于在函数调用指令前插入指令的长度是可知的,即x-y=可知的长度(用size表示),所以存在关系z=x+size+函数调用指令的长度。
[0074]
(2)根据秘密共享生成份额;
[0075]
本实施例将影子栈作为秘密,采用(k,n)门限方案实现秘密分割,将返回地址分拆成若干片段。具体方法如下:构造一个t-1次拉格朗日插值多项式f(x),f(x)=a0+a1x+

+a
t-1
x
t-1
,f(x)为返回地址。计算f(1),f(2),f(3),...,f(n),这样可以得到n个份额[i,f(i)]。只有同时获得t个及以上份额,才能还原出该多项式。利用秘密共享机制可以进一步提高影子栈的安全性:攻击者仅仅篡改少量份额,并不能对返回地址进行篡改。
[0076]
为防止攻击者对少量份额进行蓄意篡改,从而阻止系统正确还原影子栈,本实施例增加份额完整性验证操作。具体方法如下:构造一个t-1次拉格朗日插值多项式f

(x),f

(x)=a0+a1x+

+a
t-1
x
t-1
。计算f

(1),f

(2),f

(3),...,f

(n),这样可以得到n个份额[i,f

(i)]。这样最终份额的格式为[i,f(i),f

(i)]。
[0077]
需要说明的是:两个拉格朗日插值公式的多项式系数可以相等或者不相等(除常数项外)。如果生成两个完全不同的拉格朗日插值公式,这意味着要进行一次额外的运算,并且在还原的时候仍然需要进行一次额外的还原运算。但是,如果两个系数大多数相同的拉格朗日插值公式分别来生成返回地址,以及其校验值的秘密份额,会导致安全性下降。本实施例认为:可以由用户自由选择,在软件重新编译时由用户根据参数来设置,由其在安全性和性能之间进行平衡。
[0078]
(3)获取各个份额的地址;
[0079]
当影子栈内容通过秘密共享完成份额生成后,开始进行份额地址分配工作,利用上面地址生成的方法,得到n个地址,将n个地址依次分配给n个份额。
[0080]
(4)完成影子栈存储;
[0081]
获取每个份额的存储地址后,根据其确定地址将份额放置于指定位置处。
[0082]
本实施例的函数返回的变化包括:(1)获取各个份额的地址;(2)获取份额,(3)还原影子栈内容,(4)检验返回地址完整性。
[0083]
(1)获取各个份额的地址;
[0084]
根据正常栈中的返回地址,从环形存储空间检索到lfsr种子。因为只要函数调用过程和函数返回过程中输入的种子相同,lfsr所产生输出结果也相同。这样就可以通过一个种子还原得到各个份额的地址。获取返回地址的方法:采用基于处理器lbr特性,在查询种子空间内容时,首先获取当前控制转移的来源地址,即可获知返回地址。
[0085]
(2)获取份额;
[0086]
把每个地址信息当作获取相应份额的标识,根据标识定位到堆中相应位置,获取相应的份额。
[0087]
(3)还原影子栈内容;
[0088]
进行份额完整性验证找到可还原份额,将还原份额代入多项式还原出真实的返回地址。获取份额后,首先进行份额完整性验证操作。验证过程如下:从n个份额中任意抽取t个份额,分别进行f(x)和f

(x)的还原工作,通过判断f(0)和f

(0)的数量关系验证所选t个份额中是否有被篡改的份额。
[0089]
f(x)还原过程如下:如图4(a)所示,将t个份额[i,f(i),f

(i)]中的t个f(i)代入f(x)=a0+a1x+

+a
t-1
x
t-1
中,得到f(x)中的每一个系数及f(0)的值。f

(x)还原工作如下:如图4(b)所示,将t个份额[i,f(i),f

(i)]中的t个f

(x)代入f

(x)=a0+a1x+

+a
t-1
x
t-1
中,得到f

(x)中的每一个系数及f

(0)的值。系统通过判断f(0)和f

(0)的数量关系,分情况进行
相应操作。分两种情况:若f

(0)是f(0)的平方,则认为所选t个份额均未被篡改,所还原的f(x)和f

(x)均是正确的。系统将直接根据f(x)还原出正确的影子栈内容。若f

(0)不是f(0)的平方,则认为所选t个份额中存在被篡改的份额。由于从n个份额中任意选取t个份额一共有c
nt
个选择,此时系统会从剩下的个选择中依次进行验证,直到找到符合还原标准的选择为止。需要注意的是,当验证次数达到次时,则说明这n个份额中至少有n-t+1个份额被篡改,可还原份额总数少于t个。此时认为影子栈已被攻击者攻破,程序将会停止运行并报警。
[0090]
(4)检验返回地址完整性;
[0091]
根据还原出的影子栈内容,获得返回地址并验证该返回地址的完整性。若还原得到的返回地址与正常栈中的返回地址一致,则认为正常栈上的返回地址未被篡改,函数开始运行。如果不一致,则认为正常栈上的返回地址被修改,程序将异常退出。
[0092]
为了实现上述函数调用和函数返回流程,本实施例采用程序插桩的方法,在软件代码中植入相应的代码片段。一部分代码插入在函数调用前,用于获取随机数并映射为影子栈的地址,并存储影子栈。一部分代码插入在函数返回后,用于获取影子栈的地址,并根据影子栈检测返回地址的完整性。
[0093]
本发明通过影子栈地址随机化和影子栈内容分散存储对影子栈实现双重保护,具体是每次函数调用时都会产生随机变化的影子栈存储地址,并将影子栈的内容分成若干份额分散存储,将各份额存储到存储空间的多个位置,使影子栈本身的安全得到了增强,攻击者难以直接针对影子栈实施攻击,提高基于影子栈防御栈溢出攻击的可靠性。
[0094]
与上述基于动态影子栈的栈缓冲区溢出攻击防御方法相应地,本实施例还提出一种基于动态影子栈的栈缓冲区溢出攻击防御系统,包括;
[0095]
存储空间分配模块,用于在进程地址空间,申请足够大的存储空间,用来存放影子栈内容;
[0096]
影子栈内容分割模块,用于采用秘密共享的方法,将影子栈的内容分割成若干份额分散存储;
[0097]
地址发生器模块,用于基于软件自身产生的随机因子,并基于移位反馈寄存器生成随机数流,在每次函数调用的时候,生成随机变化的影子栈的存储地址,即生成随机变化的影子栈份额的存储地址,将各份额分散存储到存储空间的指定位置。
[0098]
本领域普通技术人员可以理解:实现上述方法实施例的全部或部分步骤可以通过程序指令相关的硬件来完成,前述的程序可以存储在计算机可读取的存储介质中,该程序在执行时,执行包括上述方法实施例的步骤;而前述的存储介质包括:rom、ram、磁碟或者光盘等各种可以存储程序代码的介质中。
[0099]
最后需要说明的是:以上所述仅为本发明的较佳实施例,仅用于说明本发明的技术方案,并非用于限定本发明的保护范围。凡在本发明的精神和原则之内所做的任何修改、等同替换、改进等,均包含在本发明的保护范围内。
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1