Spectre/Meltdown - 内存释放是否会擦除实际的 RAM 内容?

信息安全 数据泄露 记忆 核心 崩溃 幽灵
2021-09-10 03:02:46

前几天我一直在思考 Spectre 和 Meltdown 以及一个进程访问另一个进程的内存的能力。

目前,在我的 Linux 系统上,我禁用了所有 JavaScript,以消除某些 JS 程序以不良方式访问内存的可能性。如果某个网站需要 JS 才能工作,我首先关闭所有其他应用程序,只保留打开该单个站点的浏览器窗口,然后在有限的时间内仅为该站点启用 JS,然后恢复设置。这个想法是 - 减少内存中有任何敏感数据的机会(伪单任务)。

但这让我想到:

  1. 内存释放哪个程序/内核会删除实际的 RAM 内容?

  2. 还是它只是为另一个程序提供了分配该内存的可能性,同时仍将先前的内容保留在 RAM 中,直到另一个进程覆盖它?(考虑缓存等)

如果 1 是有效的 - 那么我正在做的事情可能是有意义的(在一定程度上)。但如果 2 是实际情况,它可能完全是徒劳的。

那么这是如何工作的呢?请分享有关它的实际事实。

2个回答

与其给你一个直接的答案,因为它主要是“它取决于”,我认为带你了解我最近发现的描述 Windows 上内存分配行为的发现可能会更有趣,这样你就已经在这一切背后都有一些背景。我对 Linux 内存管理模型了解不够,所以我不会评论这方面的事情,尽管有人告诉我它非常相似。

最近我在思考 Windows 内核提供的 W^X 内存保护功能,称为“动态代码策略”这个想法是,一旦映射到应用程序的页面被标记为可写,它就永远不能被标记为可执行。这是一个强大的漏洞利用缓解措施,因为它可以防止几乎所有类型的涉及传递 shellcode 的漏洞利用。您不能只是进行缓冲区溢出,控制指令指针,ROP 到 VirtualProtect,将有效负载标记为 RWX 并执行它 - VirtualProtect 调用失败,因为内存不可写和可执行。

我对这里的通用绕过的想法是,您可以 VirtualAlloc 一些读+写内存,VirtualFree 它,然后 VirtualAlloc 再次作为读+执行,希望重新分配相同的页面。事实证明,这是行不通的。原因与您的问题有关。

在三种情况下,Windows 内存管理会将页面清理为零:

  • 当页面提交给进程时
  • 当页面被标记为 MEM_RESET
  • 当页面已被释放并被标记为脏页面时

第一个阻碍了我的计划。进程可以在不提交它们的情况下分配虚拟地址空间,例如通过调用 VirtualAlloc MEM_RESERVE保留的地址空间不直接映射到任何物理内存或交换空间,但会保留以供进程在需要时使用。为了实际使用内存,必须提交页面,例如通过调用 VirtualAlloc MEM_RESERVE | MEM_COMMIT提交后,如果尚未通过其他方式提交页面,则该页面将被归零。因此,我分配可写数据,释放它,然后将其分配为可执行文件的技巧被破坏了,因为内存管理器将页面归零。

第二个也很有趣。如果您暂时使用完一块内存,但稍后会再次使用它,您可以使用MEM_RESET. 这向内存管理器表明它不应该从进程中取消提交页面,但它不应该费心将它们交换到磁盘,因为其中包含的数据不再感兴趣。当有空闲时间时,系统将在后台将这些页面归零。但是,如果系统尚未将页面归零,则应用程序可以使用该标志请求撤消重置MEM_RESET_UNDO标志。

第三种选择是取消提交并释放一个页面,它可以完全免费地被内存管理器与任何其他进程重用。如果页面包含数据,则将其标记为脏。系统要么在后台清理它,要么在再次提交时主动将其归零。

这些是系统级内存管理的情况。进程级管理有很大不同,例如使用 HeapAlloc、库分配器 (malloc) 或完全自定义的分配器。这些设计可以分配和提交内存页面,并在该提交的块之上执行它们自己的内存使用管理。因此,libc free() 调用实际上可能不会取消底层内存页面。malloc() 调用也完全有可能返回尚未重置的页面 - 这就是 calloc() 存在的原因。

问题的下一部分是浏览器是否对此有任何积极的防御措施。我不知道他们有没有。当然,他们的内存分配器可能会将所有新分配的页面显式归零(或将它们设置为某个神奇的值,例如 0xDEADBEEF),以限制未初始化内存读取的可能性并更好地识别这种情况。此外,例如,Microsoft Edge 的内存分配器专门设计用于提供更高的安全性以防止内存损坏,因此重置分配是其中的一部分。

总而言之,这取决于!

从操作系统的角度来看,在释放内存后不需要擦除内存,而且很有可能不会这样做,因为这是一项昂贵的操作。所以除非你有一个非常特殊的内核,否则无法保证你的物理内存在关闭应用程序后不包含敏感信息。