static void limsw_exit( void) { // Gracefully handle exit for (int i = 0 ; i < nr_limits ; ++i) { if (gpio_fd[ i]) { close( gpio_fd[ i]); gpio_fd[ i] = -1; } gpio_write_value_to_pin_file( limit_gpios[ i], "edge", "none"); if (get_kernel_type() == e_kernel_3_2) { gpio_write_int_value_to_file( "unexport", limit_gpios[ i]); } } }
int64_t find_sysent(const uint8_t *buffer, const uint64_t data_address, const uint64_t data_size) { #if DEBUG printf("[DEBUG] Executing %s\n", __FUNCTION__); #endif uint64_t i = 0; if (get_kernel_type()) // 64 bits { while (i < data_size) { struct sysent64 *table = (struct sysent64*)(&buffer[i]); if(table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4 && table[SYS_getxattr].sy_narg == 6 && table[SYS_listxattr].sy_narg == 4 && table[SYS_recvmsg].sy_narg == 3 ) { printf("[DEBUG] exit() address is %p\n", (void*)table[SYS_exit].sy_call); return(data_address+i); } i++; } } else // 32bits { while (i < data_size) { struct sysent *table = (struct sysent*)(&buffer[i]); if(table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4 && table[SYS_getxattr].sy_narg == 6 && table[SYS_listxattr].sy_narg == 4 && table[SYS_recvmsg].sy_narg == 3 ) { printf("[DEBUG] exit() address is %p\n", (void*)table[SYS_exit].sy_call); return(data_address+i); } i++; } } return(0); }
int64_t find_sysent(const uint8_t *buffer, const uint64_t data_address, const uint64_t data_size) { uint64_t i = 0; if (get_kernel_type()) // 64 bits { int major = get_kernel_version(); /* Mavericks or higher uses new sysent table format */ if (major >= 13) { while (i < data_size) { struct newsysent *table = (struct newsysent*)(&buffer[i]); if(table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4 && table[SYS_getxattr].sy_narg == 6 && table[SYS_listxattr].sy_narg == 4 && table[SYS_recvmsg].sy_narg == 3 ) { printf("[DEBUG] exit() address is %p\n", (void*)table[SYS_exit].sy_call); return(data_address+i); } i++; } } else { while (i < data_size) { struct sysent64 *table = (struct sysent64*)(&buffer[i]); if(table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4 && table[SYS_getxattr].sy_narg == 6 && table[SYS_listxattr].sy_narg == 4 && table[SYS_recvmsg].sy_narg == 3 ) { printf("[DEBUG] exit() address is %p\n", (void*)table[SYS_exit].sy_call); return(data_address+i); } i++; } } } else // 32bits { while (i < data_size) { struct sysent *table = (struct sysent*)(&buffer[i]); if(table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4 && table[SYS_getxattr].sy_narg == 6 && table[SYS_listxattr].sy_narg == 4 && table[SYS_recvmsg].sy_narg == 3 ) { printf("[DEBUG] exit() address is %p\n", (void*)table[SYS_exit].sy_call); return(data_address+i); } i++; } } return(0); }
int main(int argc, char ** argv) { int option = 0; struct config cfg = {0}; header(); if (argc < 2) { usage(); } while( (option=getopt(argc,argv,"ha:Aco:Ci:rRs")) != -1 ) { switch(option) { case 'h': usage(); exit(1); case 'a': cfg.interrupt = atoi(optarg); break; case 'A': cfg.show_all_descriptors = 1; break; case 'c': cfg.create_file_archive = 1; break; case 'r': cfg.read_file_archive = 1; break; case 'R': cfg.restore_idt = 1; break; case 'o': if(strlen(optarg) > MAXPATHLEN - 1) { ERROR_MSG("File name too long."); return -1; } strncpy(cfg.out_filename, optarg, sizeof(cfg.out_filename)); break; case 'C': cfg.compare_idt = 1; break; case 'i': if(strlen(optarg) > MAXPATHLEN - 1) { ERROR_MSG("File name too long."); return -1; } strncpy(cfg.in_filename, optarg, sizeof(cfg.in_filename)); break; case 's': cfg.resolve = 1; break; } } OUTPUT_MSG(""); if (getuid() != 0) { ERROR_MSG("This program needs to be run as root!"); return -1; } cfg.kernel_type = get_kernel_type(); if (cfg.kernel_type == -1) { ERROR_MSG("Unable to retrieve kernel type."); return -1; } else if (cfg.kernel_type == X86) { ERROR_MSG("32 bits kernels not supported."); return -1; } cfg.idt_addr = get_addr_idt(cfg.kernel_type); cfg.idt_size = get_size_idt(); cfg.idt_entries = cfg.idt_size / sizeof(struct descriptor_idt); /* we need to populate the size variable else syscall fails */ cfg.kaslr_size = sizeof(cfg.kaslr_size); get_kaslr_slide(&cfg.kaslr_size, &cfg.kaslr_slide); OUTPUT_MSG("[INFO] Kaslr slide is 0x%llx", cfg.kaslr_slide); OUTPUT_MSG("[INFO] IDT base address is: 0x%llx", cfg.idt_addr); OUTPUT_MSG("[INFO] IDT size: 0x%x\n", cfg.idt_size); /* test if we can read kernel memory using processor_set_tasks() vulnerability */ /* vulnerability presented at BlackHat Asia 2014 by Ming-chieh Pan, Sung-ting Tsai. */ /* also described in Mac OS X and iOS Internals, page 387 */ host_t host_port = mach_host_self(); mach_port_t proc_set_default = 0; mach_port_t proc_set_default_control = 0; task_array_t all_tasks = NULL; mach_msg_type_number_t all_tasks_cnt = 0; kern_return_t kr = 0; int valid_kernel_port = 0; kr = processor_set_default(host_port, &proc_set_default); if (kr == KERN_SUCCESS) { kr = host_processor_set_priv(host_port, proc_set_default, &proc_set_default_control); if (kr == KERN_SUCCESS) { kr = processor_set_tasks(proc_set_default_control, &all_tasks, &all_tasks_cnt); if (kr == KERN_SUCCESS) { DEBUG_MSG("Found valid kernel port using processor_set_tasks() vulnerability!"); cfg.kernel_port = all_tasks[0]; valid_kernel_port = 1; } } } /* if we can't use the vulnerability then try /dev/kmem */ if (valid_kernel_port == 0) { if( (cfg.fd_kmem = open("/dev/kmem",O_RDWR)) == -1 ) { ERROR_MSG("Error while opening /dev/kmem. Is /dev/kmem enabled?"); ERROR_MSG("Verify that /Library/Preferences/SystemConfiguration/com.apple.Boot.plist has kmem=1 parameter configured."); return -1; } } if (cfg.resolve == 1) { retrieve_kernel_symbols(&cfg); } if(cfg.interrupt >= 0 || cfg.show_all_descriptors == 1) { show_idt_info(&cfg); } if(cfg.create_file_archive == 1) { create_idt_archive(&cfg); } if(cfg.read_file_archive == 1) { read_idt_archive(&cfg); } if(cfg.compare_idt == 1) { compare_idt(&cfg); } if(cfg.restore_idt == 1) { compare_idt(&cfg); } return 0; }
static void* limsw_watcher( void* arg) { int i; unsigned int gpio[ nr_limits]; char buf[ 3]; int len; if (debug_flags & DEBUG_LIMSW) { printf( "Limit switches watcher thread: opening fds"); } for (i = 0 ; i < nr_limits ; ++i) { gpio[ i] = limit_gpios[ i]; if (get_kernel_type() == e_kernel_3_2) { gpio_write_int_value_to_file( "export", gpio[ i]); gpio_write_value_to_pin_file( gpio[ i], "direction", "in"); } gpio_write_value_to_pin_file( gpio[ i], "edge", "both"); gpio_fd[ i] = gpio_open_file( gpio[ i], "value"); if (gpio_fd[ i] < 0) { fprintf( stderr, ", open failed for gpio%d, bailing out\n", gpio[ i]); pthread_exit( NULL); } } atexit( limsw_exit); for (i = 0 ; i < nr_limits ; ++i) { (*fdset)[ i].fd = gpio_fd[ i]; (*fdset)[ i].events = POLLPRI; } if (debug_flags & DEBUG_LIMSW) { printf( ", start polling on:"); for (i = 0 ; i < nr_limits ; ++i) { printf( " gpio%d", gpio[ i]); } printf( "\n"); } while (1) { const int timeout = -1; /* no timeout */ int rc = poll( *fdset, nr_limits, timeout); if (rc < 0) { if (errno == EINTR) { continue; } perror( "limsw_watcher: poll() failed, bailing out!"); break; } if (rc == 0) { // poll timeout, shouldn't happen with 'timeout' == -1 !!! fprintf( stderr, "\nlimsw_watcher: poll() unexpected timeout, bailing out!\n"); break; } for (i = 0 ; i < nr_limits ; ++i) { if ((*fdset)[ i].revents & POLLPRI) { int state = 0; len = read( (*fdset)[ i].fd, buf, sizeof( buf)); if (len == 2 && (buf[ 0] == '0' || buf[ 0] == '1')) { // Get initial state info state = buf[ 0] - '0'; if (debug_flags & DEBUG_LIMSW) { printf( "*** GPIO %2d event, state %d ***\n", gpio[ i], state); } } else { fprintf( stderr, "\n*** GPIO %2d unexpected event, BUG??? ***\n", gpio[ i]); } switch (gpio[ i]) { case XMIN_GPIO: x_min_state = state; break; case XMAX_GPIO: x_max_state = state; break; case YMIN_GPIO: y_min_state = state; break; case YMAX_GPIO: y_max_state = state; break; case ZMIN_GPIO: z_min_state = state; break; case ZMAX_GPIO: z_max_state = state; break; } lseek( (*fdset)[ i].fd, 0, SEEK_SET); } } } pthread_exit( NULL); }
int main(int argc, char ** argv) { header(); // we need to run this as root if (getuid() != 0) { printf("[ERROR] Please run me as root!\n"); exit(1); } int8_t kernel_type = get_kernel_type(); if (kernel_type == -1) { printf("[ERROR] Unable to retrieve kernel type!\n"); exit(1); } if((fd_kmem = open("/dev/kmem",O_RDWR)) == -1) { fprintf(stderr,"[ERROR] Error while opening /dev/kmem. Is /dev/kmem enabled?\n"); fprintf(stderr,"Add parameter kmem=1 to /Library/Preferences/SystemConfiguration/com.apple.Boot.plist\n"); exit(1); } // retrieve int80 address idt_t idt_address = get_addr_idt(kernel_type); uint64_t int80_address = calculate_int80address(idt_address, kernel_type); uint64_t kernel_base = find_kernel_base(int80_address, kernel_type); if (kernel_base == 0) { fprintf(stderr, "[ERROR] Could not find kernel base address!\n"); exit(1); } uint64_t data_address = 0; uint64_t data_size = 0; process_header(kernel_base, &data_address, &data_size); uint8_t *read = malloc((size_t)data_size); if (read == NULL) { printf("[ERROR] Memory allocation failed!\n"); exit(1); } // read kernel memory and find sysent readkmem(fd_kmem, read, data_address, (size_t)data_size); uint64_t sysent_address = find_sysent(read, data_address, data_size); if (sysent_address) { printf("[OK] Found sysent address at %p\n",(void*)sysent_address); } else { printf("[ERROR] Could not found sysent address!\n"); } free(read); return 0; }