麻省理工学院的课程“计算机系统安全”。 讲座7:本机客户端沙箱第2部分

麻省理工学院。 讲座课程#6.858。 “计算机系统的安全性。” Nikolai Zeldovich,James Mickens。 2014年


计算机系统安全是一门有关开发和实施安全计算机系统的课程。 讲座涵盖了威胁模型,危害安全性的攻击以及基于最新科学研究的安全技术。 主题包括操作系统(OS)安全性,功能,信息流管理,语言安全性,网络协议,硬件安全性和Web应用程序安全性。

第1课:“简介:威胁模型” 第1 部分 / 第2 部分 / 第3部分
第2课:“控制黑客攻击”, 第1 部分 / 第2 部分 / 第3部分
第3讲:“缓冲区溢出:漏洞利用和保护” 第1 部分 / 第2 部分 / 第3部分
讲座4:“特权分离”, 第1 部分 / 第2 部分 / 第3部分
讲座5:“安全系统从何而来?” 第1 部分 / 第2部分
讲座6:“机会” 第1 部分 / 第2 部分 / 第3部分
讲座7:“本地客户端沙箱” 第1 部分 / 第2 部分 / 第3部分

听众:为什么地址范围存储容量范围应从头开始?

教授:因为在性能方面,如果您知道有效地址是从零开始的连续地址集,则使用目标跳转会更有效。 因此,您可以使用单个AND掩码来执行此操作,其中所有高位均为1,而只有一对低位为零。

观众:我认为AND遮罩应该可以对齐。

教授:对,面罩可以对齐,但是为什么要从头开始呢? 我认为他们依靠细分硬件细分硬件。 因此,基本上,他们可以使用它来按线性空间向上移动区域。 或者,可能与应用程序“看到”此范围的方式有关。 实际上,您可以将其放置在虚拟地址空间中的不同偏移处。 这将允许您使用分段硬件执行某些技巧,以在同一地址空间中运行多个模块。



受众:也许是因为他们想“捕获”空指针的接收点?

教授:是的,因为他们想抓住所有接待点。 但是您有办法做到。 因为空指针指向正在访问的段。 并且,如果移动该段,则可以在每个段的开头显示未使用的零页。 因此,这将有助于制作一些模块。

我认为做出此决定的原因之一-从0开始-是因为他们希望将其程序移植到x64平台上,该平台的设计略有不同。 但是他们的文章没有这么说。 在64位设计中,设备本身摆脱了一些分段硬件,出于效率考虑,它们依赖于这些分段硬件,因此必须提供一种面向软件的方法。 但是,对于x32,这仍然不是空间从头开始的好理由。

因此,我们继续提出主要问题-从安全的角度来看,我们要确保什么。 让我们以某种“天真”的方式处理此问题,看看如何破坏一切,然后尝试解决它。

我相信天真的计划是通过从头到尾简单地扫描可执行文件来查找禁止的指令。 那么如何发现这些说明呢? 您可以简单地获取程序代码并将其放在从0到256 MB的巨型行中,具体取决于代码的大小,然后开始搜索。



该行可能首先包含NOP指令模块,然后包含ADD指令模块NOTJUMP等。 您只是搜索,如果发现错误的指令,则说这是一个错误的模块并丢弃它。 而且,如果您看不到对该指令的任何系统调用,则可以启用该模块的启动,并执行0-256范围内的所有操作。 您认为这行得通吗? 他们担心什么? 为什么这么难?

观众:他们担心指示的大小吗?

教授:是的,事实是x86平台具有可变长度的指令。 这意味着指令的确切大小取决于该指令的前几个字节。 实际上,您可以查看第一个字节以表明指令将大得多,然后您可能不得不查看另外两个字节,然后确定所需的大小。 诸如SparkARMMIPS之类的某些体系结构具有更多固定长度的指令。 ARM有两个指令长度-2或4个字节。 但是在x86平台上指令长度可以是1、5和10字节,如果尝试,甚至可以得到15字节的相当长的指令。 但是,这些是复杂的指令。

