荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: cycker (快过年吧.我想回家), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(十三)(转寄)[转载]
发信站: 荔园晨风BBS站 (Thu Jan  2 18:23:56 2003), 站内信件

【 以下文字转载自 cycker 的信箱 】
【 原文由 xiaofong@bbs.pku.edu.cn 所发表 】
发信人: chenhao (努力学习), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(十三)
发信站: 北大未名站 (2000年11月20日01:22:34 星期一) , 转信

第三部分 解决方案(给系统管理员)

3.1 LKM检测的理论和想法
我想现在该到帮助我们的系统管理员来保护他们的系统的时候了。在解释一些理论以前,
为了使你的系统变的安全,请记住如下的基本原则:

绝对不要安装你没有源代码的LKMs。(当然,这对于普通的可执行文件也适用)

如果你有了源代码,要仔细检查他们(如果你能够的话)。还记得tcpd木马问题吗?大的
软件包很复杂,因此很难看懂。但是如果你需要一个安全的系统,你必须分析源代码。

甚至你已经遵守了这些原则,你的系统还是有可能被别人闯入并放置LKM(比如说溢出等等
)。


因此,可以考虑用一个LKM记录每一个模块的加载,并且拒绝任何一个不是从指定安全安全
目录的模块的加载企图。(为了防止简单的溢出。不存在完美的方法...)。记录功能可以
通过拦截create_module(...)来很轻易的实现。用同样的方法你也可以检查模块加载的目
录。


当然拒绝任何的模块的加载也是有可能的。但是这是一个很坏的方法。因为你确实需要他
们。因此我们可以考虑改变模块的加载方式,比如说要一个密码。密码可以在你控制的cr
eate-module(...)里面检查。如果密码正确,模块就会被加载,否则,模块被丢弃。


要注意的是你必须掩藏你的模块并使他不可以被卸栽。因此,让我们来看看一些记录LKM和
密码保护的实现的原型。(通过保护的create_module(...)系统调用)。


3.1.1 一个使用的检测器的原形


对于这个简单的例子,没有什么可以说的。只不过是拦截了sys_create_module(...)并且
记录下了加载的模块的名字。


#define MODULE

#define __KERNEL__


#include <linux/module.h>

#include <linux/kernel.h>

#include <asm/unistd.h>

#include <sys/syscall.h>

#include <sys/types.h>

#include <asm/fcntl.h>

#include <asm/errno.h>

#include <linux/types.h>

#include <linux/dirent.h>

#include <sys/mman.h>

#include <linux/string.h>

#include <linux/fs.h>

#include <linux/malloc.h>


extern void* sys_call_table[];



int (*orig_create_module)(char*, unsigned long);



int hacked_create_module(char *name, unsigned long size)

{

char *kernel_name;

char hide[]="ourtool";

int ret;


kernel_name = (char*) kmalloc(256, GFP_KERNEL);

memcpy_fromfs(kernel_name, name, 255);


/*这里我们向syslog记录,但是你可以记录到任何你想要的地方*/

printk("<1> SYS_CREATE_MODULE : %s\n", kernel_name);


ret=orig_create_module(name, size);

return ret;

}



int init_module(void)

/*初始化模块*/

{

orig_create_module=sys_call_table[SYS_create_module];

sys_call_table[SYS_create_module]=hacked_create_module;

return 0;

}


void cleanup_module(void)

/*卸载模块*/

{

sys_call_table[SYS_create_module]=orig_create_module;


}



这就是所有你需要的。当然,你必须加一些代码来隐藏这个模块,这个应该没有问题。在
使得这个模块不可以被卸载以后,一个hacker只可以改变记录文件了。但是你也可以把你
的记录文件存到一个不可被接触的文件中去(看2.1来获得相关的技巧).当然,你也可以
拦截sys_init_module(...)来显示每一个模块。这不过是一个品位问题。


3.1.2 一个密码保护的create_module(...)的例子


这一节我们会讨论如何给一个模块的加载加入密码校验。我们需要两件事情来完成这项任
务:


一个检查模块加载的方法(容易)

一个校验的方法(相当的难)


第一点是十分容易实现的。只需要拦截sys_create_module(...),然后检查一些变量,内核
就会知道这次加载是否合法了。但是如何进行校验呢?我必须承认我没有花多少时间在这
个问题上。因此这个方案并不是太好。但是这是一篇LKM的文章,因此,使用你的头脑去想
一些更好的办法。我的方法是,拦截stat(...)系统调用。当你敲任何命令时,系统需要搜
索他,stat就会被调用. 因此,在敲命令的同时敲一个密码,LKM会在拦截下的stat系统调
用中检查他.[我知道这很不安全;甚至一个Linux starter都可以击败这种机制.但是(再一
次的)这并不是这里的重点....].看看我的实现(我从plaguez的一个类似的LKM中直接抢过
来了很多现存的代码....)


#define MODULE

#define __KERNEL__


