• ptrace使用方法


    基础

    操作系统通过一个叫做“系统调用”的标准机制来对上层提供服务,他们提供了一系列标准的API来让上层应用程序获取底层的硬件和服务,比如文件系统。当一个进程想要进行一个系统调用的时候,它会把该系统调用所需要用到的参数放到寄存器里,然后执行软中断指令0x80. 这个软中断就像是一个门,通过它就能进入内核模式,进入内核模式后,内核将会检查系统调用的参数,然后执行该系统调用。

    在 i386 平台下(本文所有代码都基于 i386), 系统调用的编号会被放在寄存器 %eax 中,而系统调用的参数会被依次放到 %ebx,%ecx,%edx,%exi 和 %edi中,比如说,对于下面的系统调用:

    write(2, "Hello", 5)

    编译后,它最后大概会被转化成下面这样子:

    movl   $4, %eax
    movl   $2, %ebx
    movl   $hello,%ecx
    movl   $5, %edx
    int    $0x80

    其中 $hello 指向字符串 "Hello"。

    这里是基于32位系统的寄存器名称,64位系统的寄存器名称为rax,rbx

    看完上面简单的例子,现在我们来看看 ptrace 又是怎样执行的。首先,我们假设进程 A 要 ptrace 进程 B。在 ptrace 系统调用真正开始前,内核会检查一下我们将要 trace 的进程 B 是否当前已经正在被 traced 了,如果是,内核就会把该进程 B 停下来,并把控制权交给调用进程 A (任何时候,子进程只能被父进程这唯一一个进程所trace),这使得进程A有机会去检查和修改进程B的寄存器的值。

    ptrace 的使用流程一般是这样的:父进程 fork() 出子进程,子进程中执行我们所想要 trace 的程序,在子进程调用 exec() 之前,子进程需要先调用一次 ptrace,以 PTRACE_TRACEME 为参数。这个调用是为了告诉内核,当前进程已经正在被 traced,当子进程执行 execve() 之后,子进程会进入暂停状态,把控制权转给它的父进程(SIG_CHLD信号), 而父进程在fork()之后,就调用 wait() 等子进程停下来,当 wait() 返回后,父进程就可以去查看子进程的寄存器或者对子进程做其它的事情了。

    当系统调用发生时,内核会把当前的%eax中的内容(即系统调用的编号)保存到子进程的用户态代码段中(USER SEGMENT or USER CODE),我们可以像上面的例子那样通过调用Ptrace(传入PTRACE_PEEKUSER作为第一个参数)来读取这个%eax的值,当我们做完这些检查数据的事情之后,通过调用ptrace(PTRACE_CONT),可以让子进程重新恢复运行。

    #include<stdio.h>
    #include<unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    
    #include <sys/ptrace.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    // #include <linux/user.h>
    #include <sys/user.h>
    #include <sys/reg.h>
    #include <sys/syscall.h>
    const int long_size = sizeof(long);
    #define LONG_SIZE 8
    void reverse(char *str)
    {   int i, j;
        char temp;
        for(i = 0, j = strlen(str) - 2;
            i <= j; ++i, --j) {
            temp = str[i];
            str[i] = str[j];
            str[j] = temp;
        }
    }
    void getdata(pid_t child, long addr,
                 char *str, int len)
    {   char *laddr;
        int i, j;
        union u {
                long val;
                char chars[long_size];
        }data;
        i = 0;
        j = len / long_size;
        laddr = str;
        while(i < j) {
            data.val = ptrace(PTRACE_PEEKDATA,
                              child, addr + i * LONG_SIZE,
                              NULL);
            memcpy(laddr, data.chars, long_size);
            ++i;
            laddr += long_size;
        }
        j = len % long_size;
        if(j != 0) {
            data.val = ptrace(PTRACE_PEEKDATA,
                              child, addr + i * LONG_SIZE,
                              NULL);
            memcpy(laddr, data.chars, j);
        }
        str[len] = '';
        // printf("getdata str=%s
    ", str);
    }
    void putdata(pid_t child, long addr,
                 char *str, int len)
    {   char *laddr;
        int i, j;
        union u {
                long val;
                char chars[long_size];
        }data;
        i = 0;
        j = len / long_size;
        laddr = str;
        while(i < j) {
            memcpy(data.chars, laddr, long_size);
            ptrace(PTRACE_POKEDATA, child,
                   addr + i * LONG_SIZE, data.val);
            ++i;
            laddr += long_size;
        }
        j = len % long_size;
        if(j != 0) {
            memcpy(data.chars, laddr, j);
            ptrace(PTRACE_POKEDATA, child,
                   addr + i * LONG_SIZE, data.val);
        }
    }
    
    int main(int argc, char *argv[])
    {
        int fd = 0;
        char acBuf[4096] = {0};
        fd = open("./model", O_WRONLY | O_TRUNC);
        if (-1 == fd)
        {
            printf("open error!
    ");
            return 0;
        }
        sprintf(acBuf, "test model");
        write(fd, acBuf, strlen(acBuf));
        close(fd);
    }
    
    int main(int argc, char *argv[])
    {
        pid_t child;
        child = fork();
        if (child == 0)
        {
            ptrace(PTRACE_TRACEME, 0, NULL, NULL);
            printf("before
    ");
            execl("/bin/ls", "ls", NULL);
            printf("after
    ");
        }
        else
        {
            long orig_eax;
            long params[3];
            int status;
            char *str, *laddr;
            int toggle = 0;
            printf("1
    ");
            while (1)
            {
                wait(&status);
                printf("status=%d
    ", status);
                if (WIFEXITED(status))
                    break;
                struct user_regs_struct regs;
                ptrace(PTRACE_GETREGS,child,NULL,&regs);
                printf("Write called with %ld,%ld,%ld,%ld
    ",regs.orig_rax,regs.rbx,regs.rcx,regs.rdx);
                orig_eax = ptrace(PTRACE_PEEKUSER,
                                  child, LONG_SIZE * ORIG_RAX,
                                  NULL);
                if (orig_eax == SYS_write)
                {
            printf("2
    ");
                    if (toggle == 0)
                    {
                        toggle = 1;
                        params[0] = ptrace(PTRACE_PEEKUSER,
                                           child, LONG_SIZE * RBX,
                                           NULL);
                        params[1] = ptrace(PTRACE_PEEKUSER,
                                           child, LONG_SIZE * RSI,
                                           NULL);
                        params[2] = ptrace(PTRACE_PEEKUSER,
                                           child, LONG_SIZE * RDX,
                                           NULL);
                        printf("%d,%d,%d
    ", params[0],params[1],params[2]);
                        str = (char *)calloc((params[2] + 1), sizeof(char));
                        getdata(child, params[1], str,
                                params[2]);
                        printf("str=%s
    ", str);
                        reverse(str);
                        putdata(child, params[1], str,
                                params[2]);
                    }
                    else
                    {
                        toggle = 0;
                    }
                }
                ptrace(PTRACE_SYSCALL, child, NULL, NULL);
            }
        }
        return 0;
    }

    这里做的操作是,子进程会调用ls命令,而父进程会将子进程的结果反转后输出。

    参考:https://www.cnblogs.com/catch/p/3476280.html

    https://omasko.github.io/2018/04/19/ptrace%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0I/

    https://www.linuxjournal.com/article/6100

    https://www.linuxjournal.com/article/6210

    联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=
  • 相关阅读:
    Ios国际化翻译工具
    软件是什么
    angular2实现图片轮播
    DIV+CSS布局最基本的内容
    angular2中使用jQuery
    如何在Ionic2项目中使用第三方JavaScript库
    Ionic2项目中使用Firebase 3
    Ionic2中ion-tabs输入属性
    The Router路由
    templating(模板)
  • 原文地址:https://www.cnblogs.com/zl1991/p/14756918.html
Copyright © 2020-2023  润新知