结果,可能出现问题。 如果您线性扫描这行代码,一切都会好起来的。 但是也许在运行时您会进入某种指令的中间,例如NOT



这可能是一条多字节指令,如果您从第二个字节开始对其进行解释,则它看起来将完全不同。

另一个示例,我们将“玩”汇编程序。 假设我们有一条指令25 CD 80 00 00 。 看完第2个字节后,您将其解释为5个字节的指令,也就是说,您将不得不向前看5个字节,然后看到它后面是AND%EAX指令0x00 00 80 CD ,从EAX寄存器的AND运算符开始,定义的常量,例如00 00 80 CD 。 这是本机客户端应通过检查二进制指令的第一条规则简单地允许的安全指令之一。 但是,如果在程序执行期间, CPU决定应开始执行CD中的代码,则我将用箭头标记该指令的此位置,则实际上是4字节指令的%EAX,0x00 00 80 CD指令将意味着执行INT $ 0x80 ,这是在Linux上进行系统调用的一种方法。



因此,如果您错过了这个事实,那么就让不可靠的模块“跳入”内核并进行系统调用,即执行您想防止的事情。 我们如何避免这种情况?

也许我们应该尝试查看每个字节的偏移量。 因为x86只能以字节(而不是位)边界开始解释一条指令。 因此,您必须查看每个字节的偏移量以查看指令的开始位置。 您认为这是可行的计划吗?

受众:我认为,如果有人实际使用AND ,则处理器不会跳到这个位置,而只是允许程序运行。

教授:是的,因为基本上他不容易出现误报。 现在,如果您确实想要它,可以稍微更改代码以某种方式避免它。 如果您确切知道测试设备在寻找什么,则可以更改这些说明。 也许首先为一条指令设置AND ,然后在另一条指令上使用掩码。 避免这些可疑的字节排列要容易得多,尽管这似乎很不方便。

该体系结构可能包括编译器更改。 基本上,它们具有某种实际需要正确编译代码的组件。 您不能只是“摘下” GCC并为Native Client编译代码。 所以基本上这是可行的。 但是很可能,他们只是认为这会引起太多麻烦,不会是可靠或高性能的解决方案,等等。 另外,有一些x86指令被禁止,或者应该被认为是不安全的,因此应该被禁止。 但是在大多数情况下,它们只有一个字节大小,因此很难找到或过滤掉它们。

因此,如果他们不能仅收集和分类不安全的说明并希望获得最好的效果,则他们需要使用其他计划以可靠的方式分解计划。 那么, Native Client如何做以确保他们不会“迷住”这种可变长度编码?

从某种意义上说,如果我们真的从左到右扫描可执行文件并查找所有可能的不正确代码,并且如果这就是代码运行的方式,那么我们就处于良好状态。 即使有一些奇怪的指令和一些偏见,处理器仍然不会“跳到”那里,它将以扫描指令的相同顺序(即从左到右)执行程序。



因此,由于在应用程序中某处可能存在“跳动”这一事实,出现了可靠拆卸的问题。 如果处理器对从左向右扫描时未注意到的某些代码指令进行“跳转”,则处理器可能会失败。 因此,这是迄今为止正在开发的可靠拆卸的问题。 主要计划是检查所有“跳跃”的位置。 实际上,它在某种程度上很简单。 我们将在一秒钟内考虑一堆规则,但是大概的计划是,如果您看到“跳转”指令,则需要确保“跳转”的目的已在早期注意到。 实际上,要做到这一点,从左到右扫描就足够了,也就是我们在幼稚的方法中描述的过程。

在这种情况下,如果看到任何“跳转”指令和该指令指向的地址,则必须确保该地址与您在从左向右反汇编时已经看到的地址相同。

如果找到了此CD字节的跳转指令,则应将该跳转标记为无效,因为我们从未见过以CD字节开头的指令,而是看到了另一个以数字25开头的指令。但是,如果所有跳转指令命令转到说明的开头,在本例中为25,则一切与我们保持一致。 清楚吗?

