linux 函数追踪器

linux 函数追踪器
linux 函数追踪器

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <elf.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/reg.h>
#include <stdarg.h>

/*
 * For our color coding output
 */
#define WHITE          "\x1B[37m"
#define RED            "\x1B[31m"
#define GREEN          "\x1B[32m"
#define YELLOW         "\x1B[33m"
#define DEFAULT_COLOR  "\x1B[0m"

#define MAX_SYMS 8192 * 2

// On 32bit systems should be set:
//export FTRACE_ARCH=32

#define FTRACE_ENV "FTRACE_ARCH"

#define MAX_ADDR_SPACE 256
#define MAXSTR 512

#define TEXT_SPACE  0
#define DATA_SPACE  1
#define STACK_SPACE 2
#define HEAP_SPACE  3

#define CALLSTACK_DEPTH 0xf4240

//用与保存内存中的 opcode
struct branch_instr {
    const char *mnemonic;
    uint8_t opcode;
};

#define BRANCH_INSTR_LEN_MAX 5

//分支指令
struct branch_instr branch_table[64] = {
            {"jo",  0x70},
            {"jno", 0x71},  {"jb", 0x72},  {"jnae", 0x72},  {"jc", 0x72},  {"jnb", 0x73},
            {"jae", 0x73},  {"jnc", 0x73}, {"jz", 0x74},    {"je", 0x74},  {"jnz", 0x75},
            {"jne", 0x75},  {"jbe", 0x76}, {"jna", 0x76},   {"jnbe", 0x77}, {"ja", 0x77},
            {"js",  0x78},  {"jns", 0x79}, {"jp", 0x7a},    {"jpe", 0x7a}, {"jnp", 0x7b},
            {"jpo", 0x7b},  {"jl", 0x7c},  {"jnge", 0x7c},  {"jnl", 0x7d}, {"jge", 0x7d},
            {"jle", 0x7e},  {"jng", 0x7e}, {"jnle", 0x7f},  {"jg", 0x7f},  {"jmp", 0xeb},
            {"jmp", 0xe9},  {"jmpf", 0xea}, {NULL, 0}
};

struct elf_section_range {
    char *sh_name;
    unsigned long sh_addr;
    unsigned int sh_size;
};

struct {
    int stripped;
    int callsite;
    int showret;
    int attach;
    int verbose;
    int elfinfo;
    int typeinfo; //imm vs. ptr
    int getstr;
    int arch;
    int cflow;
} opts;
//elf 64位 文件
struct elf64 {
        //文件头
        Elf64_Ehdr *ehdr;
        //程序头基址
        Elf64_Phdr *phdr;
        //节表头头基址
        Elf64_Shdr *shdr;
        //符号表
        Elf64_Sym  *sym;
        //动态段
        Elf64_Dyn  *dyn;

    char *StringTable;
    char *SymStringTable;
};
typedef struct
{
    Elf32_Word  sh_name;        /* Section name (string tbl index) */
    Elf32_Word  sh_type;        /* Section type */
    Elf32_Xword sh_flags;       /* Section flags */
    Elf32_Addr  sh_addr;        /* Section virtual addr at execution */
    Elf32_Off   sh_offset;      /* Section file offset */
    Elf32_Xword sh_size;        /* Section size in bytes */
    Elf32_Word  sh_link;        /* Link to another section */
    Elf32_Word  sh_info;        /* Additional section information */
    Elf32_Xword sh_addralign;       /* Section alignment */
    Elf32_Xword sh_entsize;     /* Entry size if section holds table */
} Elf32_Shdr;
//elf 32位 文件
struct elf32 {
    Elf32_Ehdr *ehdr;
    Elf32_Phdr *phdr;
    Elf32_Shdr *shdr;
    Elf32_Sym  *sym;
    Elf32_Dyn  *dyn;

    char *StringTable;
    char *SymStringTable;

};
//堆栈 数据段等
struct address_space {
    //起始地址
    unsigned long svaddr;
    //结束地址
    unsigned long evaddr;
    //大小
    unsigned int size;
    //数量
    int count;
};
//符号条目
struct syms {
    char *name;
    unsigned long value;
};
//断点
typedef struct breakpoint {
    unsigned long vaddr;
    long orig_code;
} breakpoint_t;
//调用栈帧数据
typedef struct calldata {
        char *symname;
        char *string;
        unsigned long vaddr;
        unsigned long retaddr;
    //  unsigned int depth;
        breakpoint_t breakpoint;
} calldata_t;
//调用栈
typedef struct callstack {
    calldata_t *calldata;
    unsigned int depth;
} callstack_t;

struct call_list {
    char *callstring;
    struct call_list *next;
};

#define MAX_SHDRS 256
//用户保存要用到的数据
struct handle {
    char *path;
    char **args;
    uint8_t *map;
    struct elf32 *elf32;
    struct elf64 *elf64;
    struct elf_section_range sh_range[MAX_SHDRS];
    struct syms lsyms[MAX_SYMS]; //local syms
    struct syms dsyms[MAX_SYMS]; //dynamic syms
    char *libnames[256];
    int lsc; //lsyms count
    int dsc; // dsyms count
    int lnc; //libnames count
    int shdr_count;
    int pid;
};
//全局 pid
int global_pid;

void load_elf_section_range(struct handle *);
void get_address_space(struct address_space *, int, char *);
void MapElf32(struct handle *);
void MapElf64(struct handle *);
void *HeapAlloc(unsigned int);
char *xstrdup(const char *);
char *get_section_by_range(struct handle *, unsigned long);
//下断点
void set_breakpoint(callstack_t *callstack)
{
    int status;
    long orig = ptrace(PTRACE_PEEKTEXT, global_pid, callstack->calldata[callstack->depth].retaddr);
    long trap;

    trap = (orig & ~0xff) | 0xcc;
    if (opts.verbose)
        printf("[+] Setting breakpoint on 0x%lx\n", callstack->calldata[callstack->depth].retaddr);

    ptrace(PTRACE_POKETEXT, global_pid, callstack->calldata[callstack->depth].retaddr, trap);
    callstack->calldata[callstack->depth].breakpoint.orig_code = orig;
    callstack->calldata[callstack->depth].breakpoint.vaddr = callstack->calldata[callstack->depth].retaddr;

}
//移除断点
void remove_breakpoint(callstack_t *callstack)
{
    int status;
    if (opts.verbose)
        printf("[+] Removing breakpoint from 0x%lx\n", callstack->calldata[callstack->depth].retaddr);

    ptrace(PTRACE_POKETEXT, global_pid,
    callstack->calldata[callstack->depth].retaddr, callstack->calldata[callstack->depth].breakpoint.orig_code);
}

// 函数深度和返回值跟踪的堆栈的简单数组实现
void callstack_init(callstack_t *callstack)
{
    callstack->calldata = (calldata_t *)HeapAlloc(sizeof(calldata_t) * CALLSTACK_DEPTH);
    callstack->depth = -1; // 0 is first element

}
//push调用栈
void callstack_push(callstack_t *callstack, calldata_t *calldata)
{
    memcpy(&callstack->calldata[++callstack->depth], calldata, sizeof(calldata_t));
    set_breakpoint(callstack);
}
//pop调用栈
calldata_t * callstack_pop(callstack_t *callstack)
{
    if (callstack->depth == -1)
        return NULL;

    remove_breakpoint(callstack);
    return (&callstack->calldata[callstack->depth--]);
}

//在不弹出的情况下查看堆栈顶部
calldata_t * callstack_peek(callstack_t *callstack)
{
    if (callstack->depth == -1)
        return NULL;

    return &callstack->calldata[callstack->depth];

}

struct call_list * add_call_string(struct call_list **head, const char *string)
{
    struct call_list *tmp = (struct call_list *)HeapAlloc(sizeof(struct call_list));

    tmp->callstring = (char *)xstrdup(string);
    tmp->next = *head;
    *head = tmp;

    return *head;

}

void clear_call_list(struct call_list **head)
{
    struct call_list *tmp;

    if (!head)
        return;

    while (*head != NULL) {
        tmp = (*head)->next;
        free (*head);
        *head = tmp;
    }
}
//在branch_table中搜索对应的opcode
struct branch_instr * search_branch_instr(uint8_t instr)
{
    int i;
    struct branch_instr *p, *ret;

    for (i = 0, p = branch_table;
            p->mnemonic != NULL;
            p++, i++) {
        if (instr == p->opcode)
            return p;
    }

    return NULL;
}

void print_call_list(struct call_list **head)
{
    if (!head)
        return;

    while (*head != NULL) {
        fprintf(stdout, "%s", (*head)->callstring);
        head = &(*head)->next;
    }

}

