分类: 网络与安全
2012-01-07 21:50:16
接下来我们介绍适用于linux操作系统的rootkit技术。主要从下面几个方面考虑:
1、可执行程序替换rootkit
2、可加载内核模块(LKM)rootkit
3、运行时间内核补丁rootkit
4、虚拟文件系统(VFS)rootkit
可执行程序替换rootkit:
可执行程序替换rootkit修改或替换磁盘上常用管理使用工具的可执行程序,以达到隐身的目的。通常,该rootkit替换/bin/login使用工具,建立一个后门。每次用户从终端或通过远程访问登录,/bin/login程序就会运行。这个程序检查用户ID,同时将用户输入的密码与保存載密码文件中的密码进行匹配。被rootkit替换的登录程序可以避开这些安全检查。除登录程序外,rootkit还可以替换其他几个程序来隐藏自己,这些程序包括du(磁盘利用)、ls(文件系统内容)、ps(进程列表)及lsmod(加载的模块)。
除完全替换可执行程序外,一些rootkit还会感染磁盘上某些可执行文件(如系统对象文件)的映像。其中一种技术是感染可加载内核模块(LKM),这种模块是动态加载到内核中的对象文件。LKM使用可执行和链接格式(ELF),它由几个部分构成,包括.symtab,其中包括链接程序所需的符号。.symtab表中的关键符号为init_module,它是用于对模块进行初始化的程序。模块被动态加载后,加载器搜索.symtab部分,从中获得这个这个模块的地址。然后,加载器调用这个程序,对模块进行初始化。rootkit可以改变init_module的地址,使其指向rootkit代码,从而对.symtab部分进行修改。当加载器将控制权转交给init_module时,控制权实际被转交给rootkit。但是,这种技术并不仅限于init_module,我们可以对它进行扩展,以便替换任何输出的程序。
可加载内核模块(LKM)rootkit
可加载内核模块指可以动态加载到Linux内核中的模块。这些模块可以扩展内核的功能,而不需要修改内核的基本映像分配。LKM的作用类似于Windows操作系统中的设备驱动程序。他们可将驱动程序动态加载到正在运行的内核中,主要用于加载设备驱动程序、文件系统驱动程序,以及为rootkit提供额外支付的网络驱动程序。加载到内核中后,LKM就可以修改关键的 内核结构。rootkit也可以利用这种功能,以隐藏自己在系统中的踪迹。
系统调用表(sys_call_table)是最常被LKM rootkit修改的结构。这种技术的作用类似于Windows的SSDT钩子。LKM rootkit可以使用这种技术修改保存在系统调用表条目中的地址,将一个系统调用重定向到rootkit代码。然后,它就可以隐藏与rootkit有关的系统活动,如被rootkit打开的进程、文件及端口。例如,要将读取系统调用重定向到rootkit代码,LKM可以对系统调用表作出如下修改:sys_call_table[SYS_read] = rootkit_read。rootkit可以修改系统调用表中与SYS_getdents系统调用有关的条目,从而隐藏rootkit进程。枚举正在运行的进程的ps命令可以使用getdents系统调用从proc目录(其中包含与每一个正在运行的基于PID进程有关的目录)中获得目录项。LKM rootkit可以从输出结果中删除PID,或返回伪造的rootkit PID。
使用lsmod命令載内部使用系统调用读取/proc/modules文件,列举出当前已加载的模块,可以检测出LKM。因此为有效隐藏模块,防止其被列举出来,rootkit需要钩取sys_call_table中的读取系统调用。
另一种隐藏模块的技术,是操纵内核用于保存模块信息的“模块结构”。这个结构并非由内核输出,只有LKM的初始化程序init_module()能够访问这个结构。init_module位于可加载的模块中,它以“模块结构”为参数。因此,如果可以操纵与rootkit名称和使用次数有关的模块结构字段,这样,内核就无法发现rootkit模块。
运行时间内核补丁rootkit
在某些情况下,为阻止LKM rootkit加载,内核本身可能并不支持LKM。这时,rootkit可以给正在运行的内核安装补丁,在内核内存中插入它的映像。使用/dev/kmem设备文件可以访问内核的虚拟内存,因此,即使内核本身并不支持LKM,它也可用于加载运行时间LKM rootkit。
通过使用在/proc/ksyms中公开输出的符号地址,或使用在内核编译过程中生成的System.map文件,可以搜索kmem设备文件的内容,从而定位需要的内核结构。例如,可以使用ksyms符号文件中的kstat符号地址,定位task_struct结构。但是,在某些情况下,系统中可能并没有System.map文件,或者ksyms文件可能并不公开输出符号,另外,如果内核不支持LKM,ksyms文件可能并不存在。
这时, rootkit模块可以修改内存中的“模块结构”,使用运行时间补丁技术隐藏自己。但是,内核并不输出“模块结构”的符号。这种情况下,可以基于模块结构字段,如模块名称字符串的地址,应用某种搜索技巧。第一次对内存的搜索可以确定模块名称,并保存它的地址。然后,第二次搜索过程中使用这个地址在模块结构中定位与该模块有关的条目。这种技术可用于隐藏 rootkit模块。
rootkit还可以修改内存中的sys_call_table结构,劫持系统调用。与结构模块类似,内核也不输出系统调用表结构。如我们所知,在Linux系统中,系统调用由int 0x80中断完成,在产生中断前,一个系统调用号码保存在EAX寄存器中,同时包含中断进入点的中断描述符保存在中断描述符表(IDT)中。因此,确定IDT的位置(使用SIDT等命令),即可获得0x80中断的进入点。0x80中断通常使用该指令调用sys_call_table(0,%eax,4)。这个指令的一个分解指令可用于获得sys_call_table的地址,该地址可在内核内存中搜索到。这样,rootkit就可以在内核内存中搜索这个地址,定位sys_call_table,然后,它可以修改相关系统调用条目,将一个系统调用重定向到rootkit代码。
rootkit还可以直接将内核模块插入到内核内存中,以加载这些模块。使用这种方法面临的主要挑战是如何找到没有被正在运行的内核使用且没有被某个内存操作(如kmalloc)覆盖的内核内存。要解决这个问题,可以将kmalloc池的起始地址与页面边界对齐。这样可以在起始地址与kmalloc内存的实际起始地址之间留下内存间隙,这里可以插入LKM。但是,这一点内存可能容纳不下LKM。rootkit可以在这段没有使用的内存区域中插入引导程序代码(其中包含为LKM分配内核内存的代码),
从而克服这种限制。这样即使内核并不支持LKM,LKM rootkit也可以加载到内核内存中。
VFS rootkit
VFS rootkit修改虚拟文件系统结构,不需要修改sys_call_table条目,而将系统调用重定向至rootkit代码。linux虚拟文件系统是内核组件,它能够跨越几种不同的文件系统(如EXT2、EXT3及NTFS),进行文件系统操作。因此,VFS在进行文件处理系统调用的程序与实际的文件 系统实现之间提供抽象层。
VFS使用文件对象、索引节点对象、目录项对象及超级块等数据结构建立了一个公共文件模型。超级块用来描述整个文件系统的信息。对每个具体文件系统来说,都有各自的超级块。超级块存放于磁盘。当内核对一个文件系统进行初始化和注册时在内存为其分配一个超级块。而索引节点的数据结构中存放的是文件系统处理文件锁需要的所有信息。文件对象结构为任何被进程访问的目录或文件建立。这种结构中的一个字段是指向file_operations结构(f_op)的指针。翻过来,file_operations结构包含一些指向某种文件系统的函数指针。
用户任何时候对一个文件提出读取要求,SYS_read系统调用就会被调用。然后,该系统调用在文件对象结构中获取读取函数(即file->f_op->read())的函数指针,它可以间接读取某种文件系统。因此,rootkit可以修改VFS结构,如上述读取函数指针,将其指向rootkit代码。使用这种方法,rootkit不需要修改sys_call_table条目就可以隐藏自己。
另一种经常被rootkit修改的VFS数据结构为索引节点对象结构。索引节点结构中也包含指向查找等索引节点操作函数的函数指针。要隐藏进程,rootkit可以修改/proc目录的查询函数的函数指针。然后,rootkit通过修改进程目录的索引节点结构来隐藏该进程。