linux – 使用ptrace跟踪跨子节点的所有execve()调用

前端之家收集整理的这篇文章主要介绍了linux – 使用ptrace跟踪跨子节点的所有execve()调用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试在 Linux CentOS上编写一个工具来跟踪所有生成的进程以及运行的内容.本质上,我有兴趣遍历所有fork / clone并从execve()发出所有命令行. Strace已经(某些)做了这个,但它也截断了调用和参数.我还想更好地理解ptrace()的工作原理.

因此,第一个障碍是弄清楚如何使用ptrace()来执行fork / clone而不需要跟踪程序需要分叉自己的副本.我挖了进去,发现了这个怎么样.因为fork是在Linux上用clone实现的,所以我注意到strace将一些比特放入克隆系统调用中,以便在没有任何额外麻烦的情况下启用子跟踪.

所以,实质上代码只是一个大问题:

while (1) {
    int pid = wait3(-1,...);

    /* process what happened */

    ptrace(PTRACE_SYSCALL,pid,...);
}

这适用于相对简单的进程,如/ bin / sh,但是,某些进程导致wait()无限期挂起.我唯一能够确定的是我正在跟踪的进程是对它的子进行sys_rt_sigsuspend()(因此,跟踪器的孙子)然后事情楔入.

我很好奇是否有一种理智的方式可以调试可能发生的事情.有些事情显然阻止了流程树的进展

这是有问题的程序的源代码

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

/* For the clone flags
 */
#include <sched.h>

/* #include <errno.h> */

#include <sys/ptrace.h>
#include <sys/user.h>

/* Defines our syscalls like 
 */
#include <sys/syscall.h>

#include <sys/reg.h>
#include <stdio.h>

#include <signal.h>

#include <ctype.h>

#include <map>

using namespace std;

char bufstr[4096];

#ifdef __x86_64__
#define REG_ACC  RAX
#define REG_ARG1 RDI
#define REG_ARG2 RSI
#else
#define REG_ACC  EAX
#define REG_ARG1 EBX
#define REG_ARG2 ECX
#endif

/* Trace control structure per PID that we're tracking
 */
class tcb {
    int      pid_;
    int entering_;

    public:

    tcb(int pid,int entering = 1) : pid_(pid),entering_(entering) {};
    tcb()                          : pid_(-1)                       {};
    // tcb(const tcb& p)              : pid_(pid.pid()),entering_(entering.entering()) {};
    int&       pid() { return      pid_; }
    int&  entering() { return entering_; }
};

/* Fetch a string from process (pid) at location (ptr).  Buf is the place
 * to store the data with size limit (size).  Return the number of bytes
 * copied.
 */
int get_string(int pid,long ptr,char *buf,int size)
{
    long data;
    char *p = (char *) &data;
    int j = 0;

    while ((data = ptrace(PTRACE_PEEKTEXT,(void *) ptr,0)) && j < size) {
        int i;

        for (i = 0; i < sizeof(data) && j < size; i++,j++) {
            if (!(buf[j] = p[i]))
                goto done;
        }
        ptr += sizeof(data);
    }

    done:

    buf[j] = '\0';

    return j;
}

