ptrace 跟踪多线程程序

转自:https://blog.csdn.net/heliming945073280/article/details/11932067

ptrace 原型说明

 #include <sys/ptrace.h>
 long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

在使用PTRACE_TRACEME参数时,跟踪多线程程序需要使用PTRACE_SETOPTIONS来设置ptrace相关属性。
PTRACE_SETOPTIONS 是将父进程内由data指向的值设定为ptrace 选项,data作为掩码来解释,由下面的标志来指定:

  • PTRACE_O_EXITKILL:当跟踪进程退出时,向所有被跟踪进程发送SIGKILL信号将其退出,这个参数可以防止被跟踪进程脱离跟踪进程的控制。
  • PTRACE_O_TRACECLONE:被跟踪进程在下一次调用clone()时将其停止,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到。
  • PTRACE_O_TRACEEXEC:被跟踪进程在下一次调用exec()函数时使其停止。
  • PTRACE_O_TRACEEXIT:被跟踪进程在退出是停止其执行,被跟踪进程的退出状态可通过PTRACE_GETEVENTMSG获得。
  • PTRACE_O_TRACEFORK:被跟踪进程在下次调用fork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到。
  • PTRACE_O_TRACEVFORK:被跟踪进程在下次调用vfork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到。

PTRACE_GETEVENTMSG:获取刚刚发生的ptrace事件消息,并存放在跟踪进程由data指向的位置,addr参数被忽略。对于 PTRACE_EVENT_FORK,PTRACE_EVENT_VFORK,PTRACE_EVENT_VFORKDOWN和PTRACE_EVENT_CLONE,data是新进程的pid.

使用ptrace进行跟踪多线程程序时将要使用上述所涉及的知识进行编码,其被跟踪程序如下:

1、test.c

#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "hello";
int main()
{
	 int res;
	 pthread_t a_thread;
	 void *thread_result;
	 res = pthread_create(&a_thread, NULL, thread_function, (void*) message);
	 if (res != 0)
	 {
	 	 printf("Thread creation failed");
  		 exit(EXIT_FAILURE);
	 }
	 printf("Wait for thread to finish...\n");
	 res = pthread_join(a_thread, &thread_result);
	 if (res != 0)
	 {
	  	perror("Thread join failed");
	  	exit(EXIT_FAILURE);
	 }
	 printf("Thread joined, it returned %s\n", (char *) thread_result);
	 exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
 	printf("thread_function is running ,Argument was %s\n", (char*)arg);
 	pthread_exit("Thank you for the CPU time");
}

上述程序就是基于POSIX的简单的多线程程序,主线程创建子线程,等待子线程的结束,获取子线程结束时的退出状态,然后主线程结束。
现在我们来看一下跟踪进程的代码:
2、ptrace.c

#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
int main()
{
    pid_t child_pid;
    int status = 0;
    if ((child_pid = fork()) == 0) //子进程返回
    {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/home/2019/ptrace/test", "test", 0);//这个路径需要更改
        printf("child process start failed...\n");
    }
    else
    {
        wait(NULL); //接收SIGTRAP信号
        long ptraceOption = PTRACE_O_TRACECLONE;
        ptrace(PTRACE_SETOPTIONS, child_pid, NULL, ptraceOption); //设置ptrace属性
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
        while (1)
        {
            pid_t child_waited = waitpid(-1, &status, __WALL);//等待接收信号
          
            if(WIFSTOPPED(status))
            {
                printf("child %ld recvied signal %d\n",(long)child_waited, WSTOPSIG(status));
            }
            if (WIFSIGNALED(status))
            {
                printf("child %ld recvied signal %d\n",(long)child_waited, WTERMSIG(status));
            }
            if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP)
            { //新线程被创建完成后,收到的信号,或者遇到断点时
                ptrace(PTRACE_CONT, child_waited, 1, NULL);
                continue;
            }
            if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
            {
                //当一个线程创建另一个线程返回时,收到的信号
                pid_t new_pid;
                if (((status >> 16) & 0xffff) == PTRACE_EVENT_CLONE)
                {
                    if (ptrace(PTRACE_GETEVENTMSG, child_waited, 0, &new_pid)
                            != -1)
                    {
                        printf("thread %d created\n", new_pid);
                    }
                }
            }
            if (child_waited == -1)
                break;
            if (WIFEXITED(status))
            { //线程结束时,收到的信号
                printf("thread %d exited with status %d\t\n", child_waited,
                        WEXITSTATUS(status));
            }
            ptrace(PTRACE_CONT, child_waited, 1, NULL);
        }
    }
    return 0;
}

被跟踪进程首先是使用fork()系统调用创建子进程,然后使用exec加载被跟踪程序ex,通过exec函数族加载时,会是被跟踪进程收到SIGTRAP信号使其终止,而跟踪进程会收到SIGCHLD信号。在被跟踪进程里面首先使用wait(NULL)忽略这一个信号,然后设置ptrace属性,使其跟踪clone事件,此时,当被跟踪进程调用pthread_create时,将使其停止执行,转而执行新产生的子线程,并且子线程因为SIGTRAP停止执行,这个时候事件触发,跟踪进程得到该信号,分析PTRACE_EVENT_CLONE事件,并且使用PTRACE_GETEVENTMSG来得到新产生的子线程的pid,最后两个子线程结束退出时想跟踪进程发送SIGEXIT信号。

程序执行如下:
使用gcc -g test.c -o test -lpthread 编译被跟踪进程
使用gcc -g ptrace.c -o ptrace 编译跟踪进程
./ptrace运行跟踪进程程序,执行结果如下:

child 3574 recvied signal 19
child 3573 recvied signal 5
thread 3574 created
Wait for thread to finish...
thread_function is running ,Argument was hello
Thread joined, it returned Thank you for the CPU time
thread 3574 exited with status 0    
thread 3573 exited with status 0   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值