Linux system: Analysis of ptrace system call

table of Contents

1. Introduction to ptrace

2. Detailed explanation of ptrace function

3. Read thread register value


1. Introduction to ptrace

  Ptrace provides a parent process that can control the child process to run , and can check and change its core image. It is mainly used to implement breakpoint debugging. A tracked process is running until a signal occurs. The process is terminated and its parent process is notified. In the state of process suspension, the memory space of the process can be read and written. The parent process can also continue the execution of the child process and choose whether to ignore the signal that caused the suspension. Of course, the tracked process here is not only limited to the child process but also any other process or thread.

2. Detailed explanation of ptrace function

long ptrace(enum __ptrace_request request,pid_t pid,void *addr, void *data);
  • Parameter request: the operation requested by ptrace
  • Parameter pid: ID of the target process
  • Parameter addr: the address value of the target process
  • Parameter data: The function changes according to the request. If you need to write data to the target process, data stores the data that needs to be written; if you read data from the target process, data will store the returned data

The request parameter determines the behavior of CODE and how the subsequent parameters are used. The commonly used values ​​of the parameter request are as follows:

3. Read thread register value

Reading the registers of other processes or threads is also a commonly used method, such as obtaining the rip and rbp of the thread for stack traceback.

Target process/thread

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

Read operation:

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

Results of the:

[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]#

Explanation:
1. When the target process/thread is attached, the state of the task will be temporarily set to T (stop) state. The state is restored when detach is executed.
2. When attaching a thread or process task (task), other tasks under the process are not affected and the state remains unchanged.
3. The target task of attach cannot be the process itself or a child thread under the process, the error code errno is 1: Operation not permitted

Guess you like

Origin blog.csdn.net/wangquan1992/article/details/108471212