Meltdown原理及SEED Lab实验setup
Meltdown原理
基于FLUSH+RELOAD的cache侧信道攻击
学计算机的人都知道,从cache中读取数据的速度会比从内存中读取数据的速度快很多。 在第一次访问数据时cache miss,需要从内存中获取数据。当再次访问该数据时,cache hit,CPU直接从cache中拿到数据。 FLUSH+RELOAD就利用了cache的这个特点。 别的不多说,直接看代码吧~
#include <emmintrin.h>
#include <x86intrin.h>
// 256的来源: 对于1个Byte的secret,总共有256个可能的值,所以我们要将256个值一一映射到数组元素
// 4096的来源: Cache是基于块(block)的,一个块的大小通常是64bytes,4096的设定使得两个值不会被映射到相同的cache块内
uint8_t array[256*4096];
int temp;
unsigned char secret = 94;
/* cache hit time threshold assumed*/
#define CACHE_HIT_THRESHOLD (80)
#define DELTA 1024
void flushSideChannel()
{
int i;
// Write to array to bring it to RAM to prevent Copy-on-write
for (i = 0; i < 256; i++) array[i*4096 + DELTA] = 1;
// Flush the values of the array from cache
for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 +DELTA]);
}
void victim()
{
temp = array[secret*4096 + DELTA]; //使用secrete作为index访问数组元素
}
void reloadSideChannel()
{
int junk=0;
register uint64_t time1, time2;
volatile uint8_t *addr;
int i;
for(i = 0; i < 256; i++){
addr = &array[i*4096 + DELTA];
// 计算每个数组元素的访问时间
time1 = __rdtscp(&junk);
junk = *addr;
time2 = __rdtscp(&junk) - time1;
// 如果访问时间比较短,则该元素之前被访问过,存在cache内部
if (time2 <= CACHE_HIT_THRESHOLD){
printf("array[%d*4096 + %d] is in cache.\n", i, DELTA);
printf("The Secret = %d.\n",i);
}
}
}
int main(int argc, const char **argv)
{
flushSideChannel();
victim();
reloadSideChannel();
return (0);
}
CPU乱序执行优化
Executing instructions one after another may lead to poor performance and inefficient resources usage, i.e., current instruction is waiting for previous instruction to complete even though some execution units are idle.
number = 0;
*kernel_address = (char*)0xfb61b000;
kernel_data = *kernel_address;
number = number + kernel_data;
在微架构层面,上述代码第三行包括两个操作:1.加载数据到寄存器+2.检查访问权限。
当数据已经在缓存的时候,第一个操作会很快但是第二个操作就需要一点时间。这时CPU就会继续执行第4行代码,也就是说检查访问权限和第4行代码是并行执行的。
但是,访问权限检查完成前,CPU不会提交所做的后续操作。如果权限检查不通过,CPU会回退执行状态。 然而,大多数CPU回退执行状态只会恢复寄存器和内存的状态,cache的状态并没有回退。
这就给了Meltdown机会。
利用乱序执行以及FLUSH+RELOAD, 我们使用想要获得kernel_data/secrete作为数组下标(index)访问数组元素,之后再访问所有数组元素,根据访问时间差异,就可以获取到任何想要的内存信息。
实验环境及setup
- 使用
lscpu
指令可以查看实验主机的相关CPU信息(此处列出我的实验环境)Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 46 bits physical, 48 bits virtual ... Vendor ID: GenuineIntel Model name: 12th Gen Intel(R) Core(TM) i7-12700K Vulnerabilities: Itlb multihit: Not affected L1tf: Not affected Mds: Not affected Meltdown: Not affected Mmio stale data: Not affected Retbleed: Not affected Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Spectre v2: Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence Srbds: Not affected Tsx async abort: Not affected
- 使用
hostnamectl
查看当前OS及其版本信息Operating System: Ubuntu 22.04.2 LTS Kernel: Linux 5.19.0-42-generic Architecture: x86-64
- 使用
qemu-system-x86_64 --version
查看qemu version - 下载SEEDUbuntu-16.04-32bit虚拟机(需要VPN)
wget http://www.cis.syr.edu/\~wedu/SEEDUbuntu-16.04-32bit.zip unzip SEEDUbuntu-16.04-32bit.zip cd SEEDUbuntu-16.04-32bit
- 由于SEED Lab中提供的是在VM ware/Virtual Box上的虚拟机,在此实验中希望在qemu上运行该VM。 幸运的是,qemu直接运行vmdk镜像。
qemu-system-x86_64 -smp 2 -soundhw ac97 -enable-kvm -m 1536 \ -hda "SEEDUbuntu-16.04-32bit.vmdk" -display vnc=:00\ -boot once=c,menu=off \ -name "SEEDUbuntu"\ -net nic\ -net user \ -cpu host
由于我的实验是在服务器上跑的,没有图形界面,所以此处使用vnc的方式展示
-display vnc=:00
。如果是在个人PC上,可以直接删去这个选项。
-cpu host
: Enable every single bit that can be enabled, including the ones not present on the host but that can be emulated, 不加这个选项可能会造成illegal instruction
的报错。
-net user -net nic
: 虚拟机的网络通过host接入互联网
成功启动虚拟机后即可照着实验手册学习Meltdown内容。
Reference
Enjoy Reading This Article?
Here are some more articles you might like to read next: