2009-07-30 16 views
10

Sto scrivendo uno strumento che chiama attraverso DTrace per tracciare il programma specificato dall'utente.Come tracciare un programma sin dall'inizio senza eseguirlo come root

Se il mio strumento utilizza dtrace -c per eseguire il programma come sottoprocesso di DTrace, non solo non posso passare alcun argomento al programma, ma il programma viene eseguito con tutti i privilegi di DTrace, cioè come root (Sono su Mac OS X). Questo rende certe cose che dovrebbero funzionare, e ovviamente rende molte cose che non dovrebbero funzionare.

L'altra soluzione che conosco è quello di avviare il programma stesso, mettere in pausa inviandolo SIGSTOP, passare il suo PID dtrace -p, per poi proseguire con l'invio di esso SIGCONT. Il problema è che il programma viene eseguito per alcuni secondi senza essere tracciato mentre DTrace raccoglie le informazioni sui simboli o, se dormo per alcuni secondi prima di continuare il processo, DTrace lamenta che objc<pid>:<class>:<method>:entry non corrisponde a nessun probe.

Esiste un modo per eseguire il programma con l'account dell'utente, non come root, ma avere ancora DTrace in grado di tracciarlo dall'inizio?

risposta

2

Creare un programma di avvio che attenderà un segnale di qualche tipo (non necessariamente un segnale letterale, solo un'indicazione che è pronto), quindi exec() il target. Ora dtrace -p il programma di avvio e una volta dtrace è attivo, lascia andare il programma di avvio.

+1

Ancora meglio: arrotolare tutto in uno. Crea una pipe, fork, aspetta il bambino sulla pipe, dtrace -p CHILD_PID, scrivi nella pipe, il figlio si sveglia e chiama exec. – bstpierre

+0

I suoni promettenti. Non so che funzionerà, però: il mio strumento è scritto in Python ed è progettato per tracciare i programmi Cocoa. Senza alcun Cocoa nel mio strumento, penso che otterrò comunque l'errore che il provider objc non soddisfa. Ma ci proverò domani. –

3

Se l'altra risposta non funziona per voi, è possibile eseguire il programma in gdb, interrompere in main (o anche prima), ottenere il pid e avviare lo script? L'ho provato in passato e sembrava funzionare.

5

Qualcosa come sudo dtruss -f sudo -u <original username> <command> ha funzionato per me, ma dopo mi sono sentito in colpa.

Ho archiviato un bug Radar e l'ho chiuso come duplicato di # 5108629.

3

Bene, questo è un po 'vecchio, ma perché non :-) ..

Io non credo che ci sia un modo per fare questo è sufficiente da riga di comando, ma come suggerito, una semplice applicazione launcher, come il seguente, lo farebbe. Naturalmente il collegamento manuale potrebbe anche essere sostituito con alcune chiamate a libdtrace.

int main(int argc, char *argv[]) { 
    pid_t pid = fork(); 
    if(pid == 0) { 
     setuid(123); 
     seteuid(123); 
     ptrace(PT_TRACE_ME, 0, NULL, 0); 
     execl("/bin/ls", "/bin/ls", NULL); 
    } else if(pid > 0) { 
     int status; 
     wait(&status); 

     printf("Process %d started. Attach now, and click enter.\n", pid); 
     getchar(); 

     ptrace(PT_CONTINUE, pid, (caddr_t) 1, 0); 
    } 

    return 0; 
} 
3

Questo script prende il nome del file eseguibile (per un app questo è CFBundleExecutable del info.plist) che si desidera monitorare per DTrace come parametro (è possibile lanciare l'applicazione di destinazione dopo questo script è in esecuzione) :

string gTarget;  /* the name of the target executable */ 

dtrace:::BEGIN 
{ 
    gTarget = $$1; /* get the target execname from 1st DTrace parameter */ 

    /* 
    * Note: DTrace's execname is limited to 15 characters so if $$1 has more 
    * than 15 characters the simple string comparison "($$1 == execname)" 
    * will fail. We work around this by copying the parameter passed in $$1 
    * to gTarget and truncating that to 15 characters. 
    */ 

    gTarget[15] = 0;  /* truncate to 15 bytes */ 
    gTargetPID = -1;  /* invalidate target pid */ 
} 

/* 
* capture target launch (success) 
*/ 
proc:::exec-success 
/
    gTarget == execname 
/
{ 
    gTargetPID = pid; 
} 

/* 
* detect when our target exits 
*/ 
syscall::*exit:entry 
/
    pid == gTargetPID 
/
{ 
    gTargetPID = -1;  /* invalidate target pid */ 
} 

/* 
* capture open arguments 
*/ 
syscall::open*:entry 
/
    ((pid == gTargetPID) || progenyof(gTargetPID)) 
/
{ 
    self->arg0 = arg0; 
    self->arg1 = arg1; 
} 

/* 
* track opens 
*/ 
syscall::open*:return 
/
    ((pid == gTargetPID) || progenyof(gTargetPID)) 
/
{ 
    this->op_kind = ((self->arg1 & O_ACCMODE) == O_RDONLY) ? "READ" : "WRITE"; 
    this->path0 = self->arg0 ? copyinstr(self->arg0) : "<nil>"; 

    printf("open for %s: <%s> #%d", 
     this->op_kind, 
     this->path0, 
     arg0); 
} 
+0

Questo è ottimo, tranne che quando si esegue questa operazione, dtrace non trova alcun punto di traccia del provider di pid. Ad esempio, ottengo un errore: "probe description pid * :: confstr: return non corrisponde a nessun probe" – Droopycom

1

dtruss ha la possibilità -n in cui è possibile specificare il nome del processo che si desidera rintracciare, senza avviare esso (credito a quest'ultima parte del @ di kenorb risposta a https://stackoverflow.com/a/11706251/970301). Quindi, qualcosa come il seguente dovrebbe farlo:

sudo dtruss -n "$program" 
$program 
0

Vedi my answer sul relativo question "Come può ottenere DTrace per eseguire il comando tracciata con privilegi non root?" [Sic].

In sostanza, è possibile avviare un processo in background (non root) che attende 1sec per l'avvio di DTrace (mi dispiace per la condizione di competizione) e snoop il PID di quel processo.

sudo true && \ 
(sleep 1; cat /etc/hosts) &; \ 
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $! \ 
&& kill $! 

spiegazione completa in risposta collegato.

Problemi correlati