Sto utilizzando ptrace
per tracciare le syscalls di un processo. Dopo aver forato il processo, utilizzo PTRACE_TRACEME
per iniziare a tracciare il processo. Il codice è simile al seguente:Analisi di syscalls di un processo e di tutti i processi forked
while (true) {
int status;
int gotPid;
gotPid = waitpid(pid, &status, 0);
if (WIFEXITED(status) || WIFSIGNALED(status)) {
break;
}
if (WIFSTOPPED(status)) {
handleTrace();
}
}
Poi c'è la funzione di handleTrace
, che assomiglia a questo.
long syscall;
syscall = ptrace(PTRACE_PEEKUSER,
pid, 8 * ORIG_RAX, NULL);
// do something with the syscall
// continue program
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
Questo è tutto buono, ma se le forche programma (o crea un nuovo thread) ho anche voglia di rintracciare il bambino elabora il processo di tracciato crea (e anche i thread creati dal processo). So che può essere fatto usando PTRACE_O_TRACEFORK
, PTRACE_O_TRACEVFORK
e PTRACE_O_TRACECLONE
, ma dalla documentazione man
, è molto difficile capire come esattamente è fatto. Ho bisogno di alcuni esempi su questo.
Edit:
Ho trovato una domanda simile qui: How to ptrace a multi-threaded application? ho provato con il seguente codice. Questo codice tiene traccia delle chiamate di sistema del processo avviato e deve monitorare anche i processi biforcati. Viene eseguito dopo un fork()
nel processo principale (il figlio chiama uno PTRACE_TRACEME
e uno exec()
).
Edit2:
ho fatto alcune più modifiche sul codice, con un po 'più progressi.
long orig_eax;
int status;
int numPrograms = 1;
while(1) {
int pid;
CHECK_ERROR_VALUE(pid = waitpid(-1, &status, __WALL));
std::cout << pid << ": Got event." << std::endl;
if(WIFEXITED(status) || WIFSIGNALED(status)) {
std::cout << pid << ": Program exited." << std::endl;
if (--numPrograms == 0) {
break;
}
continue;
}
if (status >> 16 == PTRACE_EVENT_FORK || status >> 16 == PTRACE_EVENT_VFORK ||
status >> 16 == PTRACE_EVENT_CLONE) {
int newpid;
CHECK_ERROR_VALUE(ptrace(PTRACE_GETEVENTMSG, child, NULL, (long) &newpid));
std::cout << pid << ": Attached to offspring " << newpid << std::endl;
boost::this_thread::sleep(boost::posix_time::millisec(100));
CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
newpid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL, newpid, NULL, NULL));
++numPrograms;
} else {
CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
pid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
CHECK_ERROR_VALUE(orig_eax = ptrace(PTRACE_PEEKUSER,
pid, 8 * ORIG_RAX, NULL));
std::cout << pid << ": Syscall called: " <<
SyscallMap::instance().get().right.at(orig_eax) <<
std::endl;
CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL,
pid, NULL, NULL));
}
}
CHECK_ERROR_VALUE
è solo una macro che controlla il codice di risultato e un'eccezione con la descrizione delle errno
in esso.
Apparentemente, quando ottengo l'evento di un fork/clone il nuovo processo non esiste ancora e ottengo un messaggio di errore "Process does not exist" se provo a eseguirne il ptrace. Se metto un po 'di sonno prima di provare a scrivere il nuovo processo, non ricevo un messaggio di errore. Ora quando il programma arriva al punto di fork/clone, inizia a tracciare il nuovo processo, ma non arriva mai al punto di ritorno del syscall clone()
nel processo padre, il che significa che il figlio termina correttamente, ma il genitore si blocca al suo punto di forchetta.