//申请堆空间
void * HeapAlloc(unsigned int len)
{
    uint8_t *mem = (uint8_t *)malloc(len);
    if (!mem) {
        perror("malloc");
        exit(-1);
    }
    return mem;
}
//字符串拷贝
char * xstrdup(const char *s)
{
    char *p = strdup(s);
    if (p == NULL) {
        perror("strdup");
        exit(-1);
    }
    return p;
}
//格式化合并字符串
char * xfmtstrdup(const char *fmt, ...)
{
    char *s, buf[512];
    va_list va;

    va_start (va, fmt);
    //将可变参数格式化输出到一个字符数组
    vsnprintf (buf, sizeof(buf), fmt, va);
    s = xstrdup(buf);

    return s;
}
//ptrace  读取地址(src)中的数据
int pid_read(int pid, void *dst, const void *src, size_t len)
{

        int sz = len / sizeof(void *);
        int rem = len % sizeof(void *);
        unsigned char *s = (unsigned char *)src;
        unsigned char *d = (unsigned char *)dst;
        long word;

        while (sz-- != 0) {
                //            long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
                //            ptrace有四个参数:
                //            1). enum __ptrace_request request:指示了ptrace要执行的命令。
                //            2). pid_t pid: 指示ptrace要跟踪的进程。
                //            3). void *addr: 指示要监控的内存地址。
                //            4). void *data: 存放读取出的或者要写入的数据。
                //PTRACE_PEEKTEXT 请求允许追踪进程读取被追踪进程镜像的虚拟内存地址
                word = ptrace(PTRACE_PEEKTEXT, pid, s, NULL);
                if (word == -1 && errno)
                        return -1;

           *(long *)d = word;
                s += sizeof(long);
                d += sizeof(long);
        }

        return 0;
}

// 获取global/local dynamic symbol/function 信息.
int BuildSyms(struct handle *h)
{
    unsigned int i, j, k;
    //符号表
    char *SymStrTable;
    //ELF 头
    Elf32_Ehdr *ehdr32;
    //节表头
    Elf32_Shdr *shdr32;
    //符号表
    Elf32_Sym  *symtab32;
    //ELF 头64位
    Elf64_Ehdr *ehdr64;
    //节表头64 位
    Elf64_Shdr *shdr64;
    //符号表64位
    Elf64_Sym  *symtab64;
    int st_type;
    h->lsc = 0;
    h->dsc = 0;
    //判断处理器架构
    switch(opts.arch) {
        case 32:
            ehdr32 = h->elf32->ehdr;
            shdr32 = h->elf32->shdr;
            //循环遍历所有节
            for (i = 0; i < ehdr32->e_shnum; i++) {
                if (shdr32[i].sh_type == SHT_SYMTAB || shdr32[i].sh_type == SHT_DYNSYM) {
                    //在映射中找到对应的字符
                    SymStrTable = (char *)&h->map[shdr32[shdr32[i].sh_link].sh_offset];
                                symtab32 = (Elf32_Sym *)&h->map[shdr32[i].sh_offset];
                                //遍历所有字符条目
                                for (j = 0; j < shdr32[i].sh_size / sizeof(Elf32_Sym); j++, symtab32++) {
                                    //字符相关信息
                                    st_type = ELF32_ST_TYPE(symtab32->st_info);
                                    if (st_type != STT_FUNC)
                                        continue;
                                    //判断类型是普通符号还是动态库中的符号
                                    switch(shdr32[i].sh_type) {   //名称 ,值
                                        case SHT_SYMTAB:
                                            h->lsyms[h->lsc].name = xstrdup(&SymStrTable[symtab32->st_name]);
                                            h->lsyms[h->lsc].value = symtab32->st_value;
                                            h->lsc++;
                                            break;
                                        case SHT_DYNSYM:
                                            h->dsyms[h->dsc].name = xstrdup(&SymStrTable[symtab32->st_name]);
                                            h->lsyms[h->lsc].value = symtab32->st_value;
                                            h->dsc++;
                                            break;
                                    }
                                }
                }
            }
            //保存重定位符号表
            h->elf32->StringTable = (char *)&h->map[shdr32[ehdr32->e_shstrndx].sh_offset];
                    for (i = 0; i < ehdr32->e_shnum; i++) {
                            if (!strcmp(&h->elf32->StringTable[shdr32[i].sh_name], ".plt")) {
                                    for (k = 0, j = 0; j < shdr32[i].sh_size; j += 16) {
                                            if (j >= 16) {
                                                    h->dsyms[k++].value = shdr32[i].sh_addr + j;
                                            }
                                    }
                                    break;
                            }
                    }
            break;
        case 64:
                ehdr64 = h->elf64->ehdr;
                        shdr64 = h->elf64->shdr;

                        for (i = 0; i < ehdr64->e_shnum; i++) {
                                if (shdr64[i].sh_type == SHT_SYMTAB || shdr64[i].sh_type == SHT_DYNSYM) {

                                        SymStrTable = (char *)&h->map[shdr64[shdr64[i].sh_link].sh_offset];
                                        symtab64 = (Elf64_Sym *)&h->map[shdr64[i].sh_offset];

                                        for (j = 0; j < shdr64[i].sh_size / sizeof(Elf64_Sym); j++, symtab64++) {

                        st_type = ELF64_ST_TYPE(symtab64->st_info);
                        if (st_type != STT_FUNC)
                            continue;

                                                switch(shdr64[i].sh_type) {
                                                        case SHT_SYMTAB:
                                                                h->lsyms[h->lsc].name = xstrdup(&SymStrTable[symtab64->st_name]);
                                                                h->lsyms[h->lsc].value = symtab64->st_value;
                                                                h->lsc++;
                                                                break;
                                                        case SHT_DYNSYM:
                                                                h->dsyms[h->dsc].name = xstrdup(&SymStrTable[symtab64->st_name]);
                                                                h->dsyms[h->dsc].value = symtab64->st_value;
                                                                h->dsc++;
                                                                break;
                                                }
                                        }
                                }
                        }
                        h->elf64->StringTable = (char *)&h->map[shdr64[ehdr64->e_shstrndx].sh_offset];
                        for (i = 0; i < ehdr64->e_shnum; i++) {
                                if (!strcmp(&h->elf64->StringTable[shdr64[i].sh_name], ".plt")) {
                                        for (k = 0, j = 0; j < shdr64[i].sh_size; j += 16) {
                                                if (j >= 16) {
                            h->dsyms[k++].value = shdr64[i].sh_addr + j;
                                                }
                                        }
                    break;
                                }
                        }
            break;
        }

        return 0;

}

//获取保存dynamic段
void locate_dynamic_segment(struct handle *h)
{
        int i;

    switch (opts.arch) {
        case 32:
                h->elf32->dyn = NULL;
                for (i = 0; i < h->elf32->ehdr->e_phnum; i++) {
                        if (h->elf32->phdr[i].p_type == PT_DYNAMIC) {
                                h->elf32->dyn = (Elf32_Dyn *)&h->map[h->elf32->phdr[i].p_offset];
                                break;
                        }
                }
            break;
        case 64:
            h->elf64->dyn = NULL;
                        for (i = 0; i < h->elf64->ehdr->e_phnum; i++) {
                                if (h->elf64->phdr[i].p_type == PT_DYNAMIC) {
                                        h->elf64->dyn = (Elf64_Dyn *)&h->map[h->elf64->phdr[i].p_offset];
                                        break;
                                }
                        }
            break;
    }

}