唯一的问题是您无法检查程序中每个跳转的目标,因为可能存在间接跳转。 例如,在x86中,您可能会跳转到该EAX寄存器的值。 这对于实现函数指针非常有用。



也就是说,函数指针位于内存中的某个位置,将其保存在某个寄存器中,然后转到移动寄存器中的任何地址。

那么这些家伙如何应对间接跳跃? 因为,实际上,我不知道这将是字节CD还是字节25的“跳转”。在这种情况下,它们会做什么?

受众:使用工具吗?

教授:是的,使用仪器是他们的主要窍门。 因此,每当他们看到编译器已准备好执行生成操作时,就可以证明此跳转不会引起麻烦。 为此,他们需要确保所有跳转均以32字节的多重性执行。 他们是如何做到的? 他们将所有跳转指令更改为所谓的“伪指令”。 这些是相同的指令,但带前缀,可清除EAX寄存器中的5个低位。 指令清除5个低位的事实意味着它导致给定值是32的倍数,从2到5,然后已经执行到该值的跳转。



如果您在验证期间查看此内容,请确保该说明性“对”仅以32字节的多重性“跳跃”。 然后,为了确保没有“跳入”某些奇怪指令的可能性,请应用一条附加规则。 它包含以下事实:在反汇编期间,当您从左到右查看指令时,请确保每个有效指令的开头也应为32字节的倍数。

因此,除了此工具包外,还要验证32的倍数的每个代码是否正确。 所谓有效指令,是指从左向右反汇编的指令。

观众:为什么选择32号?

教授:是的,为什么他们选择32而不是1000或5? 为什么5不好?

观众:因为数字必须是2的幂。

教授:是的,这就是原因。 因为否则,要确保使用5的倍数,将需要其他指令,从而导致开销。 那八点呢? 八个数字够用吗?

受众:您的指令长度可能超过八位。

教授:是的,这可能是x86平台上允许的最长指令。 如果我们有10字节的指令,并且所有内容都应为8的倍数,则我们将无法在任何位置插入它。 因此,该长度应足以应付所有情况,因为我看到的最大指令长度为15个字节。 因此32个字节就足够了。

如果要修改说明以进入或退出流程服务环境,则可能需要在一个32字节的插槽中添加一些平凡的代码。 例如,为31个字节,因为1个字节包含一条指令。 应该更大吗? 我们应该使它等于1024个字节吗? 如果您有许多函数指针或许多间接跳转,那么每次您要创建要跳转的位置时,无论其值如何,都必须将其继续到下一个边界。 因此,如果使用32位,则相当正常。 在最坏的情况下,如果您需要快速到达下一个边界,则只会丢失31个字节。 但是,如果您的大小是1024字节的倍数,则可能会浪费整个千字节的内存以进行间接跳转。 如果您有短函数或许多函数指针,那么“跳转”长度的倍数如此之大将导致内存的大量浪费。

我不认为数字32是Native Client的绊脚石。 某些块可以使用16位(某些64位或128位)的多样性,这并不重要。 在他们看来,只有32位是最可接受的最佳值。

因此,让我们制定可靠的拆卸计划。 因此,在将CC ++代码编译为Native Client二进制文件时,编译器应格外小心,并遵守以下规则。



因此,每当他有跳跃动作时(如第一行所示),他都必须添加最后两行中给出的这些附加说明。 并且不管他创建了要“跳转”到的函数的事实,我们的指令都会跳转为附加值AND $ 0xffffffe0,%eax表示。 而且它不能仅用零进行补码,因为所有这些都必须具有正确的代码。 因此,为了确保所有可能的指令均有效,必须进行加法运算。 而且,幸运的是,在x86平台上,没有单个字节描述一个noop函数,或者至少没有一个字节大小的单个noop 。 因此,您始终可以将事物添加到常量的值。

那么,这对我们有什么保证? 确保我们始终看到将要遵循的指令术语中发生的情况。 这条规则为我们提供了-保证不会偶然发生系统调用。 这适用于跳跃,但是收益呢? 他们如何处理退货? 我们可以返回 Native Client中的功能吗? 如果运行红热代码会怎样?

