我还想知道在理论上是否可以(虽然显然很困难)翻译现有的 JIT 编译器(例如 .NET CLR),该编译器生成自动加密其用户模式内存的代码。
操作系统代表程序执行此操作可能非常困难。原因是操作系统分配的虚拟页面实际上是由 CPU 控制的——操作系统会根据存储区域向 CPU 指示它想要什么,然后 CPU 在此基础上为每个程序计算偏移量并直接应用结果。阅读MMU和x86上的分页。
简而言之,当您的程序进行计算时mov eax, [edi+something]
,MMU/paging 会处理地址转换并在找不到页面时发出页面错误。如果需要,这允许您将页面从存储空间中加载出来。
因此内存访问本身并不通过内核,因此不能像文件读取或写入那样被挂钩和处理(您的读取和写入通过适当的系统调用转换为适当的文件系统结构。作为这样,操作系统会在数据通过时看到它。您不需要系统调用即可写入 RAM。您可以制作一个,但它仅适用于调用它的程序,因此不适用于大多数程序)。
但是,这并不意味着 JIT 进程不能代表应用程序执行此操作,或者应用程序本身无法保持数据加密,直到需要将其加载到寄存器中,并在传递数据时对其进行解密.
在这种情况下,你就陷入了SteveS很好地解决的问题——你有一个密钥存储问题。密钥本身也必须在 RAM 中的某个地方。这是一个从根本上解决问题的乌龟 - 从根本上说,要保持该密钥“安全”是不可能的。
最后,由于能够读取另一个应用程序的 RAM 需要超级用户模式访问 CPU(即内核空间),如果您担心软件拦截,您可能会遇到更大的问题。如果您关心的是硬件,我认为物理安全可能是降低风险的更好方法。
编辑我看了看论文。这是一个叫做CryptKeeper的。他们的技术是有一个大的加密“RAM磁盘”作为交换文件,并在不使用时将所有页面交换到那个位置:
我们使用软件加密的虚拟内存管理器 Cryptkeeper (CK) 来缓解此漏洞。传统处理器无法对加密数据进行操作,因此 CK 将 RAM 分段为一个称为 Clear 的较小工作集和一个称为 Crypt 的较大加密 RAM 设备。随着工作空间的填满,页面会自动交换到内存的加密部分,并按需解密。
显然性能还不错,但我认为还没有任何实现。
编辑 2所以事实证明 CryptKeeper 和OpenBSD 交换加密机制在非常相似的级别上工作;它实际上并不加密物理内存,而是使用物理内存作为交换结构,强制页面错误并在解析中加密/解密数据。
来自问题作者的编辑,2018 年 12 月: AMD 现在支持SME 指令集扩展,它允许对 RAM 页面进行硬件加密,加上 TSME 用于全内存加密,以及 SEV 用于虚拟化中的加密内存。这似乎可以在现代 AMD64 平台上启用全系统内存加密。