原文地址:https://blog.cloudflare.com/meltdown-spectre-non-technical/
翻译水平有限,有不通顺的语句,请见谅。
原作者:John Graham-Cumming
写于 08 Jan 2018

译者:驱蚊器喵#ΦωΦ


上周,有两起史诗级别的计算机漏洞被公布. 他们被称为 Meltdown(熔毁) 和 Spectre(幽灵). 这些漏洞利用了 使用了现代CPU使计算机能够大幅加快运算速度的技术系统. 甚至很多专家也很难搞清这个漏洞的运作原理。但是,使用一些类比,可以准确理解这些漏洞的运作。如果您发现自己对这些漏洞的原理感到困惑,请继续阅读 - 本文就是为您准备的。

“When you come to a fork in the road, take it.” — Yogi Berra

一天傍晚,你路过你家附近的一个森林,借助GPS导航,你来到你之前多次走过的路上。不幸的是,因为某些神秘原因,你的GPS不能正常工作,但是你是一个有条理的人,你会非常小心的跟着GPS行走。

但是等待GPS恢复正常工作的过程是令人厌烦的,因为这样回家的路上会浪费很多时间。为了避免等待,你决定根据过去的经验作出一个明智的猜测,选择一条最有可能的路线,比如走向右手的路。

在走了一小段路程后,GPS恢复了正常,并告诉你刚刚的正确路线。如果你预测正确,那么你就节省了一大段时间。如果没有,那你就需要调头走另一条路,并继续前进。

像这样的事,每天都发生在每台计算机的 CPU 内部。计算机操作的本质和基础是进行分支选择的能力,如何在两个不同的代码分支之间进行选择。当您阅读本文时,您的 Web 浏览器会不断地做出分支决策(例如,它的某些部分正在等待您单击链接以转到其他页面)。

其中一个使 CPU 快速运行的能力就是预测可能的分支:在还不知道走哪个分支之前,选择分支中最有可能被选中的那个,然后开始执行。

举个例子, 检查您点击 这个链接 的代码可能会有点慢,因为它正在等待鼠标移动和按钮单击。CPU不会浪费时间等待,而是开始自动执行它认为是最有可能的分支(可能是你不会点击上面的那个链接)。一旦检查结果显示“点击了”或“没有点击”,CPU将根据结果继续执行它所执行的分支,或者放弃已经执行了的代码并从分叉处重新启动。

这就是有名的 “分支预测(branch prediction)”,并会节省大量空闲处理器的时间. 这依赖于CPU“推测性”运算代码的能力,并且如果该代码不应该首先运行,则会丢弃结果。

以前每次你都选择走的右边路线都是正确的,但今天却没有。今天是冬天,树叶更加稀疏,你会看到一些你不应该走这条路的东西:一个隐秘外星科技的秘密政府基地。

但是想要快速回家,你选的是右边的路,却没有意识到今天GPS选的路是在左边,这样本会让你远离危险。在GPS恢复正常前,你从树木的缝隙中瞄到了外星人。

片刻间,两个黑衣男子出现, 擦除了你的记忆,然后把你带回到刚刚的分叉处. 过了会儿,GPS发出了哔哔声,你也聪明的走向了另一条路。

类似的事情同样发生在 Spectre / Meltdown 的攻击中。CPU 之前学习(预测)并执行的代码分支通常是正确的代码。但它被一个聪明的攻击者欺骗了,于是这次走到了错误的分支。更糟糕的是,代码使它访问了它不应该(可能是另一个程序)的内存,使其能够访问到其他秘密信息(例如密码)。

当 CPU 意识到它以错误的方式运行时,它会忘记它所做的所有错误工作(以及它访问了它不应该访问的内存这一事实)并执行正确的分支。即使读取了非法内存,读取到的内容也被 CPU 遗忘了。

Meltdown 和 Spectre 的核心是,从这个推测性执行的代码中,通过所谓的“侧信道(side channel)”提取有关非法访问内存的信息。

实际上你听过有关黑衣人的谣言,并希望找到一些让你自己知道你是否看到外星人的方法。由于从你看到外星人到你的记忆被删除之间的空间很短,所以你想出了一个计划。

如果你看到外星人,那么你就会吞下你背包里的能量饮料。一旦被黑衣人放回路口,你可以通过步行 500 米并计时,来发现你是否喝了能量饮料(因此判断你是否看到了外星人)。因为饮用的 Reactor Core 饮料罐中存在额外碳水化合物,你会走得更快。

通过在 CPU 本身内保留频繁访问或者最近访问信息的拷贝,计算机也会达到高速运行的状态。数据越接近CPU,它被使用的速度就越快。

CPU 中最近/经常使用的数据存储被称为“缓存”。分支预测和缓存都意味着可以让 CPU 运行得非常快。遗憾的是,它们也可以结合起来,用于创造出最近英特尔和其他 CPU 一起被报告的安全问题。

在 Meltdown / Spectre 攻击中,攻击者使用定时信息(但不是功能饮料!)确定哪些机密信息(在现实世界中与外星人等同的)被访问了。在访问非法内存后的瞬间,并且在 CPU 忘记运行代码之前,攻击者的代码将单个字节加载到 CPU 缓存中。一个单独字节,这是完全可被合法访问的; 来自程序自身内存的东西!

然后,攻击者只需要通过尝试读取相同的字节,来确定在此分支中发生的事情:如果需要很长时间来读取,那么它不在缓存中;反之,如果不需要很长时间,那么就在缓存中。时间上的差异,便是攻击者所需要知道的全部,发生在分支中的事情 CPU 应该永远都不应该执行。

将此过程转换为读取非法内存的漏洞利用程序(exploit)实际上很容易。只需要每次读取你正在访问的非法内存的一个字节,一遍又一遍地重复这个过程。使用上述的时序技巧“读取”到的每单个字节的 1 或 0,说明一条信息在 CPU 缓存中的存在与否。

虽然这似乎是一个费力的过程,但事实上,它可以非常快速地完成,从而可以拷贝计算机的整个内存。在现实世界中,一次次的沿着路径徒步并被“黑衣人”摧毁,在此情况下来获取外星人的细节(他们的颜色,大小,语言等)是不切实际的,但在计算机中,由于 CPU 与生俱来的运行速度(每秒可完成数百万个分支中的100个!),一次又一次的遍历分支是可行的。

如果攻击者可以将计算机的内存拷贝出来,这就如同获取皇冠上的宝石:内存中的内容在任何时候都可能是非常非常敏感的:密码,加密的信息,您正在编写的电子邮件,私人聊天等等。

结论

我希望本文有助于您了解 Meltdown 和 Spectre 的本质。两种攻击都有许多变化,但都依赖于相同的思路:让 CPU 推测性地运行一些非法访问内存的代码(通过分支预测或其他技术),并使用通过 CPU 缓存的时序侧信道提取信息。

如果你想阅读所有的深度细节,有两篇论文,一篇关于 Meltdown,以及另一篇关于 Spectre

致谢:我很感谢所有阅读此内容的人并给了我反馈(包括悄悄告诉我不理解分支预测和推测执行的方式)。特别感谢 David Wragg,Kenton Varda,Chris Branch,Vlad Krasnov,Matthew Prince,Michelle Zatlyn 和 Ben Cartwright-Cox。 非常感谢 Kari Linder 的插图。