/* * check if a found kernel is the one we are running atm */ static boolean_t is_current_kernel(void *kernel_header) { // search backwards for the kernel base address (mach-o header) mach_vm_address_t kernel_base = find_kernel_base(); uint64_t *uuid1 = get_uuid(kernel_header); uint64_t *uuid2 = get_uuid((void*)kernel_base); if(!uuid1 || !uuid2) { return FALSE; } return uuid1[0] == uuid2[0] && uuid1[1] == uuid2[1]; }
/* * retrieve the __TEXT address of current loaded kernel so we can compute the KASLR slide * also the size of __text */ static kern_return_t get_running_text_address(kernel_info *kinfo, mach_vm_address_t slide) { // search backwards for the kernel base address (mach-o header) mach_vm_address_t base = slide ? slide : find_kernel_base(); if (base) { // get the vm address of __TEXT segment struct mach_header_64* mh = (void*)(base); size_t headerSize = sizeof(struct mach_header_64); struct load_command *loadCmd; uint8_t* addr = (uint8_t *)(base) + headerSize; uint8_t* endaddr = (uint8_t *)(base) + HEADER_SIZE; for (uint32_t i = 0; i < mh->ncmds; i++) { loadCmd = (struct load_command *)(addr); if (addr + sizeof(struct load_command) > endaddr || addr + loadCmd->cmdsize > endaddr) { // SYSLOG("mach", "running command %u of info %s exceeds header size", i, objectId ? objectId : "(null)"); return KERN_FAILURE; } if (loadCmd->cmd == LC_SEGMENT_64) { struct segment_command_64 *segCmd = (struct segment_command_64 *)(loadCmd); if (!strncmp(segCmd->segname, "__TEXT", sizeof(segCmd->segname))) { kinfo->running_text_addr = segCmd->vmaddr; kinfo->mh = mh; break; } } addr += loadCmd->cmdsize; } } // compute kaslr slide if (kinfo->running_text_addr && kinfo->mh) { if (!slide) // This is kernel image kinfo->kaslr_slide = kinfo->running_text_addr - kinfo->disk_text_addr; else // This is kext image kinfo->kaslr_slide = kinfo->prelink_slid ? kinfo->prelink_vmaddr : slide; kinfo->kaslr_slide_set = true; // DBGLOG("mach", "aslr/load slide is 0x%llx", kaslr_slide); } else { // SYSLOG("mach", "couldn't find the running addresses"); return KERN_FAILURE; } return KERN_SUCCESS; }
/* * brute force search sysent * this method works in all versions * returns a pointer to the sysent structure * Note: 32/64 bits compatible */ static void * bruteforce_sysent(mach_vm_address_t *out_kernel_base) { // retrieves the address of the IDT mach_vm_address_t idt_address = 0; get_addr_idt(&idt_address); LOG_DEBUG("IDT Address is 0x%llx", idt_address); // calculate the address of the int80 handler mach_vm_address_t int80_address = calculate_int80address(idt_address); // search backwards for the kernel base address (mach-o header) mach_vm_address_t kernel_base = find_kernel_base(int80_address); *out_kernel_base = kernel_base; uint64_t data_address = 0; uint64_t data_size = 0; // search for the __DATA segment process_header(kernel_base, &data_address, &data_size); uint64_t data_limit = data_address + data_size; // bruteforce search for sysent in __DATA segment while (data_address <= data_limit) { if (version_major == YOSEMITE || version_major == EL_CAPITAN) { struct sysent_yosemite *table = (struct sysent_yosemite*)data_address; if((void*)table != NULL && 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 ) { LOG_DEBUG("exit() address is %p", (void*)table[SYS_exit].sy_call); LOG_DEBUG("no. of args for exit() %d", table[SYS_exit].sy_narg); LOG_DEBUG("Success!! Done here."); return (void*)data_address; } } /* mavericks or higher */ else if (version_major == MAVERICKS) { struct sysent_mavericks *table = (struct sysent_mavericks*)data_address; if((void*)table != NULL && 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 ) { LOG_DEBUG("exit() address is %p", (void*)table[SYS_exit].sy_call); return (void*)data_address; } } /* all previous versions */ else { struct sysent *table = (struct sysent*)data_address; if((void*)table != NULL && 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 ) { LOG_DEBUG("exit() address is %p", (void*)table[SYS_exit].sy_call); return (void*)data_address; } } data_address++; } return 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; }