#include <linux/module.h>

#include <linux/kernel.h>

#include <asm/unistd.h>

#include <sys/syscall.h>

#include <sys/types.h>

#include <asm/fcntl.h>

#include <asm/errno.h>

#include <linux/types.h>

#include <linux/dirent.h>

#include <sys/mman.h>

#include <linux/string.h>

#include <linux/fs.h>

#include <linux/malloc.h>

#include <sys/stat.h>



extern void* sys_call_table[];


/*如果lock_mod=1 就是允许加载一个模块*/

int lock_mod=0;


int __NR_myexecve;



/*拦截create_module(...)和stat(...)系统调用*/


int (*orig_create_module)(char*, unsigned long);

int (*orig_stat) (const char *, struct old_stat*);


char *strncpy_fromfs(char *dest, const char *src, int n)

{

char *tmp = src;

int compt = 0;


do {

dest[compt++] = __get_user(tmp++, 1);

}

while ((dest[compt - 1] != '\0') && (compt != n));


return dest;

}


int hacked_stat(const char *filename, struct old_stat *buf)

{

char *name;

int ret;

char *password = "password";

/*yeah,一个很好的密码*/


name  = (char *) kmalloc(255, GFP_KERNEL);


(void) strncpy_fromfs(name, filename, 255);


/*有密码么?*/

if (strstr(name, password)!=NULL)

{

  /*一次仅允许加载一个模块*/

lock_mod=1;

kfree(name);

return 0;

}

else

{

kfree(name);

ret = orig_stat(filename, buf);

}

return ret;

}


int hacked_create_module(char *name, unsigned long size)

{

char *kernel_name;

char hide[]="ourtool";

int ret;


if (lock_mod==1)

{

lock_mod=0;

ret=orig_create_module(name, size);

return ret;

}

else

{

printk("<1>MOD-POL : Permission denied !\n");

return 0;

}

return ret;

}



int init_module(void)

/*初始化模块*/

{

__NR_myexecve = 200;


while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)

__NR_myexecve--;


sys_call_table[__NR_myexecve]=sys_call_table[SYS_execve];



orig_stat=sys_call_table[SYS_prev_stat];

sys_call_table[SYS_prev_stat]=hacked_stat;


orig_create_module=sys_call_table[SYS_create_module];

sys_call_table[SYS_create_module]=hacked_create_module;


printk("<1>MOD-POL LOADED...\n");

return 0;

}


void cleanup_module(void)

/*卸载模块*/

{

sys_call_table[SYS_prev_stat]=orig_stat;

sys_call_table[SYS_create_module]=orig_create_module;


}


代码本身很清楚.下面将会告诉你如何才能让你的LKM更安全,也许这有一些多疑了 :) :


使用另外一种检验方式(使用你自己的用户空间接口,使用你自己的系统调用;使用用户的I
D(而不仅仅是普通的密码);也许你有一个生物监测设备->读一些文档并且在linux下编写自
己的设备驱动,然后使用他 :) ...)但是,要记住:哪怕是最安全的硬件保护(软件狗,生物监
测系统,一些硬件卡)也常常脆弱的不安全的软件而被击败.你可以使用一种这样的机制来让
你的系统变得安全:用一块硬件卡来控制你的整个内核.


另外一种不这么极端的方法可以是写你自己的系统调用来负责校验.(见2.11,那里有一个创
建一个你自己的系统调用的例子)


找到一个更好的方法在sys_create_module(...)中进行检查.检查一个变量并不是十分的安
全.如果某些人控制了你的系统.他是可以修改内存的(见下一章)


找到一个方法使得一个入侵者没有办法通过你的校验来加载他的LKM


加入隐藏的功能.


...


有很多工作可以做.但是即使有了这些工作,你的系统也不是完全就是安全的.如果某些人控
制了你的系统,他是可以发现一些方法来加载他的LKM的(见下一章);甚至他并不需要一个L
KM,因为他只是控制了这个系统,并不想隐藏文件或者进程(和其他的LKM提供的美妙的功能
).


3.2 防止LKM传染者的方法


内存驻留的扫描程序(实时的)(就像DOS下的TSR病毒扫描;或者WIN9x下的VxD病毒扫描)


文件检查扫描器(检查模块文件里面的特征字串)


第一种方法可以通过拦截sys_create_module实现(或者init_module调用).第二种方法需要
一些模块文件的特征字串.因此我们必须检查两个elf文件头或者标志位.当然,其他的一些
LKM传染者可能使用一些改进了的方法.(加密,自我更改代码等等).我不会提供一个检查文
件的扫描器.因为你只不过需要写一个小的用户空间的程序来读进模块文件,并且检查两种
elf文件头('ELF'字符串,比如)



--
※ 来源:.北大未名站 bbs.pku.edu.cn [FROM: 162.105.45.129]
--
※ 转寄:·北大未名站 bbs.pku.edu.cn·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.36.220]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店