2012-06-18 13 views
5

Estoy intentando codificar un programa que se rastrea a sí mismo para las llamadas al sistema. Estoy teniendo un momento difícil para hacer este trabajo. Intenté llamar a un tenedor() para crear una instancia de sí mismo (el código) y luego supervisar el proceso hijo resultante.¿Cómo rastrear un proceso para llamadas al sistema?

El objetivo es que el proceso principal devuelva el índice de cada llamada al sistema realizada por el proceso hijo y lo muestre en la pantalla. De alguna manera, no está funcionando según lo planeado.

Aquí está el código:

#include <unistd.h>  /* for read(), write(), close(), fork() */ 
#include <fcntl.h>  /* for open() */ 
#include <stdio.h> 
#include <sys/ptrace.h> 
#include <sys/reg.h> 
#include <sys/wait.h> 
#include <sys/types.h> 


int main(int argc, char *argv[]) { 
    pid_t child; 
    long orig_eax; 
    child = fork(); 

    if (0 == child) 
    { 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     if (argc != 3) { 
      fprintf(stderr, "Usage: copy <filefrom> <fileto>\n"); 
      return 1; 
     } 

     int c; 
     size_t file1_fd, file2_fd; 
     if ((file1_fd = open(argv[1], O_RDONLY)) < 0) { 
      fprintf(stderr, "copy: can't open %s\n", argv[1]); 
      return 1; 
     } 

     if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) { 
      fprintf(stderr, "copy: can't open %s\n", argv[2]); 
      return 1; 
     } 

     while (read(file1_fd, &c, 1) > 0) 
     write(file2_fd, &c, 1); 
    } 
    else 
    { 
     wait(NULL); 
     orig_eax = ptrace (PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL); 
     printf("copy made a system call %ld\n", orig_eax); 
     ptrace(PTRACE_CONT, child, NULL, NULL); 
    }   
return 0; 
} 

Este código se basa en este código:

#include <sys/ptrace.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <linux/user.h> /* For constants 
           ORIG_EAX etc */ 
int main() 
{ 
    pid_t child; 
    long orig_eax; 
    child = fork(); 
    if(child == 0) { 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     execl("/bin/ls", "ls", NULL); 
    } 
    else { 
     wait(NULL); 
     orig_eax = ptrace(PTRACE_PEEKUSER, 
          child, 4 * ORIG_EAX, 
          NULL); 
     printf("The child made a " 
       "system call %ld\n", orig_eax); 
     ptrace(PTRACE_CONT, child, NULL, NULL); 
    } 
    return 0; 
} 

La salida de éste es:

The child made a system call 11 

que es el índice de la llamada al sistema ejecutivo.

De acuerdo con las páginas man de espera():

All of these system calls are used to wait for state changes in a child 
of the calling process, and obtain information about the child whose 
state has changed. A state change is considered to be: the child terminated; 
the child was stopped by a signal; or the child was resumed by 
a signal. 

La manera en que yo entiendo es que cada vez que una llamada al sistema es invocado por un programa de usuario, el kernel primera inspeccionará si el proceso está siendo rastreado antes de ejecutar la rutina de llamada del sistema y pausa ese proceso con una señal y devuelve el control al padre. ¿No sería eso un cambio de estado?

+2

"De alguna manera no funciona según lo previsto." ¿Cuidado para elaborar? ¿Qué espera que suceda, y qué sucede en realidad? Por favor edita la pregunta para agregarla, no como un comentario. –

+7

Además, lo primero que haces en el proceso principal es llamar a 'esperar'. Esta función hace exactamente eso, espere hasta que el proceso hijo haya finalizado, lo que significa que la llamada 'ptrace' intenta rastrear un proceso que ya no existe. –

+0

[Refiérase a esto] [1] Creo que puede ser útil. [1]: http://stackoverflow.com/questions/6468896/why-is-orig-eax-provided-in-addition-to-eax –

Respuesta

0

En su matriz, ¿cuántas llamadas desea supervisar? Si quieres más de uno vas a necesitar algún tipo de ciclo.

Nota de la línea en el ejemplo, es importante:

ptrace(PTRACE_TRACEME, 0, NULL, NULL); 

Mirando el man page el niño necesita o hacer una PTRACE_TRACEME y un ejecutivo o el padre tiene que rastrear el uso de PTRACE_ATTACH. No veo bien en el código:

El padre puede iniciar una traza llamando al tenedor (2) y que tiene el niño que resulta hacer un PTRACE_TRACEME, seguido (por lo general) por un exec (3). Alternativamente, el padre puede comenzar la traza de un proceso existente usando PTRACE_ATTACH.

+0

@ 1der, ¿cuál es el sentido de tu comentario? –

+0

Olvidé la parte TRACEME pero todavía no funciona. – 1der

2