//获取相关数据
uint8_t *get_section_data(struct handle *h, const char *section_name)
{

        char *StringTable;
    int i;

    switch (opts.arch) {
        case 32:
            StringTable = h->elf32->StringTable;
            for (i = 0; i < h->elf32->ehdr->e_shnum; i++) {
                if (!strcmp(&StringTable[h->elf32->shdr[i].sh_name], section_name)) {
                    return &h->map[h->elf32->shdr[i].sh_offset];
                }
            }
            break;
        case 64:
            StringTable = h->elf64->StringTable;
                        for (i = 0; i < h->elf64->ehdr->e_shnum; i++) {
                                if (!strcmp(&StringTable[h->elf64->shdr[i].sh_name], section_name)) {
                                        return &h->map[h->elf64->shdr[i].sh_offset];
                                }
                        }
            break;
    }

    return NULL;
}
//获取字符表名称
char *get_dt_strtab_name(struct handle *h, int xset)
{
        static char *dyn_strtbl;

        if (!dyn_strtbl && !(dyn_strtbl = (char *)get_section_data(h, ".dynstr")))
                printf("[!] Could not locate .dynstr section\n");

        return dyn_strtbl + xset;
}
//解析相关dynamic段的信息
void parse_dynamic_dt_needed(struct handle *h)
{
        char *symstr;
        int i, n_entries;
        Elf32_Dyn *dyn32;
        Elf64_Dyn *dyn64;
        //获取保存dynamic段
        locate_dynamic_segment(h);
        h->lnc = 0;

    switch(opts.arch) {
        case 32:
                dyn32 = h->elf32->dyn;
                for (i = 0; dyn32[i].d_tag != DT_NULL; i++) {
                        if (dyn32[i].d_tag == DT_NEEDED) {
                                symstr = get_dt_strtab_name(h, dyn32[i].d_un.d_val);
                                h->libnames[h->lnc++] = (char *)xstrdup(symstr);
                        }
                }
            break;
        case 64:
            dyn64 = h->elf64->dyn;
            for (i = 0; dyn64[i].d_tag != DT_NULL; i++) {
                                if (dyn64[i].d_tag == DT_NEEDED) {
                                        symstr = get_dt_strtab_name(h, dyn64[i].d_un.d_val);
                                        h->libnames[h->lnc++] = (char *)xstrdup(symstr);
                                }
                        }
            break;
        }
}

//此函数试图从指针位置获取ASCII字符串
#ifdef __x86_64__
char *getstr(unsigned long addr, int pid)
{
    int i, j, c;
    uint8_t buf[sizeof(long)];
    char *string = (char *)HeapAlloc(256);
    unsigned long vaddr;

    string[0] = '"';
    for (c = 1, i = 0; i < 256; i += sizeof(long)) {
        vaddr = addr + i;
        //读进程内存
        if (pid_read(pid, buf, (void *)vaddr, sizeof(long)) == -1) {
            fprintf(stderr, "pid_read() failed: %s <0x%lx>\n", strerror(errno), vaddr);
            exit(-1);
        }

        for (j = 0; j < sizeof(long); j++) {

            if (buf[j] == '\n') {
                string[c++] = '\\';
                string[c++] = 'n';
                continue;
            }
            if (buf[j] == '\t') {
                string[c++] = '\\';
                string[c++] = 't';
                continue;
            }

            if (buf[j] != '\0' && isascii(buf[j]))
                string[c++] = buf[j];
            else
                goto out;
        }
    }

out:
    string[c++] = '"';
    string[c] = '\0';

    return string;

}
#endif

