taskverse學習

簡介

taskverse是《linux二進制分析》一書作者編寫的一個隱藏進程的檢測工具,它使用/proc/kcore來訪問內核內存,github的地址在這裏:https://github.com/elfmaster/taskverse。

/proc/kcore

這個文件是內核提供的用來遍歷內核內存的接口,使用elf文件格式,他的實現在內核文件\fs\proc\kcore.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\n");
        exit(-1);
        }
    while(!feof(fd))
    {
        if(fscanf(fd, "%lx %c %s", &address, &type, symbol_s)<=0){
            printf("fscanf wrong\n");
            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\n");
        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\n");
        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\n",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\n");
            exit(-1);
        }
        if(lseek(fd,k_list->offset+(addr-k_list->vaddr),SEEK_SET)<0){
            printf("lseek /proc/kcore wrong\n");
            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\n",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\n", *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目錄下的進程進行對比,如果發現不一樣則是發現了隱藏進程。
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章