básicamente intentas escribir strace binary en linux, que rastrea las llamadas al sistema del proceso. Linux proporciona una llamada al sistema ptrace (2) para esto. La llamada al sistema ptrace requiere 4 argumentos y el primer argumento dice lo que debe hacer. El sistema operativo se comunica con el proceso principal con señales y el proceso secundario se detiene enviando SIGSTOP. en general, debe seguir los pasos a continuación.

if(fork() == 0) 

{ 
    //child process 

    ptrace(PTRACE_TRACEME, 0,0, 0); 
    exec(...); 
} 
else 
{ 

start: 

    wait4(...); 

    if (WIFSIGNALED(status)) { 
     //done 
    } 
    if (WIFEXITED(status)) { 
     //done 
    } 
    if(flag == startup) 
    { 
     flag = startupdone; 

     ptrace(PTRACE_SYSCALL, pid,0, 0) ; 
     goto start; 
    } 
    if (if (WSTOPSIG(status) == SIGTRAP) {) { 
      //extract the register 
      ptrace(PTRACE_GETREGS,pid,(char *)&regs,0) 

    } 

Tenga en cuenta que la lectura e interpretación del registro dependerá de su arquitectura. El código anterior es solo un ejemplo para hacerlo bien, necesitas profundizar más. eche un vistazo al código strace para una mayor comprensión.

+0

De acuerdo con las páginas man, solo se usa el primer argumento de un PTRACEME y el resto se ignora. ¿Qué pasa con (char *) 1? – 1der

+0

sí, tiene razón, se ignora addr, se editó. –

5

El problema es que cuando el hijo llama al ptrace(TRACEME) se configura para el seguimiento pero no se detiene, continúa hasta que llama al exec (en cuyo caso se detiene con un SIGTRAP), o se pone otro señal. Entonces, para que el padre vea lo que hace SIN una llamada de ejecutivo, debe hacer los arreglos necesarios para que el niño reciba una señal.La manera más fácil de hacerlo es hacer que el niño llame al raise(SIGCONT); (o cualquier otra señal) inmediatamente después de llamar al ptrace(TRACEME)

Ahora en el padre espera (una vez) y asume que el niño ahora está detenido en una llamada al sistema . Este no será el caso si se detuvo a una señal, por lo que debe llamar al wait(&status) para obtener el estado secundario y llamar al WIFSTOPPED(status) y WSTOPSIG(status) para ver POR QUÉ se ha detenido. Si se detuvo debido a un syscall, la señal será SIGTRAP.

Si desea ver varias llamadas al sistema en el cliente, tendrá que hacer todo esto en un bucle; algo así como:

while(1) { 
    wait(&status); 
    if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { 
     // stopped before or after a system call -- query the child and print out info 
    } 
    if (WIFEXITED(status) || WIFSIGNALLED(status)) { 
     // child has exited or terminated 
     break; 
    } 
    ptrace(PTRACE_SYSCALL, 0, 0, 0); // ignore any signal and continue the child 
} 

nota que va a parar dos veces para cada llamada al sistema - una vez antes de la llamada al sistema y una segunda vez justo después de la llamada al sistema se completa.

0

sólo poner juntos lo que Chris Dodd dijo:

#include <unistd.h>  /* for read(), write(), close(), fork() */ 
#include <fcntl.h>  /* for open() */ 
#include <stdio.h> 
#include <sys/ptrace.h> 
#include <sys/reg.h> 
#include <sys/wait.h> 
#include <sys/types.h> 

int main(int argc, char *argv[]) { 
pid_t child; 
int status; 
long orig_eax; 
child = fork(); 

if (0 == child) 
{ 
    ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
    raise(SIGCONT); 
    if (argc != 3) { 
     fprintf(stderr, "Usage: copy <filefrom> <fileto>\n"); 
     return 1; 
    } 

    int c; 
    size_t file1_fd, file2_fd; 
    if ((file1_fd = open(argv[1], O_RDONLY)) < 0) { 
     fprintf(stderr, "copy: can't open %s\n", argv[1]); 
     return 1; 
    } 

    if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) { 
     fprintf(stderr, "copy: can't open %s\n", argv[2]); 
     return 1; 
    } 

    while (read(file1_fd, &c, 1) > 0) 
     write(file2_fd, &c, 1); 
} 
else 
{ 
    while(1){ 
     wait(&status); 
     if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP){ 
      orig_eax = ptrace(PTRACE_PEEKUSER, child, sizeof(long) * ORIG_EAX, NULL); 
      printf("copy made a system call %ld\n", orig_eax); 
     } 
     if(WIFEXITED(status) || WIFSIGNALED(status)){ 
      break; 
     } 

     ptrace(PTRACE_SYSCALL, child, 0, 0); 
    }   
} 
return 0; 
} 
Cuestiones relacionadas