#ifdef __x86_64__
//获取对应的参数的字符串
char *getargs(struct user_regs_struct *reg, int pid, struct address_space *addrspace)
{
    unsigned char buf[12];
    int i, c, in_ptr_range = 0, j;
    char *args[256], *p;
    char tmp[512], *s;
    long val;
    char *string = (char *)HeapAlloc(MAXSTR);
    unsigned int maxstr = MAXSTR;
    unsigned int b;
    /* X86Y64只支持这一点,基本上在这里解析这个调用约定:
            mov    %rsp,%rbp
            mov    $0x6,%r9d
            mov    $0x5,%r8d
            mov    $0x4,%ecx
            mov    $0x3,%edx
            mov    $0x2,%esi
            mov    $0x1,%edi
            callq  400144 <func>
    */
    for (c = 0, in_ptr_range = 0, i = 0; i < 35; i += 5) {

        val = reg->rip - i;
        //读取当前 rip 中的数据长度为8
        if (pid_read(pid, buf, (void *)val, 8) == -1) {
            fprintf(stderr, "pid_read() failed [%d]: %s <0x%llx>\n", pid, strerror(errno), reg->rip);
            exit(-1);
        }

        in_ptr_range = 0;
        //判断 是否为mov %rsp, %rbp
        if (buf[0] == 0x48 && buf[1] == 0x89 && buf[2] == 0xe5) // mov %rsp, %rbp
            break;
        //判断首个字节
        switch((unsigned char)buf[0]) {
            //mov edi xxxx
            case 0xbf:
                //判断
                if (opts.typeinfo || opts.getstr) {
                    for (j = 0; j < 4; j++) {
                        //判断 rdi的范围
                        if (reg->rdi >= addrspace[j].svaddr && reg->rdi <= addrspace[j].evaddr) {
                            in_ptr_range++;
                            switch(j) {
                                //代码段
                                case TEXT_SPACE:
                                //判断是否有符号
                                if (opts.getstr) {
                                    //指针位置获取ASCII字符串
                                    s = getstr((unsigned long)reg->rdi, pid);
                                    if (s) {
                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                        //拷贝
                                        args[c++] = xstrdup(tmp);
                                        break;
                                    }
                                }
                                //拼接
                                sprintf(tmp, "(text_ptr *)0x%llx", reg->rdi);
                                break;
                                //数据段
                                case DATA_SPACE:
                                    if (opts.getstr) {
                                        //指针位置获取ASCII字符串
                                        s = getstr((unsigned long)reg->rdi, pid);
                                        if (s) {
                                                snprintf(tmp, sizeof(tmp), "%s", s);
                                                args[c++] = xstrdup(tmp);
                                                break;
                                        }
                                    }
                                    sprintf(tmp, "(data_ptr *)0x%llx", reg->rdi);
                                    break;
                                case HEAP_SPACE:
                                    if (opts.getstr) {
                                        //指针位置获取ASCII字符串
                                        s = getstr((unsigned long)reg->rdi, pid);
                                        if (s) {
                                                snprintf(tmp, sizeof(tmp), "%s", s);
                                                args[c++] = xstrdup(tmp);
                                                break;
                                        }
                                    }

                                    sprintf(tmp, "(heap_ptr *)0x%llx", reg->rdi);
                                    break;
                                case STACK_SPACE:
                                     if (opts.getstr) {
                                         //指针位置获取ASCII字符串
                                        s = getstr((unsigned long)reg->rdi, pid);
                                        if (s) {
                                                snprintf(tmp, sizeof(tmp), "%s", s);
                                                args[c++] = xstrdup(tmp);
                                                break;
                                        }
                                     }
                                    sprintf(tmp, "(stack_ptr *)0x%llx", reg->rdi);
                                    break;
                            }
                        }
                    }
                    if (!in_ptr_range) {
                        sprintf(tmp, "0x%llx",reg->rdi);
                    }
                    if (!s)
                        args[c++] = xstrdup(tmp);
                    break;
                }
                sprintf(tmp, "0x%llx", reg->rdi);
                args[c++] = xstrdup(tmp);
                break;
            //mov esi xxxx
            case 0xbe:
                    if (opts.typeinfo) {
                        for (j = 0; j < 4; j++) {
                                if (reg->rsi >= addrspace[j].svaddr && reg->rsi <= addrspace[j].evaddr) {
                                        in_ptr_range++;
                                        switch(j) {
                                                case TEXT_SPACE:
                                                        if (opts.getstr) {
                                                                s = getstr((unsigned long)reg->rsi, pid);
                                                                if (s) {
                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                        args[c++] = xstrdup(tmp);
                                                                        break;
                                                                }
                                                        }

                                                        sprintf(tmp, "(text_ptr *)0x%llx", reg->rsi);
                                                        break;
                                                case DATA_SPACE:
                     if (opts.getstr) {
                                                                s = getstr((unsigned long)reg->rsi, pid);
                                                                if (s) {
                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                        args[c++] = xstrdup(tmp);
                                                                        break;
                                                                }
                                                        }

                                                        sprintf(tmp, "(data_ptr *)0x%llx", reg->rsi);
                                                        break;
                                                case HEAP_SPACE:
                     if (opts.getstr) {
                                                                s = getstr((unsigned long)reg->rsi, pid);
                                                                if (s) {
                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                        args[c++] = xstrdup(tmp);
                                                                        break;
                                                                }
                                                        }

                                                        sprintf(tmp, "(heap_ptr *)0x%llx", reg->rsi);
                                                        break;
                                                case STACK_SPACE:
                     if (opts.getstr) {
                                                                s = getstr((unsigned long)reg->rsi, pid);
                                                                if (s) {
                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                        args[c++] = xstrdup(tmp);
                                                                        break;
                                                                }
                                                        }

                                                        sprintf(tmp, "(stack_ptr *)0x%llx", reg->rsi);
                                                        break;
                                        }
                                }
                        }
                        if (!in_ptr_range) {
                                sprintf(tmp, "0x%llx", reg->rsi);
                        }
                    if (!s)
                        args[c++] = xstrdup(tmp);
                    break;
                                }

                sprintf(tmp, "0x%llx", reg->rsi);
                args[c++] = xstrdup(tmp);
                break;
            //mov edx xxxx
            case 0xba:
                 if (opts.typeinfo) {
                            for (j = 0; j < 4; j++) {
                                    if (reg->rdx >= addrspace[j].svaddr && reg->rdx <= addrspace[j].evaddr) {
                                            in_ptr_range++;
                                            switch(j) {
                                                    case TEXT_SPACE:
                                                            if (opts.getstr) {
                                                                    s = getstr((unsigned long)reg->rdx, pid);
                                                                    if (s) {
                                                                            snprintf(tmp, sizeof(tmp), "%s", s);
                                                                            args[c++] = xstrdup(tmp);
                                                                            break;
                                                                    }
                                                            }

                                                            sprintf(tmp, "(text_ptr *)0x%llx", reg->rdx);
                                                            break;
                                                    case DATA_SPACE:
                            if (opts.getstr) {
                                                                    s = getstr((unsigned long)reg->rdx, pid);
                                                                    if (s) {
                                                                            snprintf(tmp, sizeof(tmp), "%s", s);
                                                                            args[c++] = xstrdup(tmp);
                                                                            break;
                                                                    }
                                                            }
                                                            sprintf(tmp, "(data_ptr *)0x%llx", reg->rdx);
                                                            break;
                                                    case HEAP_SPACE:
                                            if (opts.getstr) {
                                                                    s = getstr((unsigned long)reg->rdx, pid);
                                                                    if (s) {
                                                                            snprintf(tmp, sizeof(tmp), "%s", s);
                                                                            args[c++] = xstrdup(tmp);
                                                                            break;
                                                                    }
                                                            }
                                                            sprintf(tmp, "(heap_ptr *)0x%llx", reg->rdx);
                                                            break;
                                                    case STACK_SPACE:
                        if (opts.getstr) {
                                                                    s = getstr((unsigned long)reg->rdx, pid);
                                                                    if (s) {
                                                                            snprintf(tmp, sizeof(tmp), "%s", s);
                                                                            args[c++] = xstrdup(tmp);
                                                                            break;
                                                                    }
                                                            }
                                                            sprintf(tmp, "(stack_ptr *)0x%llx", reg->rdx);
                                                            break;
                                            }
                                    }
                            }
                            if (!in_ptr_range) {
                                    sprintf(tmp, "0x%llx", reg->rdx);
                            }
                    if (!s)
                        args[c++] = xstrdup(tmp);
                    break;
                 }

                sprintf(tmp, "0x%llx", reg->rdx);
                args[c++] = xstrdup(tmp);
                break;
            // mov ecx xxxx
            case 0xb9:
                            if (opts.typeinfo) {
                                        for (j = 0; j < 4; j++) {
                                                if (reg->rcx >= addrspace[j].svaddr && reg->rcx <= addrspace[j].evaddr) {
                                                        in_ptr_range++;
                                                        switch(j) {
                                                                case TEXT_SPACE:
                                    if (opts.getstr) {
                                                                                s = getstr((unsigned long)reg->rcx, pid);
                                                                                if (s) {
                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                        args[c++] = xstrdup(tmp);
                                                                                        break;
                                                                                }
                                                                        }
                                                                        sprintf(tmp, "(text_ptr *)0x%llx", reg->rcx);
                                                                        break;
                                                                case DATA_SPACE:
                                    if (opts.getstr) {
                                                                                s = getstr((unsigned long)reg->rcx, pid);
                                                                                if (s) {
                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                        args[c++] = xstrdup(tmp);
                                                                                        break;
                                                                                }
                                                                        }
                                                                        sprintf(tmp, "(data_ptr *)0x%llx", reg->rcx);
                                                                        break;
                                                                case HEAP_SPACE:
                                    if (opts.getstr) {
                                                                                s = getstr((unsigned long)reg->rcx, pid);
                                                                                if (s) {
                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                        args[c++] = xstrdup(tmp);
                                                                                        break;
                                                                                }
                                                                        }
                                                                        sprintf(tmp, "(heap_ptr *)0x%llx", reg->rcx);
                                                                        break;
                                                                case STACK_SPACE:
                                        if (opts.getstr) {
                                                                                s = getstr((unsigned long)reg->rcx, pid);
                                                                                if (s) {
                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                        args[c++] = xstrdup(tmp);
                                                                                        break;
                                                                                }
                                                                        }

                                                                        sprintf(tmp, "(stack_ptr *)0x%llx", reg->rcx);
                                                                        break;
                                                        }
                                                }
                                        }
                                        if (!in_ptr_range) {
                                                sprintf(tmp, "0x%llx", reg->rcx);
                                        }
                    if (!s)
                        args[c++] = xstrdup(tmp);
                    break;
                                }

                sprintf(tmp, "0x%llx", reg->rcx);
                args[c++] = xstrdup(tmp);
                break;
            case 0x41:
                switch((unsigned char)buf[1]) {
                    //mov eax xxxx
                    case 0xb8:
                            if (opts.typeinfo) {
                                                for (j = 0; j < 4; j++) {
                                                        if (reg->r8 >= addrspace[j].svaddr && reg->r8 <= addrspace[j].evaddr) {
                                                                in_ptr_range++;
                                                                switch(j) {
                                                                        case TEXT_SPACE:
                                                                        if (opts.getstr) {
                                                                                        s = getstr((unsigned long)reg->r8, pid);
                                                                                        if (s) {
                                                                                                snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                args[c++] = xstrdup(tmp);
                                                                                                break;
                                                                                        }
                                                                                }
                                                                                sprintf(tmp, "(text_ptr *)0x%llx", reg->r8);
                                                                                break;
                                                                        case DATA_SPACE:
                                            if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r8, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                sprintf(tmp, "(data_ptr *)0x%llx", reg->r8);
                                                                                break;
                                                                        case HEAP_SPACE:
                                                                                        if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r8, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                sprintf(tmp, "(heap_ptr *)0x%llx", reg->r8);
                                                                                break;
                                                                        case STACK_SPACE:
                                            if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r8, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                sprintf(tmp, "(stack_ptr *)0x%llx", reg->r8);
                                                                                break;
                                                                }
                                                        }
                                                }
                                                if (!in_ptr_range) {
                                                        sprintf(tmp, "0x%llx", reg->r8);
                                                }
                            if (!s)
                                args[c++] = xstrdup(tmp);
                            break;
                                        }

                        sprintf(tmp, "0x%llx", reg->r8);
                        args[c++] = xstrdup(tmp);
                        break;
                    case 0xb9:
                            if (opts.typeinfo) {
                                                        for (j = 0; j < 4; j++) {
                                                                if (reg->r9 >= addrspace[j].svaddr && reg->r9 <= addrspace[j].evaddr) {
                                                                        in_ptr_range++;
                                                                        switch(j) {
                                                                                case TEXT_SPACE:
                                            if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r9, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                        sprintf(tmp, "(text_ptr *)0x%llx", reg->r9);
                                                                                        break;
                                                                                case DATA_SPACE:
                                            if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r9, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                        sprintf(tmp, "(data_ptr *)0x%llx", reg->r9);
                                                                                        break;
                                                                                case HEAP_SPACE:
                                              if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r9, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                        sprintf(tmp, "(heap_ptr *)0x%llx", reg->r9);
                                                                                        break;
                                                                                case STACK_SPACE:
                                              if (opts.getstr) {
                                                                                                s = getstr((unsigned long)reg->r9, pid);
                                                                                                if (s) {
                                                                                                        snprintf(tmp, sizeof(tmp), "%s", s);
                                                                                                        args[c++] = xstrdup(tmp);
                                                                                                        break;
                                                                                                }
                                                                                        }
                                                                                        sprintf(tmp, "(stack_ptr *)0x%llx", reg->r9);
                                                                                        break;
                                                                        }
                                                                }
                                                        }
                                                        if (!in_ptr_range) {
                                                                sprintf(tmp, "0x%llx", reg->r9);
                                                        }
                            if (!s)
                                args[c++] = xstrdup(tmp);
                            break;
                                                }

                        sprintf(tmp, "0x%llx", reg->r9);
                        args[c++] = xstrdup(tmp);
                        break;
                }
        }
    }

    //用超长函数名测试的XXPY/STRCAX预分配
    if (c == 0)
        return NULL;

    for (b = 0, i = 0; i < c; i++)
        b += strlen(args[i]) + 1; // len + ','
    if (b > maxstr + 2) { // maxstr + 2 braces
        string = (char*)realloc((char *)string, maxstr + (b - (maxstr + 2)) + 1);
        maxstr += (b - maxstr) + 3;
    }

    string[0] = '(';
        strcpy((char *)&string[1], args[0]);
        strcat(string, ",");

        for (i = 1; i < c; i++) {
                strcat(string, args[i]);
                strcat(string, ",");
        }

        if ((p = strrchr(string, ',')))
                *p = '\0';
        strcat(string, ")");
        return string;

}
#endif
//求距离
int distance(unsigned long a, unsigned long b)
{
    return ((a > b) ? (a - b) : (b - a));
}