受众:它可能会使堆栈溢出。

教授:确实,它突然出现在堆栈上。 但是事实是,本机客户端模块使用的堆栈实际上包含一些内部数据。 因此,在处理Native Client时,您不必担心堆栈溢出。

听众:等等,但是您可以将任何东西放在堆栈上。 当您进行间接跳跃时。

教授:是的。 返回看起来几乎是从位于堆栈顶部的内存中某个位置间接跳转。 因此,我认为他们可以为return函数做的一件事就是以与上一次检查相同的方式设置前缀。 并且此前缀检查在堆栈顶部弹出的内容。 您检查这是否有效,并在编写或使用AND运算符时检查堆栈顶部的内容。 由于数据的不断变化,这似乎有点不可靠。 因为例如,如果您查看堆栈的顶部并确保所有内容都正确,然后再写一些内容,则同一模块中的数据流可能会修改堆栈顶部的内容,之后您将引用错误的内容地址

观众:这不适用于同样程度的跳跃吗?

教授:是的,那么跳在那里会发生什么? 我们的比赛条件会以某种方式使这项测试无效吗?

观众:但是代码不是可写的吗?

教授:是的,代码无法编写,这是事实。 因此,您不能修改AND。 但是是否有其他流不能改变这两条指令之间跳转的目的?

观众:这是在登记册中,所以...

教授:是的,这很酷。 因为如果流修改了内存中的内容或从EAX加载的内容(本身,您是在下载之前进行了修改),在这种情况下,此EAX将处于错误状态,但随后将清除错误的位。 或者,当指针已经在EAX中时 ,他可以在之后更改内存,因此它不会更改从中加载EAX寄存器的内存的位置。

实际上,线程不共享寄存器集。 因此,如果另一个线程更改了EAX寄存器,则不会影响该线程的EAX寄存器。 因此,其他线程不能使此指令序列无效。

还有另一个有趣的问题。 我们可以绕开这个AND吗? 我可以跳转到该地址空间中的任何位置。 , AND .



, , , , , AND . . jmp , .



, , - , 1237. , 32. Native Client , , , . , , 1237 ?



- EAX , , , , . , ? ?

: NaCl , .

: , . x86 , , NaCl , 2 . , , : «, , !», 25 CD 80 00 00 . . , x86 .

, Native Client . , , , , NaCl . , .

: , , . , . , , , , .



: , . , . , , , EAX . , - . EAX , EBX . , . EAX EBX AND . , , EAX , . , - 64 . Jmp *% eax AND .



, , , , . Intel , , , , . , , . AND , EAX , «» .

, , . , . , , , . , , , .
, , C1 C7 .

C1 , , . , «» . , , . , , - . , .
2 , 0 64 . , , . , , .

3 , , , . , , .

4 , hlt . halt ? , C4 . , , - , .

, , ? , , - .
, , , , . , , , , . .



55:20

:

麻省理工学院的课程“计算机系统安全”。 7: « Native Client», 3


.

, . 你喜欢我们的文章吗? 想看更多有趣的资料吗? 通过下订单或将其推荐给您的朋友来支持我们,为我们为您开发的入门级​​服务器的独特模拟,为Habr用户提供30%的折扣: 关于VPS(KVM)E5-2650 v4(6核)的全部真相10GB DDR4 240GB SSD 1Gbps从$ 20还是如何划分服务器? (RAID1和RAID10提供选件,最多24个内核和最大40GB DDR4)。

3 Dell R630 — 2 Intel Deca-Core Xeon E5-2630 v4 / 128GB DDR4 / 41TB HDD 2240GB SSD / 1Gbps 10 TB — $99,33 , , .

戴尔R730xd便宜2倍?在荷兰和美国,我们有2台Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100电视(249美元起) 阅读有关如何构建基础架构大厦的信息。 使用价格为9000欧元的Dell R730xd E5-2650 v4服务器的上等课程?

Source: https://habr.com/ru/post/zh-CN418225/


All Articles