通过实施数据流完整性来保护软件的制作方法

文档序号:6455289阅读:219来源:国知局
专利名称:通过实施数据流完整性来保护软件的制作方法
技术领域
本说明书一般涉及计算机编程领域。更具体地,本说明书涉及对于针对计算机应用程序或系统组件的攻击的保护;它具体涉及但不限于通过实施数据流完整性来保护软件。它还涉及通过发现编程错误(即使这样的错误未被攻击利用)而使得软件更为可靠。
背景
大多数软件以诸如C和0++的不安全语言编写,在这些语言中缓冲区溢出、格式串具易受攻击性以及其他易受攻击性存在,且可能被攻击者利用,使得安全或隐私侵犯成为可能。即使以安全类型的语言编写的程序也具有以不安全语言编写的库和运行时环境。从而,当前的软件易于受到各种攻击,且在可预见的将来它仍可能保持易受攻击。
大多数这样的软件攻击利用软件易受攻击性或缺陷将数据写到非预期的位置中。例如,控制数据攻击利用缓冲区溢出或其他易受攻击性来重写栈中的返回地址、函数指针或某些其他控制数据。非控制数据攻击利用类似的易受攻击性来重写安全关键数据而不会破坏程序中的预期控制流。非控制数据攻击被认为与控制数据攻击相比较不频繁,但它们同等严重,且在目前没有已知的针对它们的良好防范。
以往,建议使用防止软件攻击的C的存储器安全的语言分支。然而,这些方法要求现有的C代码被传入到这些分支中,这并非是平凡的任务,且会发生对C运行时环境的显著改变。
其他已知的方法可应用于现有的程序,但这些方法一般不针对非控制数据攻击进行防范和/或具有假阳性并在没有硬件支持的情况下导致非常高的开销。
概述下面提供本发明的简化概要以便为读者提供基本的理解。本概要不是本发明的详尽概观,并且既不标识本发明的关键/决定性要素也不描绘本发明的范围。其唯一目的是以简化形式提供在此公开一些概念作为稍后提供的更详细描述的序言。
大多数软件攻击利用软件易受攻击性或缺陷将数据编写到非预期的位置中。例如,控制数据攻击利用缓冲区溢出或其他易受攻击性来重写栈中的返回地址、函数指针或某些其他控制数据。非控制数据攻击利用类似的易受攻击性来重写安全关键数据而不会破坏程序中的预期控制流。描述用于针对控制数据和非控制数据攻击两者来保护软件的方法。执行静态分析以确定软件程序的数据流信息。形成数据流跟踪指令以便在该软件的执行或仿真期间跟踪数据流。而且,形成检查指令以针对静态分析结果检査所跟踪的数据流并由此标识潜在的攻击或错误。描述可任选的优化来减少产生的额外开销。
本示例提供一种方法,包括以下步骤
提供对软件程序的静态分析的结果的访问,静态分析结果包括数据流信
息;
形成数据流跟踪指令以便跟踪软件内的数据流;以及
形成检查指令以针对静态分析结果检查所跟踪的数据流并由此标识软件程序中的潜在缺陷。
通过以此方式实施数据流完整性,我们能够检测控制数据攻击和非控制数据攻击两者。与此相对,设法实施控制流完整性的己知方法仅能针对控制数据攻击进行保护。术语"软件程序中的缺陷"用来指示软件程序上的潜在攻击和/或软件程序中的编程错误。
提供了一种相应的装置,包括
被安排成访问对软件程序的静态分析的结果的输入,静态分析结果包括数据流信息;
被安排成形成数据流跟踪指令以便跟踪软件内的数据流的处理器;以及
被安排成形成检查指令以针对静态分析结果检査所跟踪的数据流并由此标识软件程序中潜在的缺陷的处理器。
在另一示例中,提供由软件程序的最终用户执行的、被安排成实施数据流完整性的方法。例如, 一种方法包括
访问软件程序的静态分析的结果,静态分析结果包括数据流信息;
在软件程序执行或仿真期间跟踪该软件程序中的数据流;以及
针对静态分析结果检査所跟踪的数据流,并作出是否找到失配的指示。
提供了相应的装置。
一种装置,包括
被安排成访问对软件程序的静态分析的结果的输入,静态分析结果包括 数据流信息;
被安排成在软件程序执行或仿真期间跟踪该软件程序中的数据流的处
理器;以及
其中,该处理器还被安排成针对静态分析结果检查所跟踪的数据流,并 作出是否找到失配的指示。
优选地,数据流跟踪指令和检査指令使用修改的编译器或二进制重写工具 中的任何一个来添加到软件。在另一示例中,这些指令使用被安排成仿真执行 软件程序的处理器的机器仿真程序以及机器仿真程序的硬件等价物中的任何 一个实现。
优选地,潜在的攻击包括控制数据攻击和非控制数据攻击两者。
例如,针对由软件程序中的指令读取的每一值,静态分析结果包括来自该 软件程序的可写该值的指令集合。
在某些实施例中,该方法还包括通过根据软件程序的源代码计算到达定义 (reaching definitions)来执行软件程序的静态分析。
优选地,数据流跟踪指令被安排成维护标识符和存储器位置之间的映射, 标识符是对写到每一存储器位置的最后一条指令的标识符。
例如,检查指令操纵(instrument)读指令。
优选地,操纵读指令以检查写正在读取值的指令的标识符是否是静态分析 所计算出的相关联集合中的元素。
在一实施例中,该方法还包括确定指令的等价类,并向同一类中的所有指 令分派同一标识符。
在一示例中,该方法包括基于第二静态分析移除某些数据流跟踪指令和某些检查指令。
示例提供了一种包括计算机程序代码装置的计算机程序,该计算机程序代 码装置适于当所述程序在计算机上运行时执行上述任何方法的所有步骤。例 如,该计算机程序被包含在计算机可读介质上。
对于装置示例
装置例如包括被安排成通过根据软件程序的源代码计算到达定义来执行 软件程序的静态分析的编译器。
优选地,处理器被安排成形成数据流跟踪指令以维护标识符和存储器位置 之间的映射,标识符是对写到每一存储器位置的最后一条指令的标识符。
优选地,处理器被安排成添加检查指令,使得它们操纵读指令。
优选地,处理器被安排成对每一读指令形成检查指令,以检査写正在读取 值的指令的标识符是否是静态分析所计算出的相关联集合中的元素。
该方法可由存储介质上机器可读形式的软件执行。软件可适于在并行处理 器或串行处理器上执行以使得各方法步骤可按任何合适的次序或同时执行。
这确认了软件可以是有价值的、可单独交易的商品。其旨在涵盖运行于或 者控制"哑(dumb)"或者标准硬件以实现期望功能的软件,(因此,软件在本 质上定义了软件安全系统的功能,并且从而可被称为软件安全系统,即使是在 它与其标准硬件结合之前)。出于类似的理由,它还旨在包含例如用于设计硅
芯片,或者用于配置通用可编程芯片的HDL (硬件描述语言)软件等"描述"
或者定义硬件配置以实现期望功能的软件。为出于避免疑义的目的,本发明涵 盖其中以硬件而非软件实现本系统任何部分的实现。
许多附带特征将随着参考下面的详细描述并结合附图进行理解而得到更 好的认识。


