Meltdown
Table of Contents
Meltdown
1:retry:
2:mov al, byte [rcx] // 读取地址 rcx 处一字节的数据到寄存器 rax
3:shl rax, 0xC // 将刚读取到数据乘 4096
4:jz retry
5:mov rbx, qword [rbx + rax] // 读取 rbx+rax 处的数据(位于我们开辟的用户区数据)
-
一页数据为
4096
字节(1 << 0xC = 4096
) -
rbx
中地址开始256*4096
长度的内存位于用户区(256 页的数据)。 rcx
中为内核空间的地址
第 1 行代码执行的是非法操作,读取内核空间
rcx
地址的一个字节(设这个字节为 x
)到
al
,它会触发异常,但是由于分支预测的存在,第 2、3、4
行可能被先执行,第 2 行将获取的一个字节的数据(该数据范围
0~255
)乘以~4096~ 存在 rax
,访问
rbx+rax
数据,这是用户区数据,它被缓存。
异常处理时,会撤销分支预测时执行的那些代码,清空相关寄存器,因此寄存器里面不会直接存有我们想要的数据(那一个字节)。
漏洞就是,上面最后一步对于用户区数据的缓存没有被撤销。
因此,如果这段代码执行之前我们实现清理缓存确保用户区我们开辟的那
256
页数据没有被缓存,执行这段代码后,这
256
页中有一页是被缓存的,也即
rbx + x*4096
。我们依此访问
rbx + n*4096
(
n: 0~255
)记录这些位置的访问时间,时间最短的那次的
n
就是 x
。
还有就是第 3 行是干嘛要说明一下,执行第 1 行时触发异常,异常处理时会清空相关寄存器,如果异常处理在执行完第 2 行时就执行,那么~rax~ 将为 0,不是我们需要的值,所以对于这种情况我们跳过。
下图截自 Meltdown1。
Figure 1: 展示遍历访问 256 页数据时每页的访问时间,通过 meltdown 攻击缓存的页的数据访问时间明显较短