往linux内核挂钩子--什么应该什么不应该

晨曦之光 发布于 2012/04/10 15:01
阅读 1K+
收藏 0

总是在网上看到有人讨论拦截linux的系统调用,方法数量可谓海量,几乎都是自己写的内核模块,可是这种方式有什么意义呢,模块都能加载了还有什么做不 到的呢,要知道linux的可加载内核模块功能十分强大,再加上linux内核本身就是开放源代码的,如果说你能加载模块了,那么就可以说你完全控制了内核,你再搞什么拦截系统调用实际上一点意义都没有,用模块别说拦截系统调用了,拦截任何函数都小菜一碟,当然如果内核导出的函数你可以直接拦截,没有导出 的函数有很多办法可以找到,比如/proc/kallsyms,大不了一个一个对,肯定能找到其地址。
在网上我几乎看到了两种方法来拦截,对于2.4以前的内核,内核导出了大部分的函数,这样在模块中拦截很方便,可到了2.4内核以及2.6内核中,很多以 前导出的函数现在不再导出了,于是就要费一番周折了,常见的方式有搜索内核符号表。在所有的拦截动作中,以拦截文件操作的动作居多,我们想象一下真的需要那么麻烦的从符号表找没有导出的函数地址吗(这个操作实际上一点也不麻烦)?想想看sys_read,sys_write等操作仅仅是一个二传手,执行绪只是经过它们一下,它们的地址不再导出不是为了更加安全,你都通过模块进入内核了就说明没有什么是安全的了。linux具有的虚拟文件系统是一个很好的机制
,这说明真正做事情的就是vfs中的回调函数们,你直接拦截回调函数不就结了,每个打开的文件在内存中都有一个file结构体,而此结构体中有 file_operations结构,该结构就是该文件的回调函数集合,现在问题是你要拦截一个回调函数不得先打开该文件找到这个 file_operations结构吗?如果有此疑问的,说明这位兄弟还是对内核不熟悉,仔细看一下内核,所有的file_operations都是以全 局的静态方式提供,也就是说所有的一类文件公用一个file_operations结构体,这就好办了,随便打开一个文件,将其替换了就可以了。上面说的所有的file_operations被公用可以从以下代码看出来:

void ext2_read_inode (struct inode * inode)  //该函数在vfs中已经是很底层的了

{

...

             if (S_ISREG(inode->i_mode)) {

                 inode->i_op = &ext2_file_inode_operations;

                 inode->i_fop = &ext2_file_operations;  //这里便是

...

}

另外还有一个地方,就是在create的时候:

static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)

{

         struct inode * inode = ext2_new_inode (dir, mode);

         int err = PTR_ERR(inode);

         if (!IS_ERR(inode)) {

                 inode->i_op = &ext2_file_inode_operations;

                 inode->i_fop = &ext2_file_operations;

...

}

而 这个ext2_file_operations是在fs/ext2/file.c中被定义的,是一个全局的结构变量,所以说,内核当中实际上只有一个 ext2_file_inode_operations。通过这种方式我们就很简单的拦截了文件操作,在实际操作的时候只需要进行以下的替换就可以了:

struct file *filp;

filp=filp_open(file_path/*随便打开一个,比如'/'*/,O_RDONLY,0);

orig_read=filp->f_op->read;   //备份原来的read,你自己的钩子执行完了以后还要跳回去的,或者模块卸载以后也要换回去。

filep->f_op->read=other_read; //other_read为你自己的钩子read

filp_close(filp,0);  //记得关掉试验品

如此就完成了替换,比什么寻找IDT,然后找0x80号中断,然后寻找需要替换的系统调用号所在的地址要好的多,并且不容易出错。
上述方式虽然说简单,但是还属于黑客的方式(有点说笑了,除了黑客,正当的管理员根本不用这种方式,直接修改内核并重新编译用不了多长时间),有没有更加正规的方式挂入自己的钩子呢?当然有,回想一下linux的lsm机制,内核当中的敏感地区都已经帮你放好了关卡,只等你实现拦截还是放行的策略了,我们完全可以自己写一个lsm模块,内部实现自己的钩子策略,然后载入内核当中,这是一种比较正规的方式了。不管哪种方式,还是免不了进入内核,而进入内核后任凭你怎么做都表现不出你的技术多么高超,还是那句话,既然进入了内核,内核就完全归你掌控,你对自己掌控的东西胡作非为当然不是本事了,就好像有些人总 对自己人发脾气,这叫本事吗?特别是一些无聊青年,尝试用这种模块的方式拦截系统调用,然后试了几次都没有成功,于是痛苦,这有必要吗?如果你真的想实现一些功能,何必用模块呢?既然你都有编译加载模块的权限,你肯定有重新编译内核的权限了,直接改内核不就得了?模块是让人实现正当的事情的,不是让人来玩 耍的,特别不是让人挂钩子的,那些顶级黑客根本不是用这种方式黑掉你的电脑的,他们掌握的是你的系统的漏洞,可能他们并没有写什么模块,也没有足够高的权限,但是他们真的拦截了你的sys_write,这该归结于什么呢?有两点,一是linux内核有漏洞,二是攻击者水平很高超,二者是缺一不可的,有一些黑客进入你的机器后发现你开了root权限,他可能转身就走了,为何?太没有挑战性了,恰恰那些所谓的脚本小子会shutdown你的系统或者给你屏幕画 个鬼脸什么的。
     linux是安全的,进入内核的门户特别少,如果你想在内核挂钩子,不要尝试用模块的形式了,更多的去发掘一些内核的漏洞比较现实,一是让你过了一把探究未知世界的瘾,二是为linux内核的完善作了贡献,至于用模块挂钩子,仅仅可以说明:1.你会编译模块;2.你对linux内核还有些熟悉;3.你会用 insmod或者modprobe命令,仅此而已。另外还有一点,不要尝试用/dev/mem和/dev/kmem来直接访问内核的内存从而挂上自己的钩 子,如果你都可以访问那两个文件了,很大的可能性你就可以编译并加载内核模块了,还不如练练编写模块呢,那两个内存设备文件不是让你搞破坏的,事实上,linux系统中的任何东西都
不是让你搞破坏的。因此不要拿一些雕虫小技来证实自己的linux水平多么高超。
很多人都说linux是给高手用的,这话我不太赞同,但是我认为linux是给成熟的有品味的人用的,一些人拿到linux后,立马大肆破坏了一番,然后说linux怎么一动就坏,太娇贵了,太不安全了,这些话我听着十分来气,这只能说这些人很幼稚,法拉利跑车很贵吧,你非要开着它往墙上撞,跑车当然就撞上去了,决不替你刹车,倒是一些四流的跑车会有一些自动刹车装置。linux给了用户最大的自由,只要你会用,只要你守规矩,linux就是无敌的,可是 你非要破坏一番,linux决不像windows那样拦着你(参看《随想:从down掉系统看操作系统设计》)。


原文链接:http://blog.csdn.net/dog250/article/details/5303645
加载中
0
UchihaRyuuzaki
UchihaRyuuzaki
lz讲得很好,为啥没人顶呢,其实很多人默默受教了吧
返回顶部
顶部