前提
众所周知由于CPU和内存速度不匹配,为了提高速度,在CPU增加cache临时保存着正在运行的程序和数据, 如
CPU cache memory
当CPU执行程序访问数据时候,如果数据在cache里就会快速从cache载入数据, 这个过程叫做命中,如果不在的话,就需要花费很长周期从memory中载入。
由于为了进一步提高执行速度现代CPU引入多级流水程序乱序执行和分支预测。比如你写的以下程序:
a = 3;
.........
b = 4;
c = a;
实际现代cpu可能这样执行,在同一时间 a=3 cpu正在执行, c=a已经载入cache中
a = 3; // cpu执行
.........
c = a; // cpu 将a载入cache
b = 4;
原因:
因为CPU多级流水乱序执行能将不该访问没有权限的数据加载到cache里,引发异常,当CPU恢复正常状态后,不该访问的数据驻留在cache中。
例如前提2所说, 当a=3执行时候,而c=a载入cache, 假如a=3执行引发异常后(例如没有权限),而c=a 已经将a引入cache中,当异常结束后,a还是在cache中,此处为漏洞。
攻击方法:
在linux上最明显的例子就是使用用户程序就可以肆意访问内核中的数据。
使用的方法是 side channel attack.
代码在:
https://github.com/paboldin/meltdown-exploit/blob/master/meltdown.c
由于加载汇编代码太多,科普下使用伪代码简单描述原理:
假设内核地址为kernel_address,我们想访问它里边存储的数据。
====================
首先读入内核地址。
p = (unsigned char *)kernel_address;
===========================
cache = target[*p ];
使用内核地址里的数据作为数组索引,
将target数据读入cache中。
当程序没有权限去读*p时会引发异常,
而同时target[*p]的数据载到内存。
如前提2所描述,
异常发生后 target[*p]还在cache中。
============================
遍历0到255进行猜测,
如果访问某个地址存储的数据时间
为最短的话,
就是命中cache里target[*p]。
不命中,
会花费很长周期从memory读。
for(i = 0; i
addr = &target[i ];
if(min_access_time(addr)) {
如果此时访问该地址中数据
时间最短!!!!!!!!
那么cache命中,i值就是*p
printf ("found %x value %x",kernel_address, i);
}
}
领取专属 10元无门槛券
私享最新 技术干货