结合附图阅读以下详细描述,将更好地理解本说明书,在附图中:
图1是计算机所使用的存储器的示意图lb是栈的一部分的示意图1C是计算机存储器的一部分的示意图;图2是保护软件的方法的流程图; 图3是静态分析的方法的流程图4是维护映射数据结构以及检查软件程序完整性的方法的流程图5是用于保护软件的经修改编译器的示意图6是用于保护软件的二进制重写工具的示意图7是用于保护软件的机器仿真程序的示意图8是使用图7的用于保护软件的机器仿真程序的方法的流程附图中使用相同的附图标记来指代相同的部分。
详细描述
下面结合附图提供的详细描述旨在作为对本示例的描述,而非表示用于解 释或利用本示例的唯一形式。本说明书阐述本示例的功能以及用于构造和操作 本示例的步骤序列。然而,相同或等价的功能与序列可由不同的示例来实现。
认识到需要针对控制数据和非控制数据攻击两者来保护软件。提供通过实 施数据流完整性进行操作而实现保护的方法。为了更好地理解控制数据攻击和 非控制数据攻击,参考图l、 lb和lc讨论一示例。
图1是包括操作系统专用存储器1、用户存储器B和可用存储器A (如计 算机处理器所见)的计算机存储器的一部分的示意图。操作系统存储器l不可 用于除计算机的操作系统以外的任何其他用途。用户存储器可用于由用户安装 的软件应用程序,例如在图1的示例中可为此目的占用存储器中的某些块(见 块C)。在图1的下部更详细示出了可用存储器A。它包括按照箭头方向填充 且可被认为是计算机的工作存储器的栈2和堆3。计算机存储器可被认为示意 性地使用"空穴"(pigeonhole)存储器模型,由此各信息项目被存储在各存储 单元中,其各自具有唯一的地址或标识符。例如,图lc示出了两行这样的空 穴。
现在考虑以下给出的用计算机编程语言C编写的示例代码片段。 1: int authenticated = 0; 2: char packet [1000];4: while (! authenticated) { 5: PacketRead(packet》 6:
7: if (Authenticate(packet)) 8: authenticated = 1;
9:} 10:
11: if (authenticated)
12: ProcessPacket(packet);
这包括编号为1到12的12行代码。在行1中,定义整数变量,并设置为 值0。在行2中,设置称为packet (分组)的1000字节的字符值数组,且分派 IOOO字节的存储器供该数组使用。While循环然后在行4开始,并继续直到 行9。 While循环的条件是变量authenticated (经认证)的值是否为0。此时, authenticated的值为0,在行1中刚刚被设置为0。继续前进到行5,并对自变 量packet执行函数PacketRead (分组读取)。该函数将一 packet读入到已在行 2中定义的数组packet。在行7,调用函数Authenticate (认证)来检査所读入 的packet,且如果结果成功,则将变量authenticated的值置为1 (见行8)。在 行11和12,然后使用函数ProcessPacket (处理分组)来处理经认证的packet。 然而,假定函数PacketRead能够对packet写1000以上的字节,正如在诸如C 和。++等众多计算机语言的情况。可利用该易受攻击性来重写函数PacketRead 的返回地址或重写authenticated这现在将参考图1和lb来说明。假定以上包 括12行代码的软件应用程序存储在图1的用户存储器B中,并假设调用函数 PacketRead的行5被存储在D。该行调用函数PacketRead,该函数例如被存储 在E。作为调用E处的PacketRead的结果,读入一 packet,并存储在栈上,如 图lb中的4,带有之前存储在栈上5处的变量authenticated的值,以及存储在 栈上6处的返回地址(D的地址,即调用PacketRead且控制流所返回的指令的 地址)的值R。如果所读入的packet大于1000字节,则它可重写图lb中的存 储单元5和6。通过重写返回地址R,攻击者能够阻止程序返回到行5并继续 程序。攻击者可将程序的控制流转向到存储器的其他部分,并因此破坏安全性和/或隐私。这是可允许攻击者获得对执行的控制的控制数据攻击的一种形式。
通过重写变量authenticated的值,攻击者能够使while循环停止迭代,以代替 迭代直到认证成功。攻击者然后能够使其packet在没有正确认证的情况下得到 处理。这是非控制数据攻击的一种形式。
如上所述,我们的方法包括实施数据流完整性。在高层,我们的方法包括 计算或访问预先计算的静态数据流(见图2的框10)并运行程序操纵过程(见 图2的框12),以便操纵程序(向其添加指令)来跟踪数据流和检査该动态数 据流的完整性。该操纵包括添加数据流跟踪指令来跟踪动态数据流。它还包括 添加检査指令来针对静态数据流信息检査动态数据流。然后可使所操纵的程序 运行,且如果数据流完整性被侵犯,则会发生异常、警报或停止工作。
静态分析包括针对给定软件程序确定数据流信息。例如,对每一程序位置 (诸如以上代码示例中的行号),静态分析计算可能在所关注的程序位置写值 的程序位置的集合。因此例如,程序位置行4包括变量authenticated。该变量 可在行1或行8写入。因此,对行4的静态分析结果包括集合{1,8},对行ll 则还包括{1,8}。在简化的示例中,程序位置的集合以此方式对每一行号产生以 给出完整的静态分析的结果。更一般地,静态分析可被表述为对由指令读取的 每一值计算可写该值的指令集合。如将在以下更详细描述地,静态分析结果集 合在特定的示例中与编译器对程序的中伺表示中的指令而非源代码行号相关 联。为清楚起见,在以上引用源代码行号。
可使用计算这些值的集合的任何合适的方法。例如,可使用如由Aho、Sethi 和Ullman在1986年Addison Wesley出版社ISBN 0-201-10194-7的"Compilers: Principles, Techniques and Tools (编译器原理、技术和工具),,中所述的到达 定义分析。如由Aho等人展示的到达分析定义介绍了以下语言。写存储单元的 指令定义该存储单元中的值,而从存储单元中读值的指令被称为使用该值。分 析对每一使用计算到达定义的集合。它向每一定义分派标识符,并返回每一使
用的到达定义标识符的集合。
例如,在行4和11使用了 authenticated如果在源代码中运行到达定义分 析,则可能得出行1和8中的定义到达两个使用的结论。因此,如果使用行号 来标识定义,则这两个使用的到达定义标识符的集合将是{1,8}。分析可能不是精确的,但分析是保守的,这一点是重要的。它必须在集合 中包括在运行时可能到达使用的所有定义,但它可包括其他定义。例如,仅行 8中的定义可能到达行11中的使用,但分析可计算到达定义集合{1,8}。确保 数据流完整性实施不具有假阳性是重要的。
如上所述,(例如,通过添加指令来)操纵要保护的程序以跟踪数据流并 检查所跟踪的数据流是否遵循静态分析结果。添加指令的过程,此处称为操纵, 是使用经修改的编译器、二进制重写工具、机器仿真程序、专用硬件中的任一 项或按照任何其他合适的方式实现的。换言之,操纵程序以在运行时计算到达 每个读取的定义并计算该定义是否位于静态计算的到达定义标识符的集合中。 为了在运行时计算到达定义,维护记录写到每一存储单元的最后一条指令的标
识符的运行时定义表(RDT)。操纵每个写以更新RDT。读取之前的操纵使用 正在读取存储单元的地址来从RDT检索标识符。然后,它检査该标识符是否 位于静态计算的集合中。例如,在以上的C代码片段示例中,将添加代码以在 行8中将RDT[&authenticated]置为8, 并检査在行4和11中 RDT[&authenticated]e {1,8}是否成立。
现在将参考图lc对此进行示意性地说明。假定到达以上示例程序的行8, 且变量authenticated的值被置为1。这包括向存储器写入一值以表示该变量的 值。例如,如图lc所示,向存储单元101写X。假定形成相关存储器,其由 图lc的空穴存储器的下面一行指示。在与存储单元101相关联的存储单元102 中,存储关于程序位置行8的信息,在那里出现写变量authenticated的指令。 存储在相关存储器中的这种信息是数据流跟踪信息的示例。为了在相关存储器 中存储这一信息,将数据流跟踪指令添加到示例程序。
在程序执行中稍后到达行11。在此行中,给出读取变量authenticated的值 的指令。在进行该读取之前,对相关存储器中的存储单元102处的入口是否与 静态数据流信息一致作出检査。该检査是使用添加到程序的检査指令来作出 的。如上所述,对每一程序位置(例如,行),静态数据流信息包括可能写入 当前行上的值的行号的集合。在行11的情况中,值的集合为{1,8},如图lc 所示。这些行号1、 8是以下更详细描述的到达定义标识符的示例。因为存储 单元102 (对应于存储单元101)中的入口包含作为该集合的成员的值,因此在这种情况中不需要作出任何指示。然而,如果发生了攻击,则如上所述,(例
如)变量authenticated的值和/或PacketRead的返回地址可能被重写了 。
在这种情况中,相关存储单元102中的值是z而非如图lc的8。这是因 为,在z行,当重写变量authenticated时,PacketRead函数写存储单元101 。 从而在行ll处或在行ll之前,发生异常,因为值z不是适当的静态分析结果 集{1,8}的成员。通过重写PacketRead的返回地址,程序执行不会在计算行5 之后返回到行6。然而,因为在程序读返回地址之前,以类似于以上对变量 authenticated所述的方式在相关存储器中作出检査,所以检测到该错误。该检 査标记错误,因为从中读返回地址的程序位置在对应于返回地址的相关存储单 元中具有值z。如下所述,该相关存储单元在函数入口上被置为零,并在函数 返回时添加它是否为O的检查。当攻击重写返回地址时该检查失败,因为相关 联的相关存储器具有值z。
因此,在图lc中,由空穴下面一行表示的'相关'存储器是如上所述的运行 时定义表(RDT)的一部分的示例。
为了在存在可在任何地方写入且甚至可能执行数据的强大攻击者的情况 下实施数据流完整性,要防止篡改RDT、篡改代码或绕开操纵。这以任何合适 的方式实现。
例如,通过操纵写来检查目标地址是否位于分派给RDT的存储器区域之 内来防止RDT篡改。写RDT的任何尝试会生成异常。在另一示例中,用同样 的检查或通过使用对代码页的只读保护来防止代码篡改。
保护RDT的另一方法包括修改程序,以使得所写的地址中没有一个可引 用包含RDT的存储器。例如,如果在地址4000000h保存RDT且没有其他程 序可访问数据结构在4000000h上方,则可通过使要由程序写的每一地址与 3FFFFFFh进行逻辑AND来保护RDT。
为了防止攻击者绕开操纵,要防止篡改间接控制流转移的目标地址。如前 所述,操纵对程序员定义的控制数据的写和读。此外,以相同方式操纵对由编 译器添加的控制数据的写和读。例如,将返回地址的RDT入口设置为公知的 值,检查该入口在返回时是否仍保持该值。
相对于未受操纵的软件增加了开销。例如,对向存储器的每一写入,发生对相关存储器的另一写入(图lc)。然而,有可能为较低的开销而对覆盖面进行折衷。如果数据流图没有为某些使用指定到达定义,则可能不会检测出某些攻击,因为这些使用未被操纵,但开销会降低。如果操纵了对控制数据的使用,则仍能确保数据流完整性。在示例中,我们的方法仅操纵对局部变量的使用而没有操纵函数外的定义和控制数据的使用。该示例是有意思的,因为它具有低开销且它仍能捕捉众多有意思的攻击。例如,它可防止侵犯控制流完整性的任何攻击和上述非控制数据攻击示例。
当所操纵的程序运行时,对静态计算出的数据流图的任何偏离会造成异常。由于分析是保守的,不存在任何假阳性。如果有异常,则程序具有可能是潜在攻击或编程错误的错误。
图4是维护映射数据结构以及检查软件程序完整性的方法的流程图。该流程图的上半部涉及完整性检查过程,下半部涉及现在将说明的数据流跟踪过
程。执行要保护的软件程序在位置l处的指令(见框30)。对在程序位置l读取并在位置a存储(见框31)的每个变量v,作出查看相应的相关存储器中的入口 da是否是在对适当程序位置的静态分析期间确定的程序位置的集合的成员(见框32)。如果找到矛盾,则信令错误(见框33)。可用任何合适的方式信令该错误。例如,发生异常,则终止过程或写调试日志。如果l处的每一变量成功地通过了检查(见结束框34),则继续数据跟踪过程。对在程序位置1处所写的每个存储器或寄存器地址a,这包括将适当的相关存储单元的内容更新为l (见框36,将da更新为l)。"结束"过程(见框37)完成该过程。
如上所述,可使用经修改的编译器或二进制重写工具来实现操纵。也有可能使用机器仿真程序或机器仿真程序的硬件等价物。现在将参考图5到8更详细地描述这些示例。在这些示例的每一个中,编译器、二进制重写工具、机器仿真程序或硬件等价物是使用合适的计算机以及本领域中己知的合适的操作系统软件一起实现的。
图5是用于保护软件的经修改编译器的示意图。要保护的软件程序的源代码50被输入到执行静态分析并计算静态数据流的过程中(见框51)。来自静态分析的关于数据流的这一信息被提供给具有相关联处理器54的经修改编译器52。该经修改编译器每当写寄存器或存储器地址a时即发出代码。该代码维护一数据结构,其将每一寄存器或存储器地址a映射到在动态执行中写该寄存
器或存储器单元的当前值的程序位置的标识符da (如上所述)。此外,编译器 每当存储在存储器或寄存器地址a的变量v在某一地址1使用时即发出检査数 据流完整性的代码。该代码检査da是否是Slv中的位置中的一个。它在该数据 结构中查找值da,并检査它是否在Slv中。Slv中位置的标识符可被嵌入代码 中,或者可在将变量和位置映射到位置集合的表中査找它们。第一种方法能够 产生特别好的性能,且阻止攻击者重写表。在任一情况中,如果检查失败,则 发出的代码例如通过发生异常来信令错误。
经修改编译器的输出是具有集成的数据流完整性功能的可执行软件53。 以此方式,可按照简化且有效的方式向现有源代码提供数据流完整性功能。
图6是用于保护软件的二进制重写工具62的示意图。这采用可执行代码 60作为输入,并能够访问如上所述的静态分析的结果61。在此示例中,使用 静态数据流信息61来驱动二进制重写工具62发出检查动态数据流完整性的代 码,如上对基于编译器的实现所述。以此方式,有可能对以往释放的二进制代 码添加数据流完整性功能。二进制重写工具62的输出包括经修改的可执行代 码63。
图7是用于保护软件的机器仿真程序70的示意图。这采用静态数据流信 息61作为输入,以及可执行代码60用于保护。机器仿真程序70仿真软件执 行,并发生错误或异常71。可使用该方法向之前释放的二进制代码添加检测。 如现在将参考图8所述,机器仿真程序维护两个数据结构80、 81。第一数据结 构80即映射1,将每一存储器和寄存器地址a映射到标识符da,且每当它仿 真写寄存器或存储器的CPU指令时它都更新该数据结构(见框82)。该机器 仿真程序也保存第二数据结构81 (映射2),这将变量的使用映射到所确定的 静态分析可写所使用的值的集合Slv或位置。当仿真程序处理位置1处的读存 储在寄存器或存储器地址a处的变量v的指令时,它在映射2中查找Slv,并 在第一数据结构即映射1中查找最后一次写该变量的位置da (见框83)。如 果da不在Slv中(见框84),则仿真程序例如通过调用适当的错误句柄来信 令错误(见框85)。
在另一实施例中,数据流完整性功能是使用与上述用于机器仿真程序相同的技术或任何其他合适的技术以硬件实现的。该方法允许数据流完整性功能被 添加到之前释放的二进制代码。
通过使用参考图5到8描述的任何方法,可保护软件程序免受攻击。不必 使用特定的安全语言分支来导入旧的应用程序或写新的应用程序。也能够保护 程序中的所有数据流而非仅仅诸如函数指针等程序的特定部分。能够针对不改 变目标程序的控制流的攻击成功。有利地,我们的方法的特定实施例不会生成 假阳性。
现在给出关于静态分析的更多细节。 静态分析
如上所述,静态分析包括数据流信息,诸如对由指令读取的每一值包括关 于可写该值的指令的集合的信息。图3是静态分析的示例方法的流程图。
对每个程序位置1(见框20),以及在该程序位置l使用的每个变量v(见 框21),获取关于程序位置Slv的集合的信息。例如,可能写在l处使用的值 v的程序位置Slv的集合。在某些示例中,使用关于程序位置的类的信息或关 于程序位置的其他信息,如将在以下更详细描述地。在图3中为完整起见示出 两个"结束"语句(见框23和24)。
如上所述,计算关于程序位置的集合的信息的步骤可包括使用到达定义分 析。在特定示例中,使用两种分析的组合流程敏感的过程内分析以及流程不
敏感且上下文不敏感的过程间分析。
过程内分析考虑流程控制。在一示例中,它可通过遍历如Appel,A.W.在 1998年剑桥大学出版社的"Modern Compiler Implementation in Java (使用Java 的现代编译器实现)"中所述的静态单个分派表示来实现。使用这种分析来为 在声明它们的函数之外没有定义的局部变量的使用计算到达定义。使用过程间 分析来对所有其他使用计算到达定义。
过程间分析较不精确,以允许它放大到大型程序。它忽略控制流,且在分 析函数时它不考虑进行调用的上下文。实现指向分析来计算每一指针可指向的 对象的集合,且使用这些指向集合来计算到达定义。在1994年哥本哈根大学 Andersen, L.的博士论文"Program Analysis and Specialisation for the C Programming Language (C编程语言的程序分析和范围限定)"中描述了指向分析。然而,不必使用指向分析,可使用计算每一指针可指向的对象的集合的任 何合适的方法。
例如,指向分析是域不敏感的而非基于域的(即,它不在结构、联合或类 中的不同域之间进行区分)。这种分析对所有源文件进行全局遍历以收集子集 约束。每一分派x-;;导致子集约束;c2"这意味着x的可能值的集合包含y的
可能值的集合。该分析使用编译器将每一源文件编译成高层中间表示(HIR), 且它将HIR中的所有子集约束写到文件。在该全局遍历之后,它通过迭代所有 约束直到到达固定点来计算指向集合。然而,它将该指向集合存储在文件中。
在全局遍历期间,也收集写可能在其他函数中读取的位置的指令的标识 符。这包括写入由解除引用(dereferencing)指针获取的位置、静态和全局变 量以及取得地址的本地变量。该信息也被写到文件中。
使用指向集合和在全局遍历期间收集到的分派来计算过程间到达定义。对 变量和临时变量的使用,到达定义的集合是包含对该变量(或临时变量)的所 有分派的标识符的集合与包含对可指向该变量(或临时变量)的解除引用或指 针的所有分派的标识符的集合的并集。对指针解除引用,到达定义的集合是包 含对该解除引用的指针的所有分派的标识符的集合与该指针可指向的所有变 量的到达定义的集合的并集。过程间到达定义的集合被写到用来操纵程序的文 件中。
在该示例中,过程内和过程间分析均假设未定义存储器中无关对象的相对 布局。它们假设正确的程序不会使用指针运算在存储器中无关对象之间导航。
现有的编译器在实现若干标准优化时也采用这种假设。从而,这种假设适 用于绝大多数程序。然而,正是这种假设受到众多攻击的侵犯。数据流完整性 实施检测并防止这些攻击。
现在给出关于向软件添加数据流跟踪指令和数据流检査指令的过程的更 多细节。
操纵
在特定示例中,通过将新的高级指令插入程序的高级中间表示来添加操 纵。例如,指令可具有以下形式 SETDEF opnd idCHECKDEF opnd setName.
注意到,不必对指令使用这种形式。可使用用于实现跟踪数据流和检查数 据流完整性的功能的任何合适形式的指令。
第一指令将叩nd (操作数)的RDT入口设置为id。第二指令从RDT检 索opnd的运行时定义标识符,并检查该标识符是否位于具有名称setName的 到达定义集合中。编译器或其他合适的实体维护从集合名到集合值的映射,该 映射在将CHECKDEF指令降低为目标机器的汇编语言时使用。操纵代码的高 级表示具有使操纵机器与源语言无关以及与目标体系结构大部分无关的优点。
以下是以上给出的示例代码以及从由到达定义分析计算出的信息生成的 高级操纵的示例HIR。名称为100的集合具有值{1,8}。在该示例中,不操纵可 确保被分派给寄存器的临时变量,且也不操纵对&packet的使用,因为局部变 量的地址是通过向帧指针添加常量来计算的。 SETDEF—authenticated 1 —authenticated = ASSIGN 0 # 1
$L6:CHECKDEF —authenticated 100
t274 = CMP(NE) —authenticated, 0 #2 CBRANCH(NE) t274,$L7, $L8 #3 $L8:tv275 = CALL &—PacketRead, &_packet #4 t276 = CALL &_Authenticate, &_packet #5 t277 = CMP(NE) t276, 0 #6 CBRANCH(NE) t277,$L10, $L9 #7 $L 10: SETDEF —authenticated 8
—authenticated = ASSIGN 1 #8 $L9: GOTO $L6 #9
$L7:CHECKDEF —authenticated 100
t278 = CMP(NE) —authenticated, 0 #10 CBRANCH(NE) t278,$L12, $L11 #11 $112: tv279 = CALL &—ProcessPacket, &_packet #12 $L11:在描述任何将高级操纵降低为汇编语言之前,描述如何实现RDT。为了
允许高效的访问,在一个示例中将RDT实现为具有所操纵程序中的每一 32位 的存储器字的定义标识符的数组。每一定义标识符例如是二字节长的。这导致 约50%的空间开销。
能够对每一 32位字记录单个标识符,因为可生成其中没有具有不同到达 定义集合的两个变量共享相同对齐的32位存储器字的代码。由于我们的到达 定义分析不需在对象中的不同域中间以及数组中的不同元素之间进行区分,因 此没有必要改变数组和对象的布局。仅改变编译器以便在栈帧中布局局部变量 时使用32位的最小对齐。适当对齐函数自变量、全局变量以及堆分派对象。
在特定示例中,向正在操纵的程序分派最低1GB的虚拟地址空间,并向 RDT分派512MB的虚拟地址空间,它们之间具有保护页,即保护页在地址 40000000h处,且RDT的基是在地址40001000h处的。因此为了计算操作数的 RDT入口的地址,仅将操作数的地址右移两位,将结果乘以2,并加上 40001000h。该布局也允许对写的目标地址的高效边界检查以防止篡改RDT。 在一个实现中,如果目标地扯与c0000000h的逐位AND为非零,则发生异常。 在另一实现中,使用位掩模来防止应用程序写RDT。例如,可用3fffffffh来对 目标地址掩模以防止其引用RDT。保护页允许仅检查写的目标地址并忽略大
高级操纵可降低为x86汇编语言,如以下示例所述。将SETDEF authenticated 1降低为
lea ecx, [_authenticated] test ecx, 0C0000000h je L int 3 L: shr ecx,2
movword ptr [ecx*2+4000 lOOOh], 1 第一指令将写的目标地址加载到ecx,而随后的三条指令对地址执行边界 检查。如果检査失败,则当前生成断点(int3),这对于调试是非常方便的。另一异常可能更适合生产使用。使用shr指令来计算—authenticated的RDT入 口的地址,mov指令更新入口。如果操作数的大小大于32位,则有必要更新 RDT中对应于其他字的入口。可通过移动标识符的两个副本的拼接来用单个 mov指令更新64位操作数的入口。但对于较大的操作数,添加附加的指令。 CHEKCDEF authenticated 100指令被降低为
lea ecx, [—authenticated]
shr ecx,2
cmpword ptr [ecx*2+40001000h], 1 je L
cmpword ptr [ecx*2+40001000h],8 je L int 3
L:
该代码对RDT入口中—authenticated的定义标识符与定义标识符集合100 进行比较。当操作数大于32位时,对其他字添加附加的比较。
此外,操纵在编译过程中引入的控制数据的定义和使用。例如对函数入口, 添加以下代码以将对应于函数的返回地址的RDT入口置为零 movecx, esp shr ecx,2
cmpword ptr [ecx*2+40001000h],0 je E int 3 L ret
回到以上涉及PacketRead和Authenticated的示例代码片段,被操纵的代 码不再易于受到重写返回地址的控制数据攻击以及重写authenticated的非控制 数据攻击。由于分析得出authenticated与packet未混叠的结论,对packet的写 具有保证与1到8不同的标识符。另外,标识符零仅用于返回地址的函数入口。 从而,任何返回地址重写也将被检测出。
程序通常依赖于包括若干库的复杂的运行时环境。通常不可能分析这些库的源代码。通常,仅库可用,且即使在源代码可用时,某些函数是用汇编语言 手写的。然而,众多攻击利用库中的易受攻击性。例如,c库中的串操纵函数 对于缓冲区溢出易受攻击性而言是声名狼藉的。
以往使用源代码分析来操纵写的技术无法提供任何保证,除非包装库调用 以执行安全性检查。这些以往的技术包括数组边界检査和C的存储器安全语言 分支。有时,也要求包装以执行存储器布局转换。编写这些包装可能是麻烦的。
我们的数据流完整性实施方法可任选地不要求任何库包装。当程序调用未 被分析的库函数时,分析不能计算某些使用的到达定义集合,但是对所有其他 使用保证数据流的完整性。为此,操纵库二进制代码以便将它们所写的任何存 储器的RDT入口设置为无效定义标识符。这可在没有源代码的情况下完成。
提供定义库包装以增加覆盖面的选择。例如,定义库函数的包装,包括描 述调用函数添加到指向分析的子集约束以及编写包装函数。包装检查库读取的 存储器的定义标识符、调用库并为由库所写的存储器设置定义标识符。操纵代 码以调用包装而非原始函数,并向包装提供库函数所读的存储器的到达定义集
合以及它所写的存储器的定义标识符。例如,WindowNT TM操作系统调用 CreateProcess (创建进程)的包装可检查作为自变量提供的应用程序名和命令 行串的完整性。
以下示出了 memcpy的示例包装。CHECK—BOUNDS (检査边界)确保 memcpy不会写到RDT,且CHECK_ARRAY (检查数组)检查RDT中用于scr 中字节的标识符是否位于defArgs中提供的到达定义集合中。写到dest的字节 的RDT入口由UPDATE—RDT (更新RDT)置为defld。
void*
Dfi—memcpy(int* * defArgs, void *dest,
const void *src, size一t count)
unsigned int defld = (unsigned) defArgs [O]; CHECK—BOUNDS (dest, count); CHECK—ARRAY (defArgs,l,src,count);memcpy (dest, src, count); UPDATE—RDT(dest, count, defld); return dest;
优化
我们的数据流完整性实施方法引入某些开销每一定义引入对RDT的写, 且每一使用检查引入对RDT的读继之以针对该使用的到达定义集合中每一标 识符的比较。
开发了某些方法来减少这种开销。它们归入四种类型的方法。首先,可重 命名定义以减少CHECKDEF中比较的次数,或允许要执行的批量比较更为便 宜。其次,可使用静态分析在保证移除某些SETDEF (设置定义)和SETDEF (检查定义)不会损害数据流完整性时移除它们。最后,不同的定义可在运行 时以不同的频率到达一使用。可修改运行时系统来利用这种偏向。
重命名定义
第一种类型的优化试图重构定义标识符空间,使得可将CHECKDEF编译 成更高效的本机代码。
开发了两种技术。第一种技术按照允许对同一类中的所有定义分派同一标 识符的方式将定义分成等价类。如果两个定义具有完全相同的使用集合,则它 们是等价的。这减少了 CHECKDEF中的比较次数,以及表示标识符所需的位 数。例如,PacketRead示例中的—authenticated的两个定义具有由静态分析计算 出的同样的使用集合。对这两个定义分派同一标识符1。从而,CHECKDEF authenticated 100仅需一次比较。它被编译成-
lea ecx, [—authenticated]
shr ecx,2
cmpword ptr [ecx*2+4000 lOOOh],l je Lint 3
L:
第二种类型的方法是重命名定义,以使得可使针对标识符集合的比较更 快。利用比较可采用不同的成本以不同方式执行的事实。在一个实现中,有可 能有三种形式的比较(i)针对连续标识符O...n的范围的检査可由针对n的 单个无符号整数比较来实现,(ii)针对连续标识符n...m的范围的检查可通 过减去n并执行针对m-n的无符号比较来实现,(iii)针对单个标识符n的检 查可由普通的比较来实现。在其他实现中,存在可用各种成本执行的不同或附 加形式的检査。
将CHECKDEF的成本定义为执行它所必需的减法和比较的次数。例如, 在示例实现中,检査{1,3,7}的成本为三,但检査{0,1,2}的成本仅为一。令标 识符集合的总成本为针对其的单个CHECKDEF的成本乘以在程序中发生的针 对其的CHECKDEF的次数。在示例实现中,使用对出现次数的静态计数。本 领域的技术人员可以理解,其他实现可使用其他技术,诸如估计所执行的 CHECKDEF的次数的静态试探法,或对所执行的CHECKDEF计数或估计的 运行时反馈。
使用试探法来试图减少所有集合的总成本的总和。特定的示例使用简单的 贪婪算法按照递减的总成本的次序对集合排序,并进而将成本最高的集合分 派给连续的标识符范围。从标识符0开始,因此具有最大总成本的集合受益于 最便宜的不计较。
移除边界检查
检査写的目标地址以防止攻击者篡改RDT。可通过从可静态确定安全的 所有写移除这些检查来优化SETDEF。在特定的示例中,如果目标地址是通过 将小常量偏移量(可能为0)加到栈指针、帧指针或全局或静态变量的地址而 获得的,则写是安全的。优选但并非必需地,偏移量与正在写入的数据的大小 的总和小于4KB (这是在RDT之前所分派的保护页的大小)。
例如,在PacketRead示例中,由于—authenticated是其地±止通过将小常量 加到帧指针来获取的局部变量,因此可从SETDEF authenticatedl移除边界检 查。SETDEF被编译成lea ecx, [—authenticated] shr ecx,2
movword ptr [ecx*2+40001000h],l 移除SETDEF和CHECKDEF
另一类型的优化使用静态分析来安全地移除某些SETDEF和 CHECKDEF 。
当然,对我们所指的安全必须小心。存在两个问题。首先,旨在不依赖于 其推断在数据流完整性丢失时即不健全的高级分析操纵的整个目的是检测程 序的数据流完整性受到损害的情况。其次,旨在不在编译期间过早移除检查, 因为稍后的代码变换可能改变数据流完整性丢失的情况。从而,可任选地在 SETDEF和CHECKDEF操作仍以其HIR形式存在但程序的其余部分己经被降 低成本机指令集且已经发出时执行我们的优化。
第一种技术标识了在函数外没有定义且仅通过安全写入(根据之前章节中 对安全的定义)所写的局部变量。它用置于函数入口上、带有标识符O的单个 SETDEF替换这样的变量的所有SETDEF。它也简化这样的变量的CHECKDEF 以具有等于{0}的到达定义集合。这种优化是安全的,因为安全写不会侵犯数 据流完整性。从而,仅有一个SETDEF是必需的。
第二种技术运行数据流分析以移除SETDEF和CHECKDEF。这种分析类 似于用来计算静态数据流图的到达定义分析,但它不依赖于可被攻击者侵犯的 假设。为简单起见,将在相同的基本块内描述指令序列的情况。这种技术也处 理任意控制流图上跨不同基本块传播流变量的情况。
操纵在以下情况中是冗余的。假定指令II和12是涉及在没有对其的任何 介入写的情况下执行的同一数据的SETDEF或CHECKDEF对。
1. 如果I1和I2均为具有同样的标识符的SETDEF,则I2是冗余的。
2. 如果II和12均是SETDEF,且未介入该数据的CHECKDEF,则II 是冗余的。
3. 如果II是IDl的SETDEF, 12是包含IDl的集合的CHECKDEF,则 12是冗余的(实际上,如果数据流分析正确地执行,贝U IDl必定在12的集合中)。
4. 如果II和12分别是针对集合IDS1和IDS2的CHECKDEF,则IDS2 可被縮减以仅包含IDS1中的元素(早先的检查保证不存在任何其他元素)。 而且,如果IDS1和IDS2持有同样的元素,则12可被移除(如果早先的检査 成功,则稍后的检查不可能失败)。
5. 如果II是针对集合IDS1的CHECKDEF,而12是针对ID2的SETDEF, 则当IDS1^I2)时,12冗余。
在实践中,规则3和4是最有效的。规则3在对数据的使用在其定义附近 发生时消除了众多CHECKDEF指令。规则4令在重复使用同样的数据时移除 CHECKDEF指令,即使在数据的定义以及这些使用中的第一个使用之间存在 混叠的写。
为了标识冗余指令,使用以SEFDEF和CHECKDEF运算扩充的本机代码 的符号执行。在每一指令之后更新寄存器的符号状态,并在SETDEF和 CHECKDEF之后更新RDT的符号状态。RDT的符号状态将符号存储器地址映 射到定义标识符的集合。
在一个示例中,使用简单的测试来比较符号地址。如果两个地址在句法上 是相等的,则它们是相等的。如果它们是通过将不同的偏移量加到相同的符号 寄存器状态来计算的,则它们是不同的。否则,它们可能会引用混叠的存储单 元。对存储器的写使寄存器的符号状态无效,如果状态引用可能与写的目标混 叠的存储单元的内容。另外,它从符号RDT状态中移除可能与写的目标混叠
的任何存储器的映射。
应用这些规则来通过检查符号RDT状态消除冗余操纵。由于一次对单个 基本块工作,在指令冗余时立即移除指令——这可容易地转成块间实现中的单 独的轮次。
利用偏向的数据流
我们的最终技术基于不同的定义可在运行时以不同的频率到达一使用的 预期。例如,在以下示例中,定义D1仅在循环上的第一次迭代时到达行3: 1: intx=…;〃D1 2: for (int y = 0; y < 100; y ++)3:x++; 〃D2 4:}
可任选地添加将地址映射到最近在它们处看到的定义标识符的第二全局 表。于是,对成本大于2的针对集合的任何CHECKDEF,通过针对表中所保 存的值的比较来继续检查。如果它匹配,则保证检査成功。如果它失败,则更 新表并继续检查。
或者,可使用概况信息来对成员检查重新排序,从而首先执行对更频繁到 达使用的定义标识符的检査。概况信息可在程序的概况运行期间收集,而检査 可在编译程序时重新排序。或者,概况信息可在程序的正常运行期间收集,而 检査可在程序运行时动态重新排序。
本领域的技术人员将认识到用于存储程序指令的存储设备可分布在网络 上。例如,远程计算机可存储描述为软件的该过程的示例。本地或终端计算机 可访问远程计算机并下载该软件的一部分或全部以运行该程序。或者,本地计 算机可按需下载软件的片断,或可以在本地终端处执行一些软件指令而在远程 计算机(或计算机网络)处执行一些软件指令。本领域的技术人员将认识到,
通过使用本领域技术人员已知的常规技术,软件指令的全部或部分可由专用电 路,如DSP、可编程逻辑阵列等来执行。
如本领域的技术人员将清楚的,此处给出的任何范围或者设备值都可以被 扩展或者改变而不失去所寻求的效果。
本文中描述的各方法步骤可以在适当时按任何合适的次序或同时执行。 可以理解,上面对于较佳实施例的描述仅仅是作为示例给出的,而本领域 的技术人员可以做出各种修改。以上说明、示例和数据提供了对本发明的各示 例性实施例的结构和使用的全面描述。尽管以上带着一定程度的特殊性或对一 个或多个单独实施例的参考描述了本发明的各实施例,但是本领域的技术人员 能够对所公开的实施例做出多种更改而不背离本发明的精神或范围。
权利要求
1. 一种方法,包括(i)提供对软件程序的静态分析的结果的访问,所述静态分析结果包括数据流信息;(ii)形成数据流跟踪指令以便跟踪所述软件内的数据流;以及(iii)形成检查指令以针对所述静态分析结果检查所跟踪的数据流并从而标识所述软件程序中的潜在缺陷。
2. 如权利要求1所述的方法,其特征在于,所述数据流跟踪指令和检査指 令使用修改的编译器(52)或二进制重写工具(62)中的任何一个形成并添加 到所述软件。
3. 如权利要求1所述的方法,其特征在于,所述数据流跟踪指令和所述检查指令是使用被安排成仿真执行所述软件程序的处理器的机器仿真程序和所 述机器仿真程序的硬件等价物中的任何一个实现的。
4. 如前述权利要求中任一项所述的方法,其特征在于,所述潜在缺陷包括 控制数据攻击和非控制数据攻击两者。
5. 如前述权利要求中任一项所述的方法,其特征在于,对由所述软件程序 中的指令读取的每一值,所述静态分析结果包括来自所述软件程序中可写所述 值的指令的集合。
6. 如前述权利要求中任一项所述的方法,其特征在于,还包括通过根据所 述软件程序的源代码计算到达定义来执行对所述软件程序的静态分析。
7. 如前述权利要求中任一项所述的方法,其特征在于,所述数据流跟踪指 令被安排成在标识符和存储器位置之间维护映射,所述标识符是对写入所述每一存储器位置的最后一条指令的标识符。
8. 如前述权利要求中任一项所述的方法,其特征在于,检査指令与读指令 相关联。
9. 如权利要求6所述的方法,其特征在于,与读指令相关联的检查指令被安排成检查写正由授权的读指令读取的值的指令的标识符是否是由所述静态 分析计算出的相关联集合中的元素。
10. 如权利要求5所述的方法,其特征在于,还包括确定指令的等价类, 并向同一类中的所有指令分派同一标识符。
11. 如前述权利要求中任一项所述的方法,其特征在于,还包括基于第 二静态分析移除某些数据流跟踪指令和某些检査指令。
12. 如前述权利要求中任一项所述的方法,其特征在于,还包括通过计 算到达定义并重命名这些到达定义中的至少某些来执行对所述软件程序的静 态分析,以便改善针对这些到达定义进行检査的过程的性能。
13. —种方法,包括(i) 访问软件程序的静态分析的结果,所述静态分析结果包括数据流信息;(ii) 在所述软件程序的执行或仿真期间跟踪所述软件程序中的数据流;以及(iii) 针对所述静态分析结果检査所跟踪的数据流,且如果找到失配则发 生异常。
14. 一种包括计算机程序代码装置的计算机程序,所述代码装置适于当 所述程序在计算机上运行时执行前述权利要求中任一项的所有步骤。
15. 如权利要求14所述的计算机程序,其特征在于,所述计算机程序被包含在计算机可读介质上。
16. —种装置(52、 62、 70),包括(i) 被安排成访问对软件程序的静态分析(61)的结果的输入,所述静 态分析结果包括数据流信息;(ii) 被安排成形成数据流跟踪指令以便跟踪所述软件内的数据流的处理 器(54);以及(iii) 被安排成形成检查指令以针对所述静态分析结果检查所跟踪的数据 流并从而标识所述软件程序中的潜在缺陷的处理器(54)。
17. 如权利要求16所述的装置,其特征在于,还包括被安排成通过根 据所述软件程序的源代码计算到达定义来执行对所述软件程序的静态分析的 编译器(52)。
18. 如权利要求16或权利要求17所述的装置,其特征在于,所述处理 器(54)被配置成形成数据流跟踪指令以维护标识符和存储器位置之间的映射, 所述标识符是写入每一存储器位置的最后一条指令的标识符。
19. 如权利要求18所述的装置,其特征在于,所述处理器被安排成对 每一读指令形成所述检查指令,来检査写正在读取值的指令的标识符是否是所 述静态分析所计算出的相关联集合中的元素。
20. —种装置,包括(i) 被安排成访问对软件程序的静态分析(61)的结果的输入,所述静 态分析结果包括数据流信息;(ii) 被安排成在所述软件程序执行或仿真期间跟踪所述软件程序中的数 据流的处理器(54);以及(iii)其中所述处理器(54)还被安排成针对所述静态分析结果检査所跟 踪的数据流,且如果找到失配则发出异常。
全文摘要
大多数这样的软件攻击利用软件易受攻击性或缺陷将数据编写到非预期的位置中。例如,控制数据攻击利用缓冲区溢出或其他易受攻击性来重写栈中的返回地址、函数指针或某些其他控制数据。非控制数据攻击利用类似的易受攻击性来重写安全关键数据而不会破坏程序中的预期控制流。描述用于针对控制数据和非控制数据攻击两者来保护软件的方法。执行静态分析以确定软件程序的数据流信息。形成数据流跟踪指令以便在该软件的执行或仿真期间跟踪数据流。而且,形成检查指令以针对静态分析结果检查所跟踪的数据流并从而标识潜在的攻击或错误。描述可任选的优化来减少产生的额外开销。
文档编号G06F21/52GK101473300SQ200780023352
公开日2009年7月1日 申请日期2007年5月4日 优先权日2006年6月23日
发明者M·卡斯特罗, M·科斯塔, T·哈里斯 申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1