随笔

author : https://weibo.com/jfpan

  这是一篇随笔,Win10对虚拟化实施拦截的产品设的障碍越来越大,忍不住吐槽下。话说RS3改进PatchGuard的针对性很明显,但为什么昨天提到Dual-CR3呢?因为它虽对功能实现没什么影响,但对性能造成不小麻烦(实际上,虚拟化拦截类项目,其拦截功能本身的实现是非常简单的,而能否大规模产品化、商业化的根本核心难点与重点在于完美兼容性与极高实时性能的要求:1、兼容性——除去极端软件,即“用我时就别运行其他虚拟化或硬件相关程序”的软件——必须实现对GUEST展现实际CPU全部硬件特性且GUEST确实可使用这些特性,否则在一些场景一定有兼容问题。兼容性的一些入门测试有不少,比如虚拟化功能开启时运行vmware workstation在里面各跑一个32bit Guest和64bit Guest、跑一个Bluestacks模拟器玩玩Android游戏、给Intel CPU打一个微码补丁等等;2、性能的要求是几乎不造成性能下降,而#VMEXIT的性能损耗是巨大的,因此至少需要实现未嵌套工作时在支持unrestricted guest的CPU上几乎不产生#VMEXIT。这两点可探讨的细节和实例太多,就不写了,一个小广告——可参考360HVM)。

  那么微软为什么要在RS4引入Dual-CR3,这要从内核地址空间随机化(KASLR)说起了,Win10 KASLR随机化了模块的加载基址、内核对象地址、页表地址等,缓解了内核漏洞的利用。不过之前微软对各种基于硬件的边信道攻击(double page fault、prefetch side-channel、TSX-based side-channel等等)依然是没有防护的,这次引入Dual-CR3至少目标中包含增加该种防护。学术圈对该类攻击和防御手段研究已经多时了,今年《KASLR is Dead: Long Live KASLR》这篇论文为Linux设计实现的内核地址隔离方案KAISER号称性能损失仅有0.28%,当初看到的时候只凭感觉每次系统调用都切换CR3、把非Global的TLB项清除(何况为了实现内核地址强隔离应该是没有Global项),这性能损失怎么会这么小(论文里倒是提供了一下解释:首先Global没什么用”Surprisingly, we found the performance impact of disabling global bits to be entirely negligible”;其次现代CPU对TLB管理的优化使得频繁切CR3也没什么大损失了)。没想到没几个月微软就直接在Win10上完全照搬了这套方案(不是每个进程都切换)。这套方案原理简单可行,参见附图一(论文附图)就一目了然了。微软在进程—_KPROCESS中增加了UserDirectoryTableBase配合原有DirectoryTableBase即提供论文中描述的CR3 Pair的内容。线程运行时,_KPRCB中的KernelDirectoryTableBase、RspBaseShadow、UserRspShadow、ShadowFlags用于模式转换时的隔离切换,需要加入的代码很少,附图二是Intel CPU的系统调用入口的代码,返回时自然也有相应的处理。

  回到一开始,微软的强隔离对虚拟化拦截项目有什么影响呢?首先对一些拦截了MOV-CR3操作的情况乐子就大了,增加大量的#VMEXIT;其次微软仅保留映射了极少的内核页面在所谓Shadow address space中,比如KiSystemCall64Shadow需要被映射,但KiSystemCall64- KiSystemServiceUser都未被映射,更别说虚拟机在GUEST中的HOOK代码了。如果强制在GUEST中映射自己的代码,这相当不优美又对强隔离有所破坏且带来风险。有事要忙随笔先写到这里。

参考

https://cmaurice.fr/pdf/essos17_gruss.pdf

附图1

附图2