6——Linux二进制分析——学习——ptrace调试器V2

这是3.5章节的内容,这个例子对上一章节的ptrace工具进行了完善,主要是添加了attach功能,也就是获取正在运行的程序的信息,gdb就有这个功能,用的应该也是这个方法。本来作者写此书的时候就已经很简略了,省去了很多详细的介绍,测试代码也没有给出。手头的中文版中的代码更是经常有错误,如果照搬书中的例子注定无法得到预期的结果。我进行了修改和调试,能够得到书中作者提到的正确的结果。

这个例子只是对上一个例子的补充,上一篇文章全文都可以参考。代码中有XBter字段的注释是我添加的。增加的是PTRACE_ATTACH和getopt()接口,可以参考代码和注释。下面贴出代码,模块清晰很简单,编译参考上一篇文章即可。

test2.c

#include <stdio.h>
#include <unistd.h>

void print_string(char *);

int main(void){
    int i = 0;
    for(; i < 20; i++){
        print_string("Hello 1");
        sleep(2);
    }
    return 0;
}

void print_string(char *str){
    printf("%s\n",str);
}

tracer2.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/ptrace.h>
#include <sys/mman.h>
#include <sys/wait.h>//The sample of the book don't include this lib, it's wrong.

typedef struct handle{
    char *exec;
    char *symname;
    Elf64_Addr symaddr;//symname's address
    Elf64_Ehdr *ehdr;
    Elf64_Phdr *phdr;
    Elf64_Shdr *shdr;
    uint8_t *mem;
    struct user_regs_struct pt_reg;
}handle_t;

int global_pid;
Elf64_Addr lookup_symbol(handle_t *, const char *);
char * get_exe_name(int);
void sighandler(int);
#define EXE_MODE 0
#define PID_MODE 1

int main(int argc, char **argv, char **envp)
{
    int fd, c, mode = 0;
    handle_t h;
    struct stat st;
    long trap, orig;
    int status, pid;
    char * args[2];

    printf("Usage: %s [-ep <exe>/<pid>] [f <fname>]\n", argv[0]);
    memset(&h, 0, sizeof(handle_t));

    /* This is a important and amazing function--getopt(),
     * getopt() must be the function almost all linux tools
     * used.  XBter*/
    while((c = getopt(argc, argv, "p:e:f:")) != -1){
        switch(c){
            case 'p':
                pid = atoi(optarg);
                h.exec = get_exe_name(pid);
                if(h.exec == NULL){
                    printf("Unable to retrieve executable path for pid: %d\n", pid);
                    exit(-1);
                }
                mode = PID_MODE;
                break;
            case 'e':
                if((h.exec = strdup(optarg)) == NULL){
                    perror("strdup");
                    exit(-1);
                }
                mode = EXE_MODE;
                break;
            case 'f':
                if((h.symname = strdup(optarg)) == NULL){
                    perror("strdup");
                    exit(-1);
                }
                break;
            default:
                printf("Unknown option\n");
                break;
        }
    }

    /* I think this is needless, program can't run in it.  XBter */
    if(h.symname == NULL){
        printf("Specifying a function name with -f option is required\n");
        exit(-1);
    }

    if(mode == EXE_MODE){
        args[0] = h.exec;
        args[1] = NULL;
    }

    signal(SIGINT, sighandler);
    if((fd = open(h.exec, O_RDONLY)) < 0){
        perror("open");
        exit(-1);
    }

    if(fstat(fd, &st) < 0){
        perror("fstat");
        exit(-1);
    }

    h.mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(h.mem == MAP_FAILED){
        perror("mmap");
        exit(-1);
    }

    h.ehdr = (Elf64_Ehdr*)h.mem;
    h.phdr = (Elf64_Phdr*)(h.mem + h.ehdr->e_phoff);
    h.shdr = (Elf64_Shdr*)(h.mem + h.ehdr->e_shoff);

    if(h.mem[0] != 0x7f || strncmp((char*)&h.mem[1], "ELF", 3)){
        printf("%s is not an ELF file\n", h.exec);
        exit(-1);
    }

    /* I use gcc, so I should add the -no-pie flag. XBter */
    if(h.ehdr->e_type != ET_EXEC){
        printf("%s is not an ELF executable. The type: %d\n", h.exec, h.ehdr->e_type);
        exit(-1);
    }

    if(h.ehdr->e_shstrndx == 0 || h.ehdr->e_shoff == 0 || h.ehdr->e_shnum == 0){
        printf("Section header table not found\n");
        exit(-1);
    }

    if((h.symaddr = lookup_symbol(&h, h.symname)) == 0){
        printf("Unable to find symbol: %s not found in executable\n", h.symname);
        exit(-1);
    }

    close(fd);

    if(mode == EXE_MODE){
        if((pid = fork()) < 0){
            perror("fork");
            exit(-1);
        }

        if(pid == 0){
            if(ptrace(PTRACE_TRACEME, pid, NULL, NULL) < 0){
                perror("PTRACE_TRACEME");
                exit(-1);
            }
            execve(h.exec, args, envp);//run the child pid.  XBter
            exit(0);
        }
    }else{ //PID_MODE
        if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0){
            perror("PTRACE_ATTACH");
            exit(-1);
        }
    }

    wait(&status);//wait tracee to stop
    global_pid = pid;
    printf("\nXBter\n\nBeginning analysis of pid: %d at %lx\n", pid, h.symaddr);

    // Read the 8 byte at h.symaddr
    if((orig = ptrace(PTRACE_PEEKTEXT, pid, h.symaddr, NULL)) < 0){
        perror("PTRCE_PEEKTEXT");
        exit(-1);
    }

    /* Set breakpoint, 0xcc. XBter*/
    trap = (orig & ~0xff) | 0xcc;
    if(ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0){
        perror("PTRACE_PEEKTEXT");
        exit(-1);
    }

    // Begin tracing execution