//解析ELF信息读取指令,并打印函数调用和堆栈ARG
void examine_process(struct handle *h)
{

    int symmatch = 0, cflow_change = 0;
    int i, count, status, in_routine = 0;
    //相关寄存器
    struct user_regs_struct pt_reg;
    long esp, eax, ebx, edx, ecx, esi, edi, eip;
    uint8_t buf[8];
    unsigned long vaddr;
    unsigned int offset;
    char *argstr = NULL, subname[255], output[512], *sh_src, *sh_dst;
    long ret = 0, event;
    unsigned long retaddr, cip, current_ip;
    struct call_list *call_list = NULL;
    struct branch_instr *branch;
    struct address_space *addrspace = (struct address_space *)HeapAlloc(sizeof(struct address_space) * MAX_ADDR_SPACE);

    callstack_t callstack;
    calldata_t calldata;
    calldata_t *calldp;
    //设置 pid
    global_pid = h->pid;
    //通过不同架构分配空间
    switch(opts.arch) {
        case 32:
            h->elf32 = (elf32*)HeapAlloc(sizeof(struct elf32));
            h->elf64 = NULL;
            //映射文件(h.path)同时保存相关信息(节,段等)
            MapElf32(h);
            break;
        case 64:
            h->elf64 = (elf64*)HeapAlloc(sizeof(struct elf64));
            h->elf32 = NULL;
            //映射文件(h.path)同时保存相关信息(节,段等)
            MapElf64(h);
            break;
    }

    //获取并保存相关信息 节,重定位等
    BuildSyms(h);

    //获取堆栈等相关信息
    get_address_space((struct address_space *)addrspace, h->pid, h->path);
    //打开符号信息
    if (opts.elfinfo) {
        printf("[+] Printing Symbol Information:\n\n");
        for (i = 0; i < h->lsc; i++) {
            if (h->lsyms[i].name == NULL)
                printf("UNKNOWN: 0x%lx\n", h->lsyms[i].value);
            else
                printf("%s 0x%lx\n", h->lsyms[i].name, h->lsyms[i].value);
        }
        for (i = 0; i < h->dsc; i++) {
            if (h->lsyms[i].name == NULL)
                printf("UNKNOWN: 0x%lx\n", h->lsyms[i].value);
            else
                printf("%s 0x%lx\n", h->dsyms[i].name, h->dsyms[i].value);
        }

        printf("\n[+] Printing shared library dependencies:\n\n");
        //先解析动态库中的符号同时保存
        parse_dynamic_dt_needed(h);
        //再打印出来
        for (i = 0; i < h->lnc; i++) {
            printf("[%d]\t%s\n", i + 1, h->libnames[i]);
        }
    }
    //版本信息
    if (opts.verbose ) {
        printf("[+] Printing the address space layout\n");
        printf("0x%lx-0x%lx %s [text]\n", addrspace[TEXT_SPACE].svaddr, addrspace[TEXT_SPACE].evaddr, h->path);
        printf("0x%lx-0x%lx %s [data]\n", addrspace[DATA_SPACE].svaddr, addrspace[DATA_SPACE].evaddr, h->path);
        printf("0x%lx-0x%lx %s [heap]\n", addrspace[HEAP_SPACE].svaddr, addrspace[HEAP_SPACE].evaddr, h->path);
        printf("0x%lx-0x%lx %s [stack]\n",addrspace[STACK_SPACE].svaddr, addrspace[STACK_SPACE].evaddr, h->path);
    }

    /*
     * Initiate our call frame stack
     */
    callstack_init(&callstack);
    printf("\n[+] Function tracing begins here:\n");
        for (;;) {
                //单步
                ptrace (PTRACE_SINGLESTEP, h->pid, NULL, NULL);
                wait (&status);
                count++;
                //  ptrace(PTRACE_GETREGS, h->pid, NULL, &pt_reg);

                if (WIFEXITED (status))
                    break;
                //获取寄存器值
                ptrace (PTRACE_GETREGS, h->pid, NULL, &pt_reg);
#ifdef __x86_64__
        esp = pt_reg.rsp;
        eip = pt_reg.rip;
        eax = pt_reg.rax;
        ebx = pt_reg.rbx;
        ecx = pt_reg.rcx;
        edx = pt_reg.rdx;
        esi = pt_reg.rsi;
        edi = pt_reg.rdi;
#else
        esp = pt_reg.esp;
        eip = pt_reg.eip;
        eax = pt_reg.eax;
        ebx = pt_reg.ebx;
        ecx = pt_reg.ecx;
        edx = pt_reg.edx;
        esi = pt_reg.esi;
        edi = pt_reg.edi;
#endif
        //读
        if (pid_read(h->pid, buf, (void *)eip, 8) < 0) {
            fprintf(stderr, "pid_read() failed: %s <0x%lx>\n", strerror(errno), eip);
            exit(-1);
        }
        //
        if (opts.cflow) {
            //如果EIP不在我们的二进制文件中,在一个共享对象中,那么我们不看控制流
            if (eip < addrspace[TEXT_SPACE].svaddr || eip > addrspace[TEXT_SPACE].evaddr)
                continue;
            //在branch_table中搜索对应的opcode  也就是先判断是否为 je jne等相关指令
            if (branch = search_branch_instr(buf[0])) {
                //设置单步
                ptrace(PTRACE_SINGLESTEP, h->pid, NULL, NULL);
                wait(&status);
                //获取寄存器
                ptrace(PTRACE_GETREGS, h->pid, NULL, &pt_reg);
                //设置指令指针
#ifdef __x86_64__
                current_ip = pt_reg.rip;
#else
                current_ip = pt_reg.eip;
#endif
                //偏移
                if (distance(current_ip, eip) > BRANCH_INSTR_LEN_MAX) {
                    //设置为以处理
                    cflow_change = 1;
                    //获取地址所在节的节名
                    sh_src = get_section_by_range(h, eip);
                    //获取地址所在节的节名
                    sh_dst = get_section_by_range(h, current_ip);
                    //输出信息
                    printf("%s(CONTROL FLOW CHANGE [%s]):%s Jump from %s 0x%lx into %s 0x%lx\n", YELLOW, branch->mnemonic, WHITE,
                    !sh_src?"<unknown section>":sh_src, eip,
                    !sh_dst?"<unknown section>":sh_src, current_ip);
                }
                //重置
                if (cflow_change) {
                    cflow_change = 0;
                    continue;
                }

            }
        }
        //是否遇到了断点(返回地址?)如果是这样,那么检查EAX以获得返回值,
        // 并从堆栈中弹出调用数据,这也将删除断点。
        if (buf[0] == 0xcc) {
            //在不弹出的情况下查看堆栈顶部
            calldp = callstack_peek(&callstack);
            if (calldp != NULL) {
                if (calldp->retaddr == eip) {
                    snprintf(output, sizeof(output), "%s(RETURN VALUE) %s%s = %lx\n", RED, WHITE, calldp->string, eax);
                    //POP调用堆栈并在其返回地址处删除断点。
                    fprintf(stdout, "%s", output);
                     calldp = callstack_pop(&callstack);
                    free(calldp->string);
                    free(calldp->symname);
                }
            }
        }

            //当我们捕获每个即时调用指令时,我们使用CaltStaskPuthor()将调用数据推送到堆栈上,
            // 并在函数调用的返回地址处设置断点,以便我们可以用上面的代码获得返回值
            if (buf[0] == 0xe8) {
            offset = buf[1] + (buf[2] << 8) + (buf[3] << 16) + (buf[4] << 24);
            vaddr = eip + offset + 5;
            vaddr &= 0xffffffff;
            for (i = 0; i < h->lsc; i++) {
                if (vaddr == h->lsyms[i].value) {
#ifdef __x86_64__
                    argstr = getargs(&pt_reg, h->pid, addrspace);
#endif
                    if (argstr == NULL)
                        printf("%sLOCAL_call@0x%lx:%s%s()\n", GREEN, h->lsyms[i].value,  WHITE, !h->lsyms[i].name?"<unknown>":h->lsyms[i].name);
                    else
                        printf("%sLOCAL_call@0x%lx:%s%s%s\n", GREEN, h->lsyms[i].value, WHITE,  h->lsyms[i].name, argstr);
                    //拷贝字符串
                    calldata.symname = xstrdup(h->lsyms[i].name);
                    calldata.vaddr = h->lsyms[i].value;
                    calldata.retaddr = eip + 5;
                    if (argstr == NULL)
                        //拼接字符串
                        calldata.string = xfmtstrdup("LOCAL_call@0x%lx: %s()", h->lsyms[i].value, !h->lsyms[i].name?"<unknown>":h->lsyms[i].name);
                    else
                        //拼接字符串
                        calldata.string = xfmtstrdup("LOCAL_call@0x%lx: %s%s", h->lsyms[i].value, h->lsyms[i].name, argstr);

                    if (opts.verbose)
                        printf("Return address for %s: 0x%lx\n", calldata.symname, calldata.retaddr);
                    callstack_push(&callstack, &calldata);
                    symmatch = 1;
                }

            }
            for (i = 0; i < h->dsc; i++) {
                if (vaddr == h->dsyms[i].value) {
#ifdef __x86_64__
                    argstr = getargs(&pt_reg, h->pid, addrspace);
#endif
                    if (argstr == NULL)
                            printf("%sPLT_call@0x%lx:%s%s()\n", GREEN, h->dsyms[i].value, WHITE, !h->dsyms[i].name?"<unknown>":h->dsyms[i].name);
                    else
                            printf("%sPLT_call@0x%lx:%s%s%s\n", GREEN, h->dsyms[i].value, WHITE, h->dsyms[i].name, argstr);
                    //拼接字符串
                    calldata.symname = xstrdup(h->dsyms[i].name);
                    calldata.vaddr = h->dsyms[i].value;
                    calldata.retaddr = eip + 5;
                    if (argstr == NULL)
                        //拼接字符串
                        calldata.string = xfmtstrdup("PLT_call@0x%lx: %s()", h->dsyms[i].value, !h->dsyms[i].name?"<unknown>":h->dsyms[i].name);
                    else
                        //拼接字符串
                        calldata.string = xfmtstrdup("PLT_call@0x%lx: %s%s", h->dsyms[i].value, h->dsyms[i].name, argstr);
                    if (opts.verbose)
                        printf("Return address for %s: 0x%lx\n", calldata.symname, calldata.retaddr);
                                        callstack_push(&callstack, &calldata);
                                        symmatch = 1;
                }
            }

            if (opts.stripped) {
                if (symmatch) {
                    symmatch = 0;
                } else {
#ifdef __x86_64__
                    argstr = getargs(&pt_reg, h->pid, addrspace);
#endif
                    if (argstr == NULL)
                        printf("%sLOCAL_call@0x%lx:%ssub_%lx()\n", GREEN, vaddr, WHITE, vaddr);
                    else
                        printf("%sLOCAL_call@0x%lx:%ssub_%lx%s\n", GREEN, vaddr, WHITE, vaddr, argstr);
                    //拼接字符串
                    snprintf(subname, sizeof(subname) - 1, "sub_%lx%s", vaddr, argstr == NULL ? "()" : argstr);
                    //拼接字符串
                    calldata.symname = xstrdup(subname);
                                        calldata.vaddr = vaddr;
                                        calldata.retaddr = eip + 5;
                    if (argstr == NULL)
                        //拼接字符串
                        calldata.string = xfmtstrdup("LOCAL_call@0x%lx: sub_%lx()", vaddr, vaddr);
                    else
                        //拼接字符串
                        calldata.string = xfmtstrdup("LOCAL_call@0x%lx: sub_%lx%s", vaddr, vaddr, argstr);
                                        callstack_push(&callstack, &calldata);
                                        symmatch = 1;

                }
            }

            if (argstr) {
                free(argstr);
                argstr = NULL;
            }

        }

    }

}
//映射文件(h.path)同时保存相关信息(节,段等)
void MapElf32(struct handle *h)
{
    int fd;
    struct stat st;

    if ((fd = open(h->path, O_RDONLY)) < 0) {
        fprintf(stderr, "Unable to open %s: %s\n", h->path, strerror(errno));
        exit(-1);
    }

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

    h->map = (uint8_t *)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (h->map == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }
    //elf文件头
    h->elf32->ehdr = (Elf32_Ehdr *)h->map;
    //节表头基址
    h->elf32->shdr = (Elf32_Shdr *)(h->map + h->elf32->ehdr->e_shoff);
    //程序头基址
    h->elf32->phdr = (Elf32_Phdr *)(h->map + h->elf32->ehdr->e_phoff);
    //字符串表基址
    h->elf32->StringTable = (char *)&h->map[h->elf32->shdr[h->elf32->ehdr->e_shstrndx].sh_offset];
    //加载所有节
    if (h->elf32->ehdr->e_shnum > 0 && h->elf32->ehdr->e_shstrndx != SHN_UNDEF)
                读所有节信息并保存到一个节数组里sh_range[i]
                load_elf_section_range(h);
}
//解析进程空间中的代码,数据 堆 栈
void get_address_space(struct address_space *addrspace, int pid, char *path)
{
    char tmp[64], buf[256];
        char *p, addrstr[32];
    FILE *fd;
        int i, lc;
        //拼接字符串
        snprintf(tmp, 64, "/proc/%d/maps", pid);
        //打开文件
        if ((fd = fopen(tmp, "r")) == NULL) {
                fprintf(stderr, "Unable to open %s: %s\n", tmp, strerror(errno));
                exit(-1);
        }

        for (lc = 0, p = buf;
                //循环读取一行信息
                fgets(buf, sizeof(buf), fd) != NULL;
                lc++) {
        /*
         * Get executable text and data
         * segment addresses.
         */
        //获取 执行文件的代码 数据,节的地址
        if ((char *)strchr(buf, '/') && lc == 0) {
            for (i = 0; *p != '-'; i++, p++)
                addrstr[i] = *p;
            addrstr[i] = '\0';
            //将参数nptr字符串根据参数base来转换成无符号的长整型数
            addrspace[TEXT_SPACE].svaddr = strtoul(addrstr, NULL, 16);
            for (p = p + 1, i = 0; *p != 0x20; i++, p++)
                addrstr[i] = *p;
            addrstr[i] = '\0';
            //将参数nptr字符串根据参数base来转换成无符号的长整型数
            addrspace[TEXT_SPACE].evaddr = strtoul(addrstr, NULL, 16);
            addrspace[TEXT_SPACE].size = addrspace[TEXT_SPACE].evaddr - addrspace[TEXT_SPACE].svaddr;
        }

        if ((char *)strchr(buf, '/') && strstr(buf, path) && strstr(buf, "rw-p")) {
            for (i = 0, p = buf; *p != '-'; i++, p++)
                addrstr[i] = *p;
            addrstr[i] = '\0';
            //将参数nptr字符串根据参数base来转换成无符号的长整型数
            addrspace[DATA_SPACE].svaddr = strtoul(addrstr, NULL, 16);
            for (p = p + 1, i = 0; *p != 0x20; i++, p++)
                                addrstr[i] = *p;
                        addrstr[i] = '\0';
                        //将参数nptr字符串根据参数base来转换成无符号的长整型数
                        addrspace[DATA_SPACE].evaddr = strtoul(addrstr, NULL, 16);
                        addrspace[DATA_SPACE].size = addrspace[DATA_SPACE].evaddr - addrspace[DATA_SPACE].svaddr;
        }
        /*
         * Get the heap segment address layout
         */
        //获取堆的地址
        if (strstr(buf, "[heap]")) {
            for (i = 0, p = buf; *p != '-'; i++, p++)
                addrstr[i] = *p;
            addrstr[i] = '\0';
            //将参数nptr字符串根据参数base来转换成无符号的长整型数
            addrspace[HEAP_SPACE].svaddr = strtoul(addrstr, NULL, 16);
            for (p = p + 1, i = 0; *p != 0x20; i++, p++)
                addrstr[i] = *p;
            addrstr[i] = '\0';
            //将参数nptr字符串根据参数base来转换成无符号的长整型数
            addrspace[HEAP_SPACE].evaddr = strtoul(addrstr, NULL, 16);
            addrspace[HEAP_SPACE].size = addrspace[HEAP_SPACE].evaddr - addrspace[DATA_SPACE].svaddr;
        }
        /*
         * Get the stack segment layout
         */
        //获取栈的地址
        if (strstr(buf, "[stack]")) {
             for (i = 0, p = buf; *p != '-'; i++, p++)
                                addrstr[i] = *p;
                        addrstr[i] = '\0';
                        //将参数nptr字符串根据参数base来转换成无符号的长整型数
                        addrspace[STACK_SPACE].svaddr = strtoul(addrstr, NULL, 16);
                        for (p = p + 1, i = 0; *p != 0x20; i++, p++)
                                addrstr[i] = *p;
                        addrstr[i] = '\0';
                        //将参数nptr字符串根据参数base来转换成无符号的长整型数
                        addrspace[STACK_SPACE].evaddr = strtoul(addrstr, NULL, 16);
                        addrspace[STACK_SPACE].size = addrspace[STACK_SPACE].evaddr - addrspace[STACK_SPACE].svaddr;
                }
     }
}
//通过pid获取进程路径
char * get_path(int pid)
{
    char tmp[64], buf[256];
    char path[256], *ret, *p;
    FILE *fd;
    int i;
    //Proc/pid/maps显示进程映射了的内存区域和访问权限
    //将格式化拼接
    snprintf(tmp, 64, "/proc/%d/maps", pid);
    if ((fd = fopen(tmp, "r")) == NULL) {
        //打印到终端
        fprintf(stderr, "Unable to open %s: %s\n", tmp, strerror(errno));
        exit(-1);
    }
    //读取一行
    if (fgets(buf, sizeof(buf), fd) == NULL)
        return NULL;
    //查找/首次出现的位置
    //该文件有6列,分别为:
    //地址:库在进程里地址范围
    //权限:虚拟内存的权限,r=读,w=写,x=,s=共享,p=私有;
    //偏移量:库在进程里地址范围
    //设备:映像文件的主设备号和次设备号;
    //节点:映像文件的节点号;
    //路径: 映像文件的路径
    //每项都与一个vm_area_struct结构成员对应
    p = strchr(buf, '/');
    if (!p)
        return NULL;
    for (   i = 0;
            *p != '\n' && *p != '\0';
            p++, i++
            )
        path[i] = *p;
    path[i] = '\0';
    //申请返回值的堆空间
    ret = (char *)HeapAlloc(i + 1);
    //拷贝完整路径
    strcpy(ret, path);
    //判断最后为.so文件
    if (strstr(ret, ".so")) {
        fprintf(stderr, "Process ID: %d appears to be a shared library; file must be an executable. (path: %s)\n",pid, ret);
        exit(-1);
    }
    return ret;
}
//检查elf文件
int validate_em_type(char *path)
{
    int fd;
    uint8_t *mem, *p;
    unsigned int value;
    Elf64_Ehdr *ehdr64;
    Elf32_Ehdr *ehdr32;

    if ((fd = open(path, O_RDONLY)) < 0) {
        fprintf(stderr, "Could not open %s: %s\n", path, strerror(errno));
        exit(-1);
    }

    //  原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
    //返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
    //参数:
    //  addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
    //  length: 将文件的多大长度映射到内存.
    //  prot: 映射区的保护方式, 可以是:
    //      PROT_EXEC: 映射区可被执行.
    //      PROT_READ: 映射区可被读取.
    //      PROT_WRITE: 映射区可被写入.
    //      PROT_NONE: 映射区不能存取.
    //  flags: 映射区的特性, 可以是:
    //      MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
    //      MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
    //      此外还有其他几个flags不很常用, 具体查看linux C函数说明.
    //  fd: 由open返回的文件描述符, 代表要映射的文件.
    //  offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.
    //创建内存映射
    mem = (uint8_t*)mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mem == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }
    //判断处理器架构
    switch (opts.arch) {
        case 32:
            ehdr32 = (Elf32_Ehdr *)mem;
            if (ehdr32->e_machine != EM_386)
                return 0;
            break;
        case 64:
            ehdr64 = (Elf64_Ehdr *)mem;
            if (ehdr64->e_machine != EM_X86_64 && ehdr64->e_machine != EM_IA_64)
                return 0;
            break;
    }
    return 1;
}

