static int do_fork(struct ptrace_child *child) { if (ptrace_syscall_numbers(child)->nr_fork != -1) { return do_syscall(child, fork, 0, 0, 0, 0, 0, 0); } else { return do_syscall(child, clone, SIGCHLD, 0, 0, 0, 0, 0); } }
static int do_dup2(struct ptrace_child *child, int oldfd, int newfd) { if (oldfd == newfd) { return 0; } if (ptrace_syscall_numbers(child)->nr_dup2 != -1) { return do_syscall(child, dup2, oldfd, newfd, 0, 0, 0, 0); } else { return do_syscall(child, dup3, oldfd, newfd, 0, 0, 0, 0); } }
int mmap_scratch(struct ptrace_child *child, child_addr_t *addr) { long mmap_syscall; child_addr_t scratch_page; mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap2; if (mmap_syscall == -1) mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap; scratch_page = ptrace_remote_syscall(child, mmap_syscall, 0, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); //MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (scratch_page > (unsigned long) - 1000) { return -(signed long)scratch_page; } *addr = scratch_page; debug("Allocated scratch page: %lx", scratch_page); return 0; }
int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) { int err; if (ptrace_syscall_numbers(child)->nr_signal != -1) { err = do_syscall(child, signal, SIGHUP, (unsigned long)SIG_IGN, 0, 0, 0, 0); } else { struct sigaction act = { .sa_handler = SIG_IGN, }; err = ptrace_memcpy_to_child(child, scratch_page, &act, sizeof act); if (err < 0) return err; err = do_syscall(child, rt_sigaction, SIGHUP, scratch_page, 0, 8, 0, 0); } return err; }
int attach_child(pid_t pid, const char *pty, int force_stdio) { struct ptrace_child child; unsigned long scratch_page = -1; int *child_tty_fds = NULL, n_fds, child_fd, statfd; int i; int err = 0; long page_size = sysconf(_SC_PAGE_SIZE); char stat_path[PATH_MAX]; long mmap_syscall; if ((err = copy_tty_state(pid, pty))) { if (err == ENOTTY && !force_stdio) { error("Target is not connected to a terminal.\n" " Use -s to force attaching anyways."); return err; } } snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); statfd = open(stat_path, O_RDONLY); if (statfd < 0) { error("Unable to open %s: %s", stat_path, strerror(errno)); return -statfd; } kill(pid, SIGTSTP); wait_for_stop(pid, statfd); if (ptrace_attach_child(&child, pid)) { err = child.error; goto out_cont; } if (ptrace_advance_to_state(&child, ptrace_at_syscall)) { err = child.error; goto out_detach; } if (ptrace_save_regs(&child)) { err = child.error; goto out_detach; } mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap2; if (mmap_syscall == -1) mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap; scratch_page = ptrace_remote_syscall(&child, mmap_syscall, 0, page_size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if (scratch_page > (unsigned long)-1000) { err = -(signed long)scratch_page; goto out_unmap; } debug("Allocated scratch page: %lx", scratch_page); if (force_stdio) { child_tty_fds = malloc(3 * sizeof(int)); if (!child_tty_fds) { err = ENOMEM; goto out_unmap; } n_fds = 3; child_tty_fds[0] = 0; child_tty_fds[1] = 1; child_tty_fds[2] = 2; } else { child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds); if (!child_tty_fds) { err = child.error; goto out_unmap; } } if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty)+1)) { err = child.error; error("Unable to memcpy the pty path to child."); goto out_free_fds; } child_fd = do_syscall(&child, open, scratch_page, O_RDWR|O_NOCTTY, 0, 0, 0, 0); if (child_fd < 0) { err = child_fd; error("Unable to open the tty in the child."); goto out_free_fds; } debug("Opened the new tty in the child: %d", child_fd); err = ignore_hup(&child, scratch_page); if (err < 0) goto out_close; err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0); if (err != child.pid) { debug("Target is not a session leader, attempting to setsid."); err = do_setsid(&child); } else { do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0); } if (err < 0) goto out_close; err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 0, 0, 0, 0); if (err < 0) { error("Unable to set controlling terminal."); goto out_close; } debug("Set the controlling tty"); for (i = 0; i < n_fds; i++) do_syscall(&child, dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0); err = 0; out_close: do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0); out_free_fds: free(child_tty_fds); out_unmap: do_unmap(&child, scratch_page, page_size); ptrace_restore_regs(&child); out_detach: ptrace_detach_child(&child); if (err == 0) { kill(child.pid, SIGSTOP); wait_for_stop(child.pid, statfd); } kill(child.pid, SIGWINCH); out_cont: kill(child.pid, SIGCONT); close(statfd); return err < 0 ? -err : err; }