我有一个多节点进程,必须控制另一个进程的执行。 为此,我从其中一个线程使用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
...
...
}
我有一个类似的非multithreades应用程序,完美地工作,加载exec并检查堆栈和内存没有问题。但是当我在multithreades上尝试这个配置时,我创建的进程根本不会运行。
我的问题是。如何从线程中跟踪进程?我是否必须改变附加过程的方式?
答案 0 :(得分:1)
在多线程应用程序中,为了跟踪程序,你需要为每个特定线程使用ptrace,父进程使用ptrace(PTRACE_foo,pid,...)生成,其中pid是进程的线程id 。为了跟踪父本身,请在父代码中使用ptrace和pid = 0。 ptrace仅限于特定的线程。 希望你找到你所照顾的......
答案 1 :(得分:1)
帖子末尾的代码是问题的一个答案。 您可以拥有一个跟踪流程的主题。
如果有人感兴趣,我正在尝试的问题是,由于一些无法理解的原因,跟踪线程不是发送所有跟踪命令的线程。其中一个是调用fork并负责跟踪,另一个是发送
,结果错误是: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看起来效果很好。
答案 2 :(得分:0)
[编辑]
我对问题解释犯了错误。
回答下面的评论:根据手册,PTRACE_TRACEME不会将tracee附加到主线程,而是附加到主线程。
PTRACE_TRACEME - 表示该进程将由其父进程跟踪。
[旧的不道德的回答]
跟踪是每个线程,您需要单独附加每个线程。您的代码只附加到execve调用的进程的主线程。
来自README-linux-ptrace:
附件和后续命令是每个线程:在多线程进程中,每个线程可以单独附加到(可能不同的)跟踪器,或者不附加,因此不进行调试。因此,“tracee”总是意味着“(一个)线程”,而不是“一个(可能是多线程的)进程”。
你可以捕捉到SIGTRAP信号(来自ptrace man):
如果PTRACE_O_TRACEEXEC选项没有生效,跟踪进程对execve(2)的所有成功调用都将使其被发送SIGTRAP信号,使父母有机会在新程序开始执行之前获得控制权。 / p>
并使用PTRACE_GETEVENTMSG恢复pid:
检索刚刚发生的ptrace事件的消息(作为unsigned long),将其放在跟踪器中的地址数据中。对于PTRACE_EVENT_EXIT,这是tracee的退出状态。对于PTRACE_EVENT_FORK,PTRACE_EVENT_VFORK,PTRACE_EVENT_VFORK_DONE和PTRACE_EVENT_CLONE,这是新进程的PID。 (addr被忽略。)
然后使用PTRACE_ATTACH附加到恢复的新pid。