Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Thread trace a Process?

I have a multithreades process that has to control the execution of one other process. To do so, from one of the threads I use Ptrace. This is how the tracee is created and launched.

switch( childPID=fork() ){
    case -1:   
         perror("fork()");
         return -1;
    case 0 :
         ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
         execve(execPath,NULL,NULL);
         return -1;
   default:
         break;
}

This is how the process is run

while (1) {
    ptrace(PTRACE_CONT, childPID, 0, 0);
    waitpid( childPID, &status, 0);
    // inspect status and break in some cases
    ...
    ...
}

I have a similar non multithreades application that works perfectly, load exec and inspect stack and memory without problems. But when I try this configuration on the multithreades one the process I create does not run at all.

My question is. How can I trace a process from a thread ? Do I have to change the way I attach the process?

like image 394
Lapo Breschi Avatar asked Oct 22 '22 01:10

Lapo Breschi


2 Answers

The code at the end of the post is one answer to the question. You can have a thread that trace a process.


If someone is interested, the problem I was experimenting was that, for some unintelligible reasons, the tracer thread was not the one sending all the tracing commands. One of them was calling the fork and having the responsibility of trace, one other was sending

  1. ptrace(PTRACE_CONT, childPID, 0, 0);
  2. ptrace (PTRACE_GETREGS, childPID, 0, registers);

and the resulting error was: ptrace (PTRACE_GETREGS,..) Couldn't get registers: No such process


#include <pthread.h>
#include <sys/ptrace.h>    
#include <sys/wait.h>    
#include <stdlib.h>    
#include <stdio.h>    
#include <unistd.h>    
#include <sys/reg.h>    
#include <sys/user.h>

#define NUM_THREADS    9


int childPID;    
int fatherPID;    


void print_registers(struct user_regs_struct *registers){        

   printf("\tReg ebx 0x%lx\n",registers->ebx);    
   printf("\tReg ecx 0x%lx\n",registers->ecx);    
   printf("\tReg edx 0x%lx\n",registers->edx);    
   printf("\tReg esi 0x%lx\n",registers->esi);    
   printf("\tReg edi 0x%lx\n",registers->edi);    
   printf("\tReg ebp 0x%lx\n",registers->ebp);   
   printf("\tReg eax 0x%lx\n",registers->eax);    
   printf("\tReg xds 0x%lx\n",registers->xds);   
   printf("\tReg xes 0x%lx\n",registers->xes);    
   printf("\tReg xfs 0x%lx\n",registers->xfs);    
   printf("\tReg xgs 0x%lx\n",registers->xgs);    
   printf("\tReg orig_eax 0x%lx\n",registers->orig_eax);    
   printf("\tReg eip 0x%lx\n",registers->eip);    
   printf("\tReg xcs 0x%lx\n",registers->xcs);    
   printf("\tReg eflags 0x%lx\n",registers->eflags);    
   printf("\tReg esp 0x%lx\n",registers->esp);    
   printf("\tReg xss 0x%lx\n",registers->xss);    
}

int load(char * execPath){    
   switch( childPID=fork() ){    
      case -1:       
         perror("fork()");    
         return -1;    
      case 0 :    
         if( access(execPath, X_OK)==-1){    
            printf("\tAcces denied to\n",execPath);    
         }    
         else {    
            printf("\tChild Process pid :%d %d\n",childPID,getpid());            
            if(ptrace(PTRACE_TRACEME, 0, NULL, NULL)<0){    
               perror("ptrace(PTRACE_TRACEME)");    
            return -1;    
            }            
            execve(execPath,NULL,NULL);    
            perror("execve()");    
         }    
         return -1;    
      default:    
         wait(NULL);    
         fatherPID=getpid();    
         printf("\tParent Process pid :%d  %d\n",fatherPID,childPID);    
         if (ptrace(PTRACE_SETOPTIONS, childPID, 0, PTRACE_O_TRACEEXIT)){    
            perror("stopper: ptrace(PTRACE_SETOPTIONS, ...)");    
            return -1;   
         }   
         break;   
   }    
   return -1;    
}

void registers(){    
   printf("\t@@Command get_registers@\n");    
   struct user_regs_struct * registers = (struct user_regs_struct*)(calloc(1, sizeof(struct user_regs_struct)));    
   long ret = ptrace (PTRACE_GETREGS, childPID, 0,  registers);    
   if (ret <0) perror("ptrace (PTRACE_GETREGS,..) Couldn't get registers");     
   print_registers(registers);    
   free(registers);
}  