//读所有节信息并保存到一个节数组里sh_range[i]
void load_elf_section_range(struct handle *h)
{

    Elf32_Ehdr *ehdr32;
    Elf32_Shdr *shdr32;
    Elf64_Ehdr *ehdr64;
    Elf64_Shdr *shdr64;

    char *StringTable;
    int i;

    h->shdr_count = 0;
    switch(opts.arch) {
        case 32:
            StringTable = h->elf32->StringTable;
            ehdr32 = h->elf32->ehdr;
            shdr32 = h->elf32->shdr;

            for (i = 0; i < ehdr32->e_shnum; i++) {
                h->sh_range[i].sh_name = xstrdup(&StringTable[shdr32[i].sh_name]);
                h->sh_range[i].sh_addr = shdr32[i].sh_addr;
                h->sh_range[i].sh_size = shdr32[i].sh_size;
                if (h->shdr_count == MAX_SHDRS)
                    break;
                h->shdr_count++;
            }
            break;
        case 64:
            StringTable = h->elf64->StringTable;
                        ehdr64 = h->elf64->ehdr;
                        shdr64 = h->elf64->shdr;

                        for (i = 0; i < ehdr64->e_shnum; i++) {
                                h->sh_range[i].sh_name = xstrdup(&StringTable[shdr64[i].sh_name]);
                                h->sh_range[i].sh_addr = shdr64[i].sh_addr;
                                h->sh_range[i].sh_size = shdr64[i].sh_size;
                if (h->shdr_count == MAX_SHDRS)
                    break;
                h->shdr_count++;
                        }
                        break;

    }

}
//获取地址所在节的节名
char * get_section_by_range(struct handle *h, unsigned long vaddr)
{
    int i;

    for (i = 0; i < h->shdr_count; i++) {
        if (vaddr >= h->sh_range[i].sh_addr && vaddr <= h->sh_range[i].sh_addr + h->sh_range[i].sh_size)
            return h->sh_range[i].sh_name;
    }

    return NULL;
}

