/* This code is in the Public Domain. Any use for any purpose is explicitly allowed. */ #include #include #include #include #include #include #include #include void check(int cond, char* msg) { if (!cond) { printf("%s\n", msg); exit(-1); } } int main() { pid_t child = fork(); if (child == 0) { /* PT_TRACE_ME will stop the process after the execl is executed and allows the parent to take control. */ check(!ptrace(PT_TRACE_ME, 0, 0, 0), "PT_TRACE_ME failed."); execl("./hello", "hello", NULL); } else { /* Get the task for this pid. Seems to require superuser privileges or some gid hack. Go Apple! */ mach_port_t task; check(task_for_pid(mach_task_self(), child, &task) == KERN_SUCCESS, "task_for_pid failed."); /* Get the list of threads in that process (we expect one thread exactly.) */ thread_act_port_array_t threadList; mach_msg_type_number_t threadCount; check(task_threads(task, &threadList, &threadCount) == KERN_SUCCESS, "task_threads failed."); check(threadCount == 1, "task has more than one thread."); /* If we find the syscall we are looking for we set this flag, continue and then check the result of the mmap syscall, which is the address of the new mapping. */ int after_syscall = 0; while (1) { /* Wait for updates from the child process we are tracing */ int status; pid_t w = wait(&status); /* Read the register state of the child via Mach (since we are single-stepping the child is guaranteed to be suspended at the moment. */ x86_thread_state64_t state; mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT; check(thread_get_state(threadList[0], x86_THREAD_STATE64, (thread_state_t)&state, &stateCount) == KERN_SUCCESS, "thread_get_state failed."); /* If the previous step found an interesting system call, and we just stepped over it, analyze it now. */ if (after_syscall) { printf("I think the file was mapped to %lx.\n", state.__rax); /* remote process is now stopped at the instruction after the syscall, and we can take control of it */ exit(0); } /* 0xc5 is mmap on Darwin, and we also check for the length of the mmap we are looking for (0x88000). Check for other criteria (i.e. rsi > some_large_size_of_image) at your leasure. */ if (((state.__rax & 0xffff) == 0xc5) && (state.__rsi == 0x88000)) { /* To confirm this is a syscall we have to read the machince code at that address */ char data[16]; vm_size_t data_count; check(vm_read_overwrite(task, (vm_address_t)state.__rip, 16, (vm_address_t)data, &data_count) == KERN_SUCCESS, "vm_read_overwrite failed."); check(data_count == 16, "vm_read_overwrite didn't return 16 bytes as expected."); /* System calls on Darwin use SYSCALL (0x0F 0x05) */ if ((data[0] == 0x0f) && (data[1] == 0x05)) { /* Step over the syscall and analyze the result next time around. */ after_syscall = 1; } } check(ptrace(PT_STEP, child, (char*)1, 0) == 0, "PT_STEP failed."); } } }