5

我有一个多线程进程,它必须控制另一个进程的执行。为此,从我使用 Ptrace 的线程之一。这就是创建和启动 tracee 的方式。

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;
}

这就是流程的运行方式

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

我有一个类似的非多线程应用程序可以完美运行,加载 exec 并检查堆栈和内存没有问题。但是,当我在多线程上尝试此配置时,我创建的进程根本不会运行。

我的问题是。如何从线程跟踪进程?我是否必须更改附加流程的方式?

4

3 回答 3

2

帖子末尾的代码是该问题的一个答案。 您可以拥有一个跟踪进程的线程。


如果有人感兴趣,我正在试验的问题是,由于一些难以理解的原因,跟踪线程并不是发送所有跟踪命令的线程。其中一个负责调用fork并负责trace,另一个负责发送

  1. ptrace(PTRACE_CONT, childPID, 0, 0);
  2. ptrace(PTRACE_GETREGS,childPID,0,寄存器);

结果错误是: ptrace (PTRACE_GETREGS,..) 无法获取寄存器:没有这样的过程


#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;

}

真正让我惊讶的是,没有迹象表明哪个线程是示踪剂。ptrace(PTRACE_TRACEME, 0, NULL, NULL) 0 似乎工作得很好。

于 2013-09-12T08:33:23.243 回答
1

在多线程应用程序中,为了跟踪程序,您需要为每个特定线程使用 ptrace,父进程通过使用 ptrace(PTRACE_foo, pid, ...) 产生,其中 pid 是进程的线程 ID。为了跟踪父级本身,然后在父级代码中使用 pid = 0 的 ptrace。ptrace 仅限于特定线程。希望你找到了你正在寻找的东西......

于 2013-09-11T11:31:32.527 回答
0

[编辑]

我在问题解释上犯了一个错误。

回答下面的评论:根据手册 PTRACE_TRACEME 不会将 tracee 附加到主线程,而是附加到父线程。

PTRACE_TRACEME -- 指示该进程将由其父进程跟踪。

[旧的不正确答案]

跟踪是每个线程的,您需要单独附加每个线程。您的代码只是附加到 execve 调用的进程的主线程。

来自 README-linux-ptrace:

附加和后续命令是每个线程的:在多线程进程中,每个线程可以单独附加到(可能不同的)跟踪器,或者不附加,因此不进行调试。因此,“tracee”总是意味着“(一个)线程”,而不是“一个(可能是多线程的)进程”。

您可以捕获 SIGTRAP 信号(来自 ptrace man):

如果 PTRACE_O_TRACEEXEC 选项无效,则被跟踪进程对 execve(2) 的所有成功调用都将导致它被发送一个 SIGTRAP 信号,从而使父进程有机会在新程序开始执行之前获得控制权。

并使用 PTRACE_GETEVENTMSG 恢复 pid:

检索有关刚刚发生的 ptrace 事件的消息(作为无符号长整数),将其放置在跟踪器中的地址数据处。对于 PTRACE_EVENT_EXIT,这是被跟踪者的退出状态。对于 PTRACE_EVENT_FORK、PTRACE_EVENT_VFORK、PTRACE_EVENT_VFORK_DONE 和 PTRACE_EVENT_CLONE,这是新进程的 PID。(地址被忽略。)

然后使用 PTRACE_ATTACH 附加到恢复的新 pid。

于 2013-09-11T11:49:49.507 回答