Rootkit--阻止其他模块加载

目的

把进来的漏洞堵上,防止其他人进入系统,尤其是阻止有可能威胁rootkit的代码。如Anit-rootkit,这个有些难度,我们先实现了基本的控制内核模块的加载。

例子:小偷潜入了银行,要保证偷偷在银行这段时间不会被警察给揪出来。

通知链机制

在实现我们如何控制内核模块加载前,我们先简要介绍一下linux的通知链机制。

大多数的内核子系统都是相互独立的,因此有可能某个子系统对其他子系统产生的事件感兴趣。为了满足这个需求,linux提供了通知链的机制,使得某个子系统在发生某个事件时通知其他的子系统。

通知链的数据结构如下:

struct notifier_block
{
    int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
    struct notifier_block *next;
    int priority;
};

所以这个数据结构是一个函数链表,每个节点都注册了一个函数,当某个事件发生时链表上的所有节点对应的函数就会被执行。

另外通知这个事件使所运行的函数由被通知方决定,就是说被通知方可以在链表上注册一个通知函数,在发生某个事件的时候,这些函数就会得到执行,从而通知到被通知方。

我们这里的事件是加载模块。所以现在我们来分析一下内核中加载模块的代码。首先是init_module,在init_module下函数会返回一个load_module函数,所以我们跟进到load_module函数,会发现有好多流程,比如检查签名、prepare_coming_module、使模块与sysfs发生联系、执行模块入口函数等等。我们关注的是prepare_coming_module,因为这里将会调用注册的通知处理函数。然后我们继续跟进到prepare_coming_module,会发现它有个blocking_notifier_call_chain函数传入了MODULE_STATE_COMING参数,还有通知链表module_notify_list。它会调用通知链中的通知处理函数。这里相当于内核告诉模块通知链的通知处理函数一个信:MODULE_STATE_COMING,即一个模块准备好了,同时把这个模块传递给处理函数。继续跟进,会发现有这样一行代码说明在这里调用我们的通知处理函数。

ret = notifier_call_chain(&nh->head, val, v, nr_to_call,nr_calls);

所以,加载模块这个事件使用了linux通知链的机制,即模块加载时会执行通知链表上的通知处理函数。于是我们就可以编写一个通知处理函数,当其他模块完成加载,开始初始化前的状态为module_state_coming时,我们篡改他的入口函数和出口函数,达到了其他模块伪加载的目的。

实现

  • 实现通知处理函数
    • 加锁
    • 当模块状态为MODULE_STATE_COMING,我们篡改其入口函数与出口函数。
    • 解锁
  • 注册入口函数和注销出口函数

加锁的目的是为了保证篡改成功。

    //保存中断状态加锁。
    spin_lock_irqsave(&module_notifier_spinlock, flags);
    switch (module->state) {
    case MODULE_STATE_COMING:
        printk("Replacing init and exit functions: %s.\n",
                 module->name);
        // 篡改模块的初始函数与退出函数。
        module->init = fake_init;
        module->exit = fake_exit;
        break;
    default:
        break;
    }

    // 恢复中断状态解锁。
    spin_unlock_irqrestore(&module_notifier_spinlock, flags);

功能展示

guard目录下是阻止其他内核模块的加载,lamb目录下是一个简单的模块。

正常情况下lamb目录下模块的加载是这样的。

输入:

在这里插入图片描述
结果:

在这里插入图片描述

而当加载了module_block模块后,简单模块被其阻止加载了。

输入:

image-20191101132238493

实验结果:

image4

参考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章