static void handle_sigtrap(struct child *son) { long scno, ret; const char *scname; /* We get this event twice, one at entering a * system call and one at exiting a system * call. */ if (son->insyscall) { if (!pink_util_get_return(son->pid, &ret)) err(1, "pink_util_get_return"); if (son->inexecve) { son->inexecve = false; if (ret == 0) { /* execve was successful */ /* Update bitness */ son->bitness = pink_bitness_get(son->pid); if (son->bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf(" = 0 (Updating the bitness of child %i to %s mode)\n", son->pid, pink_bitness_name(son->bitness)); son->printret = false; return; } } son->insyscall = false; if (!son->printret) { son->printret = true; return; } /* Exiting the system call, print the * return value. */ putchar(' '); print_ret(ret); putchar('\n'); } else { son->insyscall = true; /* Get the system call number and call * the appropriate decoder. */ if (!pink_util_get_syscall(son->pid, son->bitness, &scno)) err(1, "pink_util_get_syscall"); scname = pink_name_syscall(scno, son->bitness); assert(scname != NULL); if (!strcmp(scname, "execve")) son->inexecve = true; if (!strcmp(scname, "open")) decode_open(son->pid, son->bitness); else if (!strcmp(scname, "execve")) decode_execve(son->pid, son->bitness); else if (!strcmp(scname, "bind") || !strcmp(scname, "connect")) decode_socketcall(son->pid, son->bitness, scname); else printf("%s()", scname); } }
int main(int argc, char **argv) { int sig, status, exit_code; pink_event_t event; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); event = pink_event_decide(status); assert(event == PINK_EVENT_STOP); /* Set up the tracing options. */ if (!pink_trace_setup(son.pid, PINK_TRACE_OPTION_SYSGOOD | PINK_TRACE_OPTION_EXEC)) { perror("pink_trace_setup"); pink_trace_kill(son.pid); return EXIT_FAILURE; } /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.dead = son.insyscall = false; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event. */ event = pink_event_decide(status); switch (event) { case PINK_EVENT_SYSCALL: handle_syscall(&son); break; break; case PINK_EVENT_EXEC: /* Update bitness */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); else printf(" (Updating the bitness of child %i to %s mode)\n", son.pid, pink_bitness_name(son.bitness)); break; case PINK_EVENT_GENUINE: case PINK_EVENT_UNKNOWN: /* Send the signal to the traced child as it * was a genuine signal. */ sig = WSTOPSIG(status); break; case PINK_EVENT_EXIT_GENUINE: exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); son.dead = true; break; case PINK_EVENT_EXIT_SIGNAL: exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); son.dead = true; break; default: /* Nothing */ ; } if (son.dead) break; } return exit_code; } }
int main(int argc, char **argv) { int sig, status, exit_code; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.inexecve = son.insyscall = false; son.printret = true; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event */ if (WIFSTOPPED(status)) { if (WSTOPSIG(status) == SIGTRAP) handle_sigtrap(&son); else { /* Child received a genuine signal. * Send it to the child. */ sig = WSTOPSIG(status); } } else if (WIFEXITED(status)) { exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); break; } else if (WIFSIGNALED(status)) { exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); break; } } return exit_code; } }