uint64_t calculate_int80address(const uint64_t idt_address, uint8_t kernel_type) { // find the address of interrupt 0x80 - EXCEP64_SPC_USR(0x80,hi64_unix_scall) @ osfmk/i386/idt64.s struct descriptor_idt *int80_descriptor = NULL; uint64_t int80_address = 0; uint64_t high = 0; uint32_t middle = 0; int80_descriptor = malloc(sizeof(struct descriptor_idt)); // retrieve the descriptor for interrupt 0x80 // the IDT is an array of descriptors readkmem(fd_kmem, int80_descriptor, idt_address+sizeof(struct descriptor_idt)*0x80, sizeof(struct descriptor_idt)); // we need to compute the address, it's not direct if (kernel_type) { // extract the stub address high = (unsigned long)int80_descriptor->offset_high << 32; middle = (unsigned int)int80_descriptor->offset_middle << 16; int80_address = (uint64_t)(high + middle + int80_descriptor->offset_low); } else { int80_address = (uint32_t)(int80_descriptor->offset_middle << 16) + int80_descriptor->offset_low; } printf("[OK] Address of interrupt 80 stub is %p\n", (void*)int80_address); return(int80_address); }
/* * r - read data from kernel * p - put data to kernel * pr - print data to screen */ static void rdata (addr address, void *data, unint size) { unint n; n = readkmem(address, data, size); if (n < size) { err_jmp("not enough data"); } }
uint64_t find_kernel_base(const uint64_t int80_address, uint8_t kernel_type) { uint64_t temp_address = int80_address; // the step amount to search backwards from int80 uint16_t step_value = 500; // step must be at least sizeof mach_header and a segment_command uint16_t length = step_value; uint8_t *temp_buffer = malloc(step_value); if (kernel_type) // 64bits { struct segment_command_64 *segment_command = NULL; while (temp_address > 0) { // read the kernel mem contents readkmem(fd_kmem, temp_buffer, temp_address, length); // iterate thru buffer contents, searching for mach-o magic value for (uint32_t x = 0; x < length; x++) { if (*(uint32_t*)(temp_buffer+x) == MH_MAGIC_64) { segment_command = (struct segment_command_64*)(temp_buffer+x+sizeof(struct mach_header_64)); if (strncmp(segment_command->segname, "__TEXT", 16) == 0) { printf("[OK] Found kernel mach-o header address at %p\n", (void*)(temp_address+x)); return((uint64_t)(temp_address+x)); } } } // verify if next block to be read is valid or not // adjust the step value to a smaller value so we can proceed while(readkmem(fd_kmem, temp_buffer, temp_address-step_value, length) == -2) { step_value = 1; // we could find out which is the biggest acceptable value // but it seems like a waste of time - I'm an Economist :P // we can read smaller values to avoid overlapping length = sizeof(struct mach_header_64) + sizeof(struct segment_command_64); } // check for int overflow if (temp_address - step_value > temp_address) break; temp_address -= step_value; } } else // 32bits { struct segment_command *segment_command = NULL; while (temp_address > 0) { readkmem(fd_kmem, temp_buffer, temp_address, length); for (uint32_t x = 0; x < length; x++) { if (*(uint32_t*)(temp_buffer+x) == MH_MAGIC) { segment_command = (struct segment_command*)(temp_buffer+x+sizeof(struct mach_header)); if (strncmp(segment_command->segname, "__TEXT", 16) == 0) { printf("[OK] Found kernel mach-o header address at %p\n", (void*)(temp_address+x)); return((uint32_t)(temp_address+x)); } } } if(readkmem(fd_kmem, temp_buffer, temp_address-step_value, length) == -2) { step_value = 1; length = sizeof(struct mach_header) + sizeof(struct segment_command); } // check for int overflow if (temp_address - step_value > temp_address) break; temp_address -= step_value; } } return(0); }
uint8_t process_header(const uint64_t target_address, uint64_t *data_address, uint64_t *data_size) { uint8_t *header_buffer = malloc(1000); readkmem(fd_kmem, header_buffer, target_address, 1000); // verify if it's a valid mach-o binary uint8_t *address = NULL; uint32_t nrLoadCmds = 0; uint32_t magic = *(uint32_t*)(header_buffer); if (magic == MH_MAGIC) { struct mach_header *machHeader = (struct mach_header*)(header_buffer); nrLoadCmds = machHeader->ncmds; // first load cmd address address = (uint8_t*)(header_buffer + sizeof(struct mach_header)); } else if (magic == MH_MAGIC_64) { struct mach_header_64 *machHeader = (struct mach_header_64*)(header_buffer); nrLoadCmds = machHeader->ncmds; // first load cmd address address = (uint8_t*)(header_buffer + sizeof(struct mach_header_64)); } // error else { return(1); } // find the last command offset struct load_command *loadCommand = NULL; for (uint32_t i = 0; i < nrLoadCmds; i++) { loadCommand = (struct load_command*)address; switch (loadCommand->cmd) { case LC_SEGMENT: { struct segment_command *segmentCommand = (struct segment_command *)(loadCommand); if (strncmp(segmentCommand->segname, "__DATA", 16) == 0) { *data_address = segmentCommand->vmaddr; *data_size = segmentCommand->vmsize; printf("[OK] Found __DATA segment at %p (size:0x%llx)\n", (void*)*data_address, *data_size); } break; } case LC_SEGMENT_64: { struct segment_command_64 *segmentCommand = (struct segment_command_64 *)(loadCommand); if (strncmp(segmentCommand->segname, "__DATA", 16) == 0) { *data_address = segmentCommand->vmaddr; *data_size = segmentCommand->vmsize; printf("[OK] Found __DATA segment at %p (size:0x%llx)\n", (void*)*data_address, *data_size); } break; } } // advance to next command address += loadCommand->cmdsize; } return 0; }
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; }