Leggi la pila di un altro processo?


16

Sto cercando di leggere lo stack di un processo figlio ma senza fortuna. So che è possibile utilizzarlo ptrace, ma ptracel'interfaccia di ti permette di leggere solo una parola alla volta e sto cercando di scansionare porzioni più grandi dello stack.

Ho anche provato a leggere i /proc/$pid/memlimiti dello stack come estratto dal /proc/$pid/mapsfile dopo aver usato ptrace per collegarlo (come suggerito qui ) ma la lettura continua a fallire (anche quando viene eseguita come root) anche se lo stesso codice ha esito positivo quando provato lettura da diverse parti del processo (ad es. heap).

Che cosa sto facendo di sbagliato? C'è qualche altra opzione?


Hai chiamato waitpidtra ptrace(PTRACE_ATTACH,…)e read(altrimenti c'è una possibile condizione di gara)? Quale errore readrestituisce? Il bambino sta facendo qualcosa di strano con la sua mappatura della memoria? Puoi provare il tuo codice con un bambino semplice come sleep?
Gilles 'SO- smetti di essere malvagio' il

Ho usato wait after ptrace e ho inserito uno scanf nel bambino per costringerlo ad aspettare.
user4537

Questo è solo su Linux? Solaris ha anche un filesystem / proc, ma è completamente diverso da Linux, vigilia filosoficamente. Molti "file binari".
Bruce Ediger,

basta fare un sistema ("pstack pid ") e analizzare l'output ..
vrdhn

Risposte:


5

ptraceL'interfaccia ti permette di leggere solo una parola alla volta e sto cercando di scansionare porzioni più grandi dello stack

Bene, basta usare un loop, quindi. Onestamente non vedo come ciò costituisca un problema ptrace, lo uso sempre per accedere ai processi in remoto.

Uso qualcosa del genere:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;

    while (n)
    {
        size_t todo = MIN(n, sizeof(long) - (src & align));
        long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
        if (errno)
        {
            perror("ptrace_peektext (memcpy_from_target)");
            return -1;
        }
        memcpy(dest, (char *)&data + (src & align), todo);

        dest += todo; src += todo; n -= todo;
    }

    return 0;
}

Ciao Sam, mentre il tuo codice farà (e in realtà è quello che sto facendo attualmente), ha un grande sovraccarico di prestazioni.
user4537

@ user4536: vedo. Ho in mente un'altra strategia di cui posterò quando avrò tempo di scriverla. Quali sono le dimensioni tipiche dello stack?
Sam Hocevar,

È difficile dirlo davvero poiché la mia ricerca non presuppone una specifica dimensione dello stack, ma per il bene di questo argomento diciamo che è lungo almeno un paio di pagine Potresti lasciare un suggerimento sulla tua strategia? Grazie comunque dell'aiuto!
user4537

1

Ecco un'altra strategia che potrebbe richiedere modifiche, ma dovrebbe essere più efficiente con grandi blocchi di dati. L'idea è di eseguire syscall nel processo remoto per recuperare i contenuti dello stack. Avrà bisogno di un codice di architettura specifico ma se scegli come target solo x86 / x86_64 non dovrebbe essere troppo seccante.

  1. Creare una pipa denominata come "/tmp/fifo"nel processo di chiamata.
  2. Passare al processo tracciato fino a quando non ritorna da una syscall, usando PTRACE_SYSCALLstep, waitpid()per attendere e PTRACE_GETREGS/ PTRACE_PEEKTEXTper verificare il codice operativo attualmente eseguito.
  3. Eseguire il backup dei registri del processo remoto e di una piccola area del suo stack.
  4. Esegui chiamate di sistema sul processo remoto sovrascrivendo il suo stack con i propri dati: open("/tmp/fifo"), write()il contenuto dello stack, close()il descrittore.
  5. Ripristina lo stato del processo remoto.
  6. Leggi i dati Fifo dal processo di chiamata.

Potrebbero esserci alternative più eleganti alla pipa nominata, ma non riesco a pensare a nessuna in questo momento. Il motivo per cui utilizzo solo syscalls è perché l'iniezione di codice in remoto è piuttosto inaffidabile sui sistemi moderni a causa di varie protezioni di sicurezza. Lo svantaggio è che si bloccherà fino a quando il processo remoto non esegue un syscall (che può essere un problema per alcuni programmi che eseguono principalmente calcoli).

Puoi vedere del codice gratuito che implementa la maggior parte del lavoro in questo file sorgente . Feedback sul codice è il benvenuto!



1

Puoi facilmente leggere lo stack di un altro processo usando il file system proc (per questo avrai bisogno dell'accesso root). Prima di leggere arbitrariamente da / proc / pid / mem devi consultare / proc / pid / maps. Una semplice lettura in questo file mostra molte voci. Siamo interessati alla voce contrassegnata come pila. Una volta ottenuto questo, è necessario leggere i limiti inferiore e superiore della pila. Ora apri semplicemente il file / proc / pid / mem, cerca il limite inferiore dello stack e leggi la dimensione corretta dei dati.


1
Sei sicuro di voler dire memse no maps? (Non riesco a vedere alcuna memsvoce nel mio /procfilesystem.) L'OP ha già menzionato la lettura dei limiti dello stack da /proc/$pid/maps- cosa stai suggerendo di fare diversamente?
JigglyNaga,

Modificato l'errore di battitura. Ho fatto esattamente ciò che ho menzionato nella mia risposta e ho scaricato 132 KB di dati dello stack. Abbiamo bisogno di maggiori informazioni su ciò che OP ha fatto di sbagliato. Forse OP può condividere il codice che ha usato per leggere i limiti dello stack. Se non risponde, condividerò il mio.
Ajay Brahmakshatriya,

0

Potresti provare lsstack . Usa ptrace, proprio come ogni altro programma "leggi lo stack di un altro processo" di successo. Non sono riuscito a ottenere un programma usando / proc / $ pid / mem per funzionare. Credo che non puoi farlo in questo modo, anche se, logicamente, dovresti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.