trace:
    if(ptrace(PTRACE_CONT, pid, NULL, NULL) < 0){
        perror("PTRACE_CONT");
        exit(-1);
    }

    wait(&status);
    /**
     * WIFSTOPPED(status): 如果进程在被ptrace调用监控的时候被信号暂停/停止,返回True
     * WSTOPSIG(status): WIFSTOPPED为true时,返回导致子进程停止的信号类型
     * WIFEXITED(status):子进程正常退出情况下为true
     */
     /**
      * If we receive a SIgTRAP then we presumably hit a break
      * Point instrunction. In which case we will print out the
      * current register state.
      */
    if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
    {
        if(ptrace(PTRACE_GETREGS, pid, NULL, &h.pt_reg) < 0)
        {
            perror("PTRACE_GETREGS");
            exit(-1);
        }

        printf("\nExecutable %s (pid: %d) has hit breakpoint 0x%lx\n\n", h.exec, pid, h.symaddr);

        printf("%%rcx: %llx\n%%rdx: %llx\n%%rbx: %llx\n"
               "%%rax: %llx\n%%rdi: %llx\n%%rsi: %llx\n"
               "%%r8: %llx\n%%r9: %llx\n%%r10: %llx\n"
               "%%r11: %llx\n%%r12: %llx\n%%r13: %llx\n"
               "%%r14: %llx\n%%r15: %llx\n%%rsp: %llx,\n"
               "%%rip: %llx\n",
               h.pt_reg.rcx, h.pt_reg.rdx, h.pt_reg.rbx,
               h.pt_reg.rax, h.pt_reg.rdi, h.pt_reg.rsi,
               h.pt_reg.r8, h.pt_reg.r9, h.pt_reg.r10,
               h.pt_reg.r11, h.pt_reg.r12, h.pt_reg.r13,
               h.pt_reg.r14, h.pt_reg.r15, h.pt_reg.rsp,
               h.pt_reg.rip);
        printf("\nPlease hit any key to contunue: ");

        getchar();

        /* restore the program, delete the breakpoint. XBter*/
        if(ptrace(PTRACE_POKETEXT, pid, h.symaddr, orig) < 0)
        {
            perror("PTRACE_POKETEXT");
            exit(-1);
        }

        /* rip: next cmd address.  XBter */
        h.pt_reg.rip = h.pt_reg.rip - 1;
        printf("%d rip=%llx\n",__LINE__, h.pt_reg.rip);
        if(ptrace(PTRACE_SETREGS, pid, NULL, &h.pt_reg) < 0)
        {
            perror("PTRACE_SETREGS");
            exit(-1);
        }
        if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
        {
            perror("PTRACE_SINGLESTEP");
            exit(-1);
        }
        wait(NULL);
        if(ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0)
        {
            perror("PTRACE_POKETEXT");
            exit(-1);
        }
        goto trace;
    }
    if(WIFEXITED(status))
        printf("Completed tracing pid: %d\n", pid);
    exit(0);
}

/**
 * This function will lookup a symbol by name, specifically from
 * The .symtab section, and return the symbol value.
 */
Elf64_Addr lookup_symbol(handle_t *h, const char *symname)
{
    int i, j;
    char *strtab;      //.strtab section
    Elf64_Sym *symtab; //.symtab section
    for(i = 0; i <h->ehdr->e_shnum; i++){
        if(h->shdr[i].sh_type == SHT_SYMTAB){
            strtab = (char*)&h->mem[h->shdr[h->shdr[i].sh_link].sh_offset];
            symtab = (Elf64_Sym*)&h->mem[h->shdr[i].sh_offset];
            for(j = 0; j < h->shdr[i].sh_size/sizeof(Elf64_Sym); j++){
                if(strcmp(&strtab[symtab->st_name], symname) == 0)
                    return (symtab->st_value);
                symtab++;
            }
        }
    }
    return 0;
}

/**
 * This function will parse the cmdline proc entry to retrieve
 * the executable name of the process.
 */
char * get_exe_name(int pid){
    char cmdline[255], path[512], *p;
    int fd;
    snprintf(cmdline, 255, "/proc/%d/cmdline", pid);
    if((fd = open(cmdline, O_RDONLY)) < 0){
        perror("open");
        exit(-1);
    }

    if(read(fd, path, 512) < 0){
        perror("open");
        exit(-1);
    }

    if((p = strdup(path)) == NULL){
        perror("strdup");
        exit(-1);
    }

    return p;
}

void sighandler(int sig){
     printf("Caught SIGINT: Detaching from %d\n", global_pid);
     if(ptrace(PTRACE_DETACH, global_pid, NULL, NULL) < 0 && errno){
         perror("");
         exit(-1);
     }
     exit(0);
 }

gcc调试一切正常,支持-p和-e两种模式。执行指令:

./tracer2 -p `pidof ./test2`  -f print_string

./tracer2 -e ./test2  -f print_string

 

文章中有这么一段话,......附加到一个已经处于运行状态的进程上......。所以在运行的时候需要先把要附加的程序运行起来,我这里也就是test2程序。在一个终端上./test2,然后在另外一个终端上执行./tracer2 -p `pidof ./test2`  -f print_string。这样就能在运行的程序上加断点了,gdb应该用的就是这种办法。

谢谢读者指出问题,谢谢。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值