linux系统:ptrace系统调用浅析

目录

1. ptrace 介绍

2.  ptrace 的函数详解

3、读取线程寄存器值


1. ptrace 介绍

  Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。当然这里的被追踪的进程不仅仅是仅限于子进程也可以是任意的其它的进程或者线程。

2.  ptrace 的函数详解

long ptrace(enum __ptrace_request request,pid_t pid,void *addr, void *data);
  • 参数request:请求ptrace执行的操作
  • 参数pid:目标进程的ID
  • 参数addr:目标进程的地址值
  • 参数data:作用则根据request的不同而变化,如果需要向目标进程中写入数据,data存放的是需要写入的数据;如果从目标进程中读数据,data将存放返回的数据

request参数决定了CODE的行为以及后续的参数是如何被使用的,参数request的常用的值如下:

3、读取线程寄存器值

读取其它进程或者线程的寄存器,这个也是比较常用的方法,例如:获取线程的rip、rbp进行堆栈回溯等。

目标进程/线程

#include <stdio.h>
#include <unistd.h> //for sleep
#include <stdlib.h> //for exit
#include <pthread.h>//for pthread
#include <errno.h>  //for errno
#include <sys/syscall.h> //for gettid
#define gettid() syscall(__NR_gettid)

void func_test(){
    int i = 0,sum = 0;
	while(1){
		i++;
		sum +=i;
		sleep(1);
	}
}
 
void *func(void *para){
    printf("child process tid: %u\n", gettid());
	func_test();
    return NULL;
}
 
int main(){ 
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, func, NULL);
    if(ret != 0)
    {   
        exit(errno);
    }   
    printf("\nparent process pid: %u\n", getpid());
 
    pthread_join(tid, NULL);
    return 0;
}

读取操作:

#define _GNU_SOURCE
#include <sys/ptrace.h>
#include <stdlib.h>     // for atoi
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>  //for errno
#include <sys/user.h> /* For user_regs_struct */
#include <dlfcn.h>
#include <string.h>
#include <sys/syscall.h> //for gettid
#define gettid() syscall(__NR_gettid)

int main(int argc, char *argv[])
{ 
	if(argc != 2) {
		printf("Usage: %s <pid to be traced> ");
		exit(1);
	}   
	pid_t traced_process = atoi(argv[1]);
	struct user_regs_struct regs;   
	int ret = ptrace(PTRACE_ATTACH, traced_process, NULL, NULL);
	if(ret == -1){
		printf("attach failed,ret is %ld ,errno is %d,strerror(errno) is %s. \n", ret, errno, strerror(errno));
		return -1;
	}else{
		printf("attach ok...\n");
	}
	
	wait(NULL);
	
	ret = ptrace(PTRACE_GETREGS, traced_process,NULL, &regs);
	if(ret == -1){
		printf("get register failed,ret is %ld ,errno is %d,strerror(errno) is %s. \n", ret, errno, strerror(errno));
		return -1;
	}else{
	    printf("RIP: %lx,RBP: %lx\n", regs.rip, regs.rbp);
	}

	ret = ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
	if(ret == -1){
		printf("detach failed,ret is %ld ,errno is %d,strerror(errno) is %s. \n", ret, errno, strerror(errno));
		return -1;
	}else{
		printf("detach ok...\n");
	}
	return 0;
}

执行结果:

[root@localhost read_thread_regs]# gcc target_thread.c -o target_thread -lpthread
[root@localhost read_thread_regs]# ./target_thread &
[15] 27668
[root@localhost read_thread_regs]#
parent process pid: 27668
Hello world.
child process tid: 27669

[root@localhost read_thread_regs]# ./read_regs 27668
attach ok...
RIP: 31cf4082fd,RBP: 0
detach ok...
[root@localhost read_thread_regs]# ./read_regs 27669
attach ok...
RIP: 31ce8aca7d,RBP: 7f3afd481d40
detach ok...
[root@localhost read_thread_regs]#

说明:
1、目标进程 / 线程被 attach 时,任务的状态会被暂时置位T(stop)状态。当执行 detach 时状态恢复。
2、当 attach 线程或者进程任务(task)时,进程下的其他任务不受影响,状态不变。
3、attach的目标任务不能是进程自己或是进程下的子线程,错误码errno is 1:Operation not permitted

猜你喜欢

转载自blog.csdn.net/wangquan1992/article/details/108471212