2012-11-23 17 views
7

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.

risposta

1

Strace fa questo ed è il file README.linux ha alcune informazioni sul tema:

http://strace.git.sourceforge.net/git/gitweb.cgi?p=strace/strace;a=blob;f=README-linux-ptrace;h=97e2c019a075f216f125f8b9aa6da68fa2abf230;hb=refs/heads/master

Sembra essere lamentano bug del kernel in questa piattaforma, in modo YMMV.

Il codice spiega come ottenere il pid del bambino. Tuttavia potrebbe essere possibile che il bambino ottenga un altro userid a causa dei bit setuid o setgid impostati sui binari che chiama. Quindi la risposta è che chiami ptrace sul PID figlio e vedi se riesci ad accedere.

Ecco la sezione pertinente:

PTRACE_EVENT stops are observed by tracer as waitpid returning with 
WIFSTOPPED(status) == true, WSTOPSIG(status) == SIGTRAP. Additional bit 
is set in a higher byte of status word: value ((status >> 8) & 0xffff) 
will be (SIGTRAP | PTRACE_EVENT_foo << 8). The following events exist: 

PTRACE_EVENT_VFORK - stop before return from vfork/clone+CLONE_VFORK. 
When tracee is continued after this, it will wait for child to 
exit/exec before continuing its execution (IOW: usual behavior on 
vfork). 

PTRACE_EVENT_FORK - stop before return from fork/clone+SIGCHLD 

PTRACE_EVENT_CLONE - stop before return from clone 

PTRACE_EVENT_VFORK_DONE - stop before return from 
vfork/clone+CLONE_VFORK, but after vfork child unblocked this tracee by 
exiting or exec'ing. 

For all four stops described above: stop occurs in parent, not in newly 
created thread. PTRACE_GETEVENTMSG can be used to retrieve new thread's 
tid. 
Problemi correlati