int continuE(){  
   int status = 0;    
   int signo;    
   long long_var=0;    
   // to continue the execution is needed to trigger the event                       
   while (1) {    
      ptrace(PTRACE_CONT, childPID, 0, 0);    
      waitpid( childPID, &status, 0);    
      if (WIFEXITED(status))    
            printf("Child exited by %d\n",WEXITSTATUS(status));    
        if (WIFSIGNALED(status))

         printf(" child process terminated by a signal %d \n",WTERMSIG(status) );

        if (WIFSTOPPED(status)) {    
         signo = WSTOPSIG(status);    
         //printf("Child stopped by %d\n",signo);    
        }        
        // we had the sigtrap and we are at the exec    
      if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))){    
         printf("\t###Stopped the tracee at EXEC, with status %d###\n",WEXITSTATUS(status));    
         ptrace(PTRACE_GETEVENTMSG, childPID,0,&long_var);    
         printf("\t###PTRACE_GETEVENTMSG result %lu ,%d ###\n",long_var,WEXITSTATUS(long_var));     
      }    

      // we have a sigtrap and we are on the exit    
      // we could think to take out PTRACE_O_TRACEEXIT    
      if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))){   
         printf("\t###Stopped the tracee at EXIT###\n");    
         signo= SIGHUP;    
      }



      // normal cases   
        if ((signo == SIGTRAP) || (signo == SIGTERM) ||(signo ==SIGINT) || (signo == SIGHUP)    
        || ( signo == SIGSEGV) ){    
         break;    
        }    
   }        
   return signo;    
}

void *work(void *threadid)    
{    
   long tid;    
   tid = (long)threadid;    
   printf("Hello World! It's me, thread #%ld!\n", tid);    
   load("/home/rtems/plibeagleeye/Plib/Tests/bin/stanford.o");    
   registers();    
   continuE();    
   registers();
   pthread_exit(NULL);    
}

void *work2(void *threadid)    
{    
   long tid;    
   tid = (long)threadid;   
   printf("Hello World! It's me, thread #%ld!\n", tid);            
   pthread_exit(NULL);    
}



int main (int argc, char *argv[])    
{    
   pthread_t threads[NUM_THREADS];    
   pthread_attr_t attr;    
   int rc;    
   long *taskids;    
   void *status;    
   taskids = (long *) malloc( NUM_THREADS * sizeof(long));    
   long t=0;        

   /* Initialize and set thread detached attribute */

   pthread_attr_init(&attr);    
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);   

   taskids[t] = 0;    
   rc = pthread_create(&threads[t], &attr, work, (void *)taskids[t]);


   for(t=1; t<NUM_THREADS; t++){    
      taskids[t] = t;    
      printf("Creating thread %ld\n", t);
      rc = pthread_create(&threads[t], &attr, work2, (void *)taskids[t]);
      if (rc){    
         printf("ERROR; return code from pthread_create() is %d\n", rc);    
         exit(-1);    
      }    
   }      

   pthread_attr_destroy(&attr);
   for(t=0; t<NUM_THREADS; t++){
      rc = pthread_join(threads[t], &status);
      if (rc) {    
         printf("ERROR; return code from pthread_join()  is %d\n", rc);
         exit(-1);
         }
      printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status);
   }
   printf("Ciaoz all threads finished their jobs\n");
   free(taskids);
   /* Last thing that main() should do */
   pthread_exit(NULL);
   return 0;

}

The thing that really surprise me is that there is no indications on which thread is the tracer. ptrace(PTRACE_TRACEME, 0, NULL, NULL) the 0 seems to work perfectly.

like image 109
Lapo Breschi Avatar answered Oct 24 '22 02:10

Lapo Breschi


In a multi-threaded application in order to trace the program you need to use ptrace for each and particular thread the parent process spawns by using ptrace(PTRACE_foo, pid, ...) where pid is the thread id of the process. In order to trace the parent itself then use ptrace with pid = 0 in the parent code. ptrace is strictly to a particular thread only. hope u found what u were looking after...

like image 43
Trilok M Avatar answered Oct 24 '22 01:10

Trilok M