zoukankan      html  css  js  c++  java
  • taskverse学习

    简介

    taskverse是《linux二进制分析》一书作者编写的一个隐藏进程的检测工具,它使用/proc/kcore来访问内核内存,github的地址在这里:https://github.com/elfmaster/taskverse。

    /proc/kcore

    这个文件是内核提供的用来遍历内核内存的接口,使用elf文件格式,他的实现在内核文件fsprockcore.c中,文件的主要操作在proc_kcore_operations中,围绕一个链表kclist_head来组织elf中的各个段。而一个段就代表着一段内存,将这些内存映射成可执行文件的段。看看这些段的信息,在这之前有一个note段,这里面保存着一些其他信息,这里就不分析了,从内核源码里的get_kcore_size可以看出网note段里面放的是什么。

    为了解释这个文件的用法,我写了一个简单的例子,代码在这里:https://github.com/smakk/kocre_sample

    可以使用readelf -h的命令去查看,可以发现/proc/kcore这个文件是没有节区的,也就是没有办法访问到符号表,这里使用了/proc/kallsyms这个文件来访问符号地址,具体代码如下,根据名字,不断读取符号文件来找到符号地址

    //由于/proc/kcore没有节头,找不到符号表,所以通过kallsym来找符号地址
    unsigned long get_sym(char* name){
        FILE *fd;
        char symbol_s[255];
        int i;
        unsigned long address;
        char tmp[255], type;
    
        if ((fd = fopen("/proc/kallsyms", "r")) == NULL)
        {
            printf("fopen /proc/kallsym wrong
    ");
            exit(-1);
            }
        while(!feof(fd))
        {
            if(fscanf(fd, "%lx %c %s", &address, &type, symbol_s)<=0){
                printf("fscanf wrong
    ");
                exit(-1);
            }
            if (strcmp(symbol_s, name) == 0)
            {
                fclose(fd);
                return address;
            }
            if (!strcmp(symbol_s, ""))
                break;
            }
        fclose(fd);
        return 0;
    }

    接着就是/proc/kcore的使用,这里使用一个全局的链表kcore,来表示/kcore的一个段信息中的虚拟地址,文件偏移量和大小,有这3个量就可以访问内存了

    struct kcore_list{
        struct kcore_list* list;
        unsigned long vaddr;
        unsigned long offset;
        size_t size;
    };
    struct kcore_list kcore;
    
    /*
    生成kcore链表,按照虚拟地址升序存放kcore提供的所有的地址空间,而且这些地址空间是不重叠的
    */
    void* init_kcore_list(){
        int fd = open("/proc/kcore", O_RDONLY);
        if(fd < 0){
            printf("open /proc/kcore wrong
    ");
            exit(-1);
        }
        Elf64_Ehdr head;
        read(fd,&head,sizeof(Elf64_Ehdr));
        if(lseek(fd,head.e_phoff,SEEK_SET)<0){
            printf("lseek /proc/kcore wrong
    ");
            exit(-1);
        }
        Elf64_Phdr phdr[head.e_phnum];
        read(fd,&phdr,sizeof(Elf64_Phdr)*head.e_phnum);
        close(fd);
        int i;
        for(i=0;i<head.e_phnum;i++){
            struct kcore_list* k_list = malloc(sizeof(struct kcore_list));
            k_list->vaddr = phdr[i].p_vaddr;
            k_list->offset = head.e_phoff+sizeof(Elf64_Phdr)*i;
            k_list->size = phdr[i].p_memsz;
            struct kcore_list* tmplist = &kcore;
            while(tmplist->list != NULL && tmplist->list->vaddr<k_list->vaddr){
                tmplist = tmplist->list;
            }
            k_list->list = tmplist->list;
            tmplist->list = k_list;
            //printf("%lx
    ",k_list->vaddr);
        }
        return;
    }

    现在已经存储完kcore的段信息,要想访问符号虚拟地址指向处的地址,就是要先找出虚拟地址位于哪个段中,然后转换成这个段在文件中的偏移,最终根据文件偏移去访问文件。代码如下:

    /*
    根据虚拟地址addr,从/proc/kcore这个位置读取size大小的内存
    */
    void* get_area(unsigned long addr, size_t size){
        void* ret;
        struct kcore_list* k_list = kcore.list;
        while(k_list != NULL && addr>k_list->vaddr + k_list->size){
            k_list = k_list->list;
        }
        if(addr>=k_list->vaddr && addr+size<k_list->vaddr + k_list->size){
            int fd = open("/proc/kcore", O_RDONLY);
            if(fd < 0){
                printf("open /proc/kcore wrong
    ");
                exit(-1);
            }
            if(lseek(fd,k_list->offset+(addr-k_list->vaddr),SEEK_SET)<0){
                printf("lseek /proc/kcore wrong
    ");
                exit(-1);
            }
            ret = malloc(size);
            read(fd,ret,size);
            close(fd);
        }
        return ret;
    }

     最后,使用这个借口,我做了一个简单的使用例子,找到init_task的进程描述符,在内核的进程描述符中,第一个字段表示的是进程状态,这里输出init_task的进程状态,打印这和结果。

    int main(){
        init_kcore_list();
        printf("_text addrs is %lx
    ",get_sym("_text"));
        void * code = get_area(get_sym("init_task"),100);
        unsigned long * statue = (unsigned long *)code;
        //0代表正在运行,大于0表示停止了
        printf("init_task statue is %ld
    ", *statue);
        return 0;
    }

    taskverse分析

    程序入口地址在主目录下的taskverse.c中的mian函数,在taskverse中,可以使用两种寻找符号的方式,一种是kallsyms,另一种是systemmap文件。

    沿着主函数,load_live_kcore函数去分析kcore文件,然后取出该文件的一些部分,存放形成一个Elf_t,在遍历段的过程中,作者关注了3个段,一个是文本段,一个是vmalloc使用的段,一个是kmalloc使用的段。地址分别是0xffff880100000000和0xffff880000100000,text段的起始地址放在_text符号的位置。

    值得注意的是elf结构中的mem结构,这里的mem结构放了3个内容

    1、elf头
    2、程序头表
    3、text段内容
    后面从mem中取值的时候要注意,这里的地址计算
    然后就开始获取符号地址,这里获取到init_task的位置,然后kcore_translate_kernel将它翻译为文件地址,最后从文件中读取task_struct结构的内容,遍历tasklist来获取到进程列表,然后和proc目录下的进程进行对比,如果发现不一样则是发现了隐藏进程。
     
  • 相关阅读:
    清北学堂2019.7.18 & 清北学堂2019.7.19
    清北学堂2019.7.17
    清北学堂2019.7.16
    清北学堂2019.7.15
    清北学堂2019.7.14
    清北学堂2019.7.13
    【洛谷P1383 高级打字机】
    考试整理
    考试整理
    【洛谷P5018 对称二叉树】
  • 原文地址:https://www.cnblogs.com/likaiming/p/11114670.html
Copyright © 2011-2022 走看看