int main(int argc,char *argv[])
{
    int status = 0;
    long scno = 0;
    // int entering = 1;
    struct user_regs_struct regs;
    map<int,tcb> pidTable;
    struct sigaction sa;


    /* Setup 
     */


    int pid = fork();



    if (!pid && argc) {
        if (ptrace(PTRACE_TRACEME,0) < 0) {
            perror("ptrace(PTRACE_ME,... ");

            exit(1);
        }
        execvp(argv[1],&argv[1]);
    } else {
        sa.sa_flags = 0;
        sa.sa_handler = SIG_DFL;
        sigemptyset(&sa.sa_mask);
        sigaction(SIGCHLD,&sa,NULL);

        waitpid(pid,&status,0);

        pidTable[pid] = tcb(pid);

        fprintf(stderr,"pid is %d\n",pidTable[pid].pid());

        while (!pidTable.empty()) {
            if (pid > 0) {
                //fprintf(stderr,"%d: Restarting %d\n",getpid(),pid);
                if (ptrace(PTRACE_SYSCALL,0) < 0) {
                    perror("ptrace(PTRACE_SYSCALL,...");
                    exit(1);
                }
            }

            // waitpid(pid,0);
            // pid = waitpid(-1,0);
            pid = wait3(&status,__WALL,0);

            // fprintf(stderr,"Pid from wait is %d\n",pid);

            if (pid < 0) {
                perror("waitpid");
                break;
            } else {

                /* fprintf(stderr,"%d: Status is: ",pid); */

                /*
                if (WIFEXITED(status)) {
                    fprintf(stderr,"exited");
                } else if (WIFSIGNALED(status)) {
                    fprintf(stderr,"exited");
                } else if (WIFSTOPPED(status),"stopped") {
                    fprintf(stderr,"stopped");
                } else if (WIFCONTINUED(status)) {
                    fprintf(stderr,"continued");
                }
                fprintf(stderr,"\n");
                */

                if (WIFEXITED(status) || WIFSIGNALED(status)) {
                    /* Probably empty the table here */
                    pidTable.erase(pid);

                    fprintf(stderr,"Detect process term/kill %d\n",pid);

                    /* if (ptrace(PTRACE_DETACH,0) < 0) {
                        perror("ptrace");
                    } */

                    pid = -1;

                    continue;
                }
            }

            ptrace(PTRACE_GETREGS,&regs);

#ifdef __x86_64__
            scno = regs.orig_rax;
#else
            scno = regs.orig_eax;
#endif /* __x86_64__ */

            if (scno == SYS_execve) {
                fprintf(stderr,"%d: Exec branch\n",pid);
                if (pidTable[pid].entering()) {
                    long ldata,ptr,ptr1;

                    ptrace(PTRACE_GETREGS,&regs);

                    #ifdef __x86_64__
                    ptr = regs.rdi;
                    #else
                    ptr = regs.ebx;
                    #endif /* __x86_64__ */

                    fprintf(stderr,"%d: exec(",pid);

                    if (ptr) {
                        get_string(pid,bufstr,sizeof(bufstr));

                        fprintf(stderr,"%s",bufstr);

                    } 

                    #ifdef __x86_64__
                    ptr1 = regs.rsi;
                    #else
                    ptr1 = regs.ecx;
                    #endif /* __x86_64__ */


                    for (; ptr1; ptr1 += sizeof(unsigned long)) {
                        ptr = ptr1;
                        /* Indirect through ptr since we have char *argv[] */
                        ptr = ptrace(PTRACE_PEEKTEXT,0);

                        if (!ptr)
                            break;

                        get_string(pid,sizeof(bufstr));
                        fprintf(stderr,",%s",bufstr);
                    }
                    fprintf(stderr,")\n");

                    pidTable[pid].entering() = 0;
                }
                else {
                    long acc = ptrace(PTRACE_PEEKUSER,sizeof(unsigned long) * REG_ACC,0);
                    pidTable[pid].entering() = 1;
                    fprintf(stderr,"%d: Leaving exec: eax is %ld\n",acc);
                }
            } else if (scno == SYS_fork || scno == SYS_clone) {
                fprintf(stderr,"%d: fork/clone branch\n",pid);
                if (pidTable[pid].entering()) {
                    long flags = ptrace(PTRACE_PEEKUSER,(sizeof(unsigned long) * REG_ARG1),0);

                    fprintf(stderr,"%d: Entering fork/clone\n",pid);
                    pidTable[pid].entering() = 0;

                    if (ptrace(PTRACE_POKEUSER,flags | CLONE_PTRACE & 
                                                                                         ~(flags & CLONE_VFORK ? 
                                                                                         CLONE_VFORK | CLONE_VM : 0)) < 0) {
                        perror("ptrace");
                    }

                    if (ptrace(PTRACE_POKEUSER,(sizeof(unsigned long) * REG_ARG2),0) < 0) {
                        perror("ptrace");
                    }

                } else {
                    // int child;

                    ptrace(PTRACE_GETREGS,&regs);

                    #ifdef __x86_64__
                    fprintf(stderr,"%d: Leaving fork/clone: rax = %ld\n",regs.rax);
                    #else
                    fprintf(stderr,"%d: Leaving fork/clone: eax = %ld\n",regs.eax);
                    #endif

                    pidTable[pid].entering() = 1;

                    #ifdef __x86_64__
                    if (regs.rax <= 0) {
                    #else
                    if (regs.eax <= 0) {
                    #endif
                        continue;
                    }

                    #ifdef __x86_64__
                    int newpid = regs.rax;
                    #else
                    int newpid = regs.eax;
                    #endif
                    pidTable[newpid] = tcb(newpid,0);
                    //pidTable[newpid] = tcb(newpid,1);
                    //pidTable[newpid] = pidTable[pid];
                    fprintf(stderr,"%d: forked child is %d\n",newpid);
                }
            } else if (scno == SYS_exit) {
                fprintf(stderr,"%d: exit syscall detected\n",pid);
            } else if (scno < 0) {
                fprintf(stderr,"Negative syscall number for %d\n",pid);
                exit(1);
            } else {
                fprintf(stderr,"%d: Scno is %ld\n",scno);
            }
        }
    }
    return 0;
}

解决方法

有ptrace PTRACE_SETOPTIONS子标志的标志:PTRACE_O_TRACEFORK,PTRACE_O_TRACEEXEC和PTRACE_O_TRACEEXIT.更多内容是ptrace的手册页.

猜你在找的Linux相关文章