//映射文件(h.path)同时保存相关信息(节,段等)
void MapElf64(struct handle *h)
{
    int fd;
        struct stat st;

        if ((fd = open(h->path, O_RDONLY)) < 0) {
                fprintf(stderr, "Unable to open %s: %s\n", h->path, strerror(errno));
                exit(-1);
        }

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

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

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

        h->elf64->StringTable = (char *)&h->map[h->elf64->shdr[h->elf64->ehdr->e_shstrndx].sh_offset];

    if (h->elf64->ehdr->e_shnum > 0 && h->elf64->ehdr->e_shstrndx != SHN_UNDEF)
        load_elf_section_range(h);

}

void sighandle(int sig)
{
    fprintf(stdout, "Caught signal ctrl-C, detaching...\n");
    ptrace(PTRACE_DETACH, global_pid, NULL, NULL);
    exit(0);
}

//下面是信号函数集:
//1、int sigemptyset(sigset_t *set);
//该函数的作用是将信号集初始化为空。
//
//2、int sigfillset(sigset_t *set);
//该函数的作用是把信号集初始化包含所有已定义的信号。
//
//3、int sigaddset(sigset_t *set, int signo);
//该函数的作用是把信号signo添加到信号集set中,成功时返回0,失败时返回-1。
//
//4、int sigdelset(sigset_t *set, int signo);
//该函数的作用是把信号signo从信号集set中删除,成功时返回0,失败时返回-1.
//
//5、int sigismember(sigset_t *set, int signo);
//该函数的作用是判断给定的信号signo是否是信号集中的一个成员,如果是返回1,如果不是,返回0,
//如果给定的信号无效,返回-1;
//
//6、int sigpromask(int how, const sigset_t *set, sigset_t *oset);
//该函数可以根据参数指定的方法修改进程的信号屏蔽字。新的信号屏蔽字由参数set(非空)指定,
//而原先的信号屏蔽字将保存在oset(非空)中。如果set为空,则how没有意义,
//但此时调用该函数,如果oset不为空,则把当前信号屏蔽字保存到oset中。
//argc 参数个数     argv参数数组         envp环境变量
int main(int argc, char **argv, char **envp)
{
    int opt, i, pid, status, skip_getopt = 0;
    struct handle handle;
    char **p, *arch;

        struct sigaction act;
        sigset_t set;
        act.sa_handler = sighandle;
        //将参数set信号集初始化并清空
        sigemptyset (&act.sa_mask);
        act.sa_flags = 0;
        //  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
        //
        //  这个系统调用的作用是改变进程接收到的指定信号的行动。
        //
        //  使用这个函数需要包含头文件 #include <signal.h>
        //
        //  函数参数说明如下:
        //
        //  signum : 说明具体信号,它可以是除了SIGKILL和SIGSTOP之外的任何有效信号值。
        //
        //  act : 将要安装的signum定义信号的新行动。
        //
        //  oldact: 用来保存signum定义信号的过去的行动。
        sigaction (SIGINT, &act, NULL);
        sigemptyset (&set);
        sigaddset (&set, SIGINT);

    if (argc < 2) {
usage:
        printf("Usage: %s [-p <pid>] [-Sstve] <prog>\n", argv[0]);
        printf("[-p] 根据 pid 追踪\n");
        printf("[-t] 检测函数参数的类型\n");
        printf("[-s] 打印字符串值\n");
        //printf("[-r] 显示详细的输出\n");
        printf("[-v] 显示详细的输出\n");
        printf("[-e] 显示各种 elf信息(符号 依赖)\n");
        printf("[-S] 显示缺失了符号的函数调用\n");
        printf("[-C] 完成控制流分析\n");
        exit(0);
    }
    if (argc == 2 && argv[1][0] == '-')
        goto usage;
    //初始化
    memset(&opts, 0, sizeof(opts));
    //默认设置为64位
    opts.arch = 64; // default
    //获取环境变量
    arch = getenv(FTRACE_ENV);
    if (arch != NULL) {
        //字符串转换成整型数
        // On 32bit systems should be set:
        //export FTRACE_ARCH=32
        switch(atoi(arch)) {
            case 32:
                opts.arch = 32;
                break;
            case 64:
                opts.arch = 64;
                break;
            default:
                fprintf(stderr, "Unknown architecture: %s\n", arch);
                break;
        }
    }
    //判断参数
    if (argv[1][0] != '-') {
        //第二个参数
        handle.path = xstrdup(argv[1]);
        //申请空间
        handle.args = (char **)HeapAlloc(sizeof(char *) * argc - 1);

        for (i = 0, p = &argv[1];
                i != argc - 1;
                p++, i++
                ) {
            //保存参数
            *(handle.args + i) = xstrdup(*p);
        }
        //最后一个为空
        *(handle.args + i) = NULL;
        //表示合法
        skip_getopt = 1;

    } else {
        handle.path = xstrdup(argv[2]);
        handle.args = (char **)HeapAlloc(sizeof(char *) * argc - 1);

        for (i = 0, p = &argv[2]; i != argc - 2; p++, i++) {
            *(handle.args + i) = xstrdup(*p);
        }
        *(handle.args + i) = NULL;
    }

    //判断合法性
    if (skip_getopt)
        goto begin;
    //选项CSrhtvep 必须有一个参数 s 不要参数     :-C xxx -S xxx -r xxx -h xxx -t xxx -v xxx -e xxx -p xxx -s
    while ((opt = getopt(argc, argv, "CSrhtvep:s")) != -1) {
        //判断返回值                           :注意他是一个个遍历的遍历到最后就返回-1
        switch(opt) {
            case 'S':
                opts.stripped++;
                break;
            case 'r':
                opts.showret++;
                break;
            case 'v':
                opts.verbose++;
                break;
            case 'e':
                opts.elfinfo++;
                break;
            case 't':
                opts.typeinfo++;
                break;
            case 'p':
                opts.attach++;
                //pid转成×××
                handle.pid = atoi(optarg);
                break;
            case 's':
                opts.getstr++;
                break;
            case 'C':
                opts.cflow++;
                break;
            case 'h':
                goto usage;
            default:
                printf("Unknown option\n");
                exit(0);
        }
    }

begin:
    //判断版本
    if (opts.verbose) {
        switch(opts.arch) {
            case 32:
                //32模式启用
                printf("[+] 32bit ELF mode enabled!\n");
                break;
            case 64:
                //64位模式启用
                printf("[+] 64bit ELF mode enabled!\n");
                break;
        }
        //类型
        if (opts.typeinfo)
            printf("[+] Pointer type prediction enabled\n");
    }
    //判断兼容性
    if (opts.arch == 32 && opts.typeinfo) {
        //选项-t不可用于32位可执行文件
        printf("[!] Option -t may not be used on 32bit executables\n");
        exit(0);
    }

    if (opts.arch == 32 && opts.getstr) {
        //选项-s不可用于32位可执行文件
        printf("[!] Option -s may not be used on 32bit executables\n");
        exit(0);
    }
        //选项-t -s不可用于32位可执行文件
    if (opts.getstr && opts.typeinfo) {
        printf("[!] Options -t and -s may not be used together\n");
        exit(0);
    }
    //判断是附加还是直接执行
    if (!opts.attach) {
        //检查elf文件
        if (!validate_em_type(handle.path)) {
            printf("[!] ELF Architecture is set to %d, the target %s is not the same architecture\n", opts.arch, handle.path);
            exit(-1);
        }
        //直接创建一个子进程
        if ((pid = fork()) < 0) {
            perror("fork");
            exit(-1);
        }
        //      long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
        //      ptrace有四个参数:
        //      1). enum __ptrace_request request:指示了ptrace要执行的命令。
        //      2). pid_t pid: 指示ptrace要跟踪的进程。
        //      3). void *addr: 指示要监控的内存地址。
        //      4). void *data: 存放读取出的或者要写入的数据。
        //子进程
        if (pid == 0) {
            //PTRACE_TRACEME表示该进程会被父进程追踪
            if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
                        perror("PTRACE_TRACEME");
                        exit(-1);
            }
            //在使用PTRACE_TRACEME参数时,跟踪多线程程序需要使用PTRACE_SETOPTIONS来设置ptrace相关属性。
            //PTRACE_SETOPTIONS 是将父进程内由data指向的值设定为ptrace 选项,data作为掩码来解释,由下面的标志来指定:
            //
            //(1) PTRACE_O_EXITKILL:当跟踪进程退出时,向所有被跟踪进程发送SIGKILL信号将其退出,这个参数可以防止被跟踪进程脱离跟踪进程的控制。
            //
            //(2) PTRACE_O_TRACECLONE:被跟踪进程在下一次调用clone()时将其停止,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以
            //
            //通过PTRACE_GETEVENTMSG得到。
            //
            //(3) PTRACE_O_TRACEEXEC:被跟踪进程在下一次调用exec()函数时使其停止。
            //
            //(4) PTRACE_O_TRACEEXIT:被跟踪进程在退出是停止其执行,被跟踪进程的退出状态可通过PTRACE_GETEVENTMSG获得。
            //
            //(5) PTRACE_O_TRACEFORK:被跟踪进程在下次调用fork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以
            //
            //通过PTRACE_GETEVENTMSG得到。
            //
            //(6) PTRACE_O_TRACEVFORK:被跟踪进程在下次调用vfork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以
            //
            //通过PTRACE_GETEVENTMSG得到。
            //
            //PTRACE_GETEVENTMSG:获取刚刚发生的ptrace事件消息,并存放在跟踪进程由data指向的位置,addr参数被忽略。对于
            //
            //PTRACE_EVENT_FORK,PTRACE_EVENT_VFORK,PTRACE_EVENT_VFORKDOWN和PTRACE_EVENT_CLONE,data是新进程的pid.
            //从数据中设置ptrace选项
            ptrace(PTRACE_SETOPTIONS, 0, 0, PTRACE_O_TRACEEXIT);
            //启动进程
            execve(handle.path, handle.args, envp);
            //从原来拷贝的父进程退出
            exit(0);
        }
        //pid_t waitpid(pid_t pid,int *status,int options)
        //
        //从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。
        //
        //参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。   
        //
        //pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。     
        //pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
        //
        //pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。   
        //
        //pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
        //
        //pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。   
        //
        //options: options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
        //父进程
        waitpid(0, &status, WNOHANG);
        //保存
        handle.pid = pid;
        //同时设置为全局
        global_pid = pid;
        //解析ELF信息读取指令,并打印函数调用和堆栈ARG
        examine_process(&handle);
        goto done;
    }

    //这里就是附加一个进程
    if (ptrace(PTRACE_ATTACH, handle.pid, NULL, NULL) == -1) {
        perror("PTRACE_ATTACH");
        exit(-1);
    }
    //通过pid获取进程路径
    handle.path = get_path(handle.pid);
        //判断
        if (!validate_em_type(handle.path)) {
            printf("[!] ELF Architecture is set to %d, the target %s is not the same architecture\n", opts.arch, handle.path);
            exit(-1);
        }

    waitpid(handle.pid, &status, WUNTRACED);
    global_pid = handle.pid;
    //解析ELF信息读取指令,并打印函数调用和堆栈ARG
    examine_process(&handle);

done:
    printf("%s\n", WHITE);
    //重启被追踪的进程,并解除追踪
    ptrace(PTRACE_DETACH, handle.pid, NULL, NULL);
    exit(0);

}

转载于:https://blog.51cto.com/haidragon/2134709

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值