| 導購 | 订阅 | 在线投稿
分享
 
 
 

Linux操作系统下隐藏文件的新方法

2008-12-24 08:08:39  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
  作者:yan

  一. 概述

  目前通用的隐藏文件方法还是hooksys_getdents64系统调用, 大致流程就是先调用原始的sys_getdents64系统调用,然后在在buf中做过滤。修改sys_call_table是比较原始的rk技术了,碰到好点的管理员, 基本上gdb一下vmlinux就能检测出来。 如何想做到更加隐蔽的话,就要寻找新的技术。 inline hook也是目前比较流行的做法,不容易检测。本文通过讲解一种利用inline hook内核中某函数, 来达到隐藏文件的方法。

  二. 剖析sys_getdnts64系统调用

  想隐藏文件, 还是要从sys_dents64系统调用下手。 去看下它在内核中是如何实现的。

  代码在linux-2.6.26/fs/readdir.c中:

  asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64

  __user * dirent, unsigned int count)

   {

   struct file * file;

   struct linux_dirent64 __user * lastdirent;

   struct getdents_callback64 buf;

   int error;

   error = -EFAULT;

   if (!access_ok(VERIFY_WRITE, dirent, count))

   goto out;

   error = -EBADF;

   file = fget(fd);

   if (!file)

   goto out;

   buf.current_dir = dirent;

   buf.previous = NULL;

   buf.count = count;

   buf.error = 0;

   error = vfs_readdir(file, filldir64, &buf);

   if (error < 0)

   goto out_putf;

   error = buf.error;

   lastdirent = buf.previous;

   if (lastdirent) {

   typeof(lastdirent->d_off) d_off = file->f_pos;

   error = -EFAULT;

   if (__put_user(d_off, &lastdirent->d_off))

   goto out_putf;

   error = count - buf.count;

   }

   out_putf:

   fput(file);

   out:

   return error;

   }

  首先调用access_ok来验证是下用户空间的dirent地址是否越界,是否可写。 接着根据fd,利用fget找到对应的file结构。 接着出现了一个填充buf数据结构的操作,先不管它是干什么的,接着往下看。

  vfs_readdir(file, filldir64, &buf);

  函数最终还是调用vfs层的vfs_readdir来获取文件列表的。 到这,我们可以是否通过hookvfs_readdir来达到隐藏文件的效果呢。 继续跟踪vfs_readdir看看这个想法是否可行。

  源代码在同一文件中:

  int vfs_readdir(struct file *file, filldir_t filler, void *buf)

   {

   struct inode *inode = file->f_path.dentry->d_inode;

   int res = -ENOTDIR;

   if (!file->f_op || !file->f_op->readdir)

   goto out;

   res = security_file_permission(file, MAY_READ);

   if (res)

   goto out;

   res = mutex_lock_killable(&inode->i_mutex);

   if (res)

   goto out;

   res = -ENOENT;

   if (!IS_DEADDIR(inode)) {

   res = file->f_op->readdir(file, buf, filler);

   file_accessed(file);

   }

   mutex_unlock(&inode->i_mutex);

   out:

   return res;

   }

  EXPORT_SYMBOL(vfs_readdir);

  它有3个参数,第一个是通过fget得到的file结构指针, 第2个通过结合上下文可得知,这是一个回调函数用来填充第3个参数开始的用户空间的指针。 接着看看它具体是怎么实现的。

  通过security_file_permission()验证后, 在用mutex_lock_killable()对inode结构加了锁。然后调用ile->f_op->readdir(file, buf, filler);通过进一步的底层函数来对buf进行填充。这个buf就是用户空间strcut dirent64结构的开始地址。

  所以到这里我们可以断定通过hook vfs_readdir函数对buf做过滤还是可以完成隐藏文件的功能。而且vfs_readdir的地址是导出的, 这样就不用复杂的方法找它的地址了。

  但是还有没有更进一步的方法呢? 前面不是提到过有个filldir64函数吗, 它用来填充buf结构的。也许通过hook它来做更隐蔽的隐藏文件方法。 继续跟踪filldir64,看看它是怎么实现的。

  static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,

   u64 ino, unsigned int d_type)

   {

   struct linux_dirent64 __user *dirent;

   struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;

   int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64));

   buf->error = -EINVAL;

   if (reclen > buf->count)

   return -EINVAL;

   dirent = buf->previous;

   if (dirent) {

   if (__put_user(offset, &dirent->d_off))

   goto efault;

   }

   dirent = buf->current_dir;

   if (__put_user(ino, &dirent->d_ino))

   goto efault;

   if (__put_user(0, &dirent->d_off))

   goto efault;

   if (__put_user(reclen, &dirent->d_reclen))

   goto efault;

   if (__put_user(d_type, &dirent->d_type))

   goto efault;

   if (copy_to_user(dirent->d_name, name, namlen))

   goto efault;

   if (__put_user(0, dirent->d_name + namlen))

   goto efault;

   buf->previous = dirent;

   dirent = (void __user *)dirent + reclen;

   buf->current_dir = dirent;

   buf->count -= reclen;

   return 0;

   efault:

   buf->error = -EFAULT;

   return -EFAULT;

   }

  先把参数buf转换成struct getdents_callback64的结构指针。

  struct getdents_callback64 {

   struct linux_dirent64 __user * current_dir;

   struct linux_dirent64 __user * previous;

   int count;

   int error;

   };

  current_dir始终指向当前的struct dirent64结构,filldir64每次只填充一个dirent64结构。

  它是被file->f_op->readdir循环调用的。 通过代码可以看出是把dirent64结构的相关项拷贝到用户空间的dirent64结构中, 然后更新相应的指针。

  所以通过分析filldir64代码, 可以判定通过判断参数name,看它是否是我们想隐藏的文件,是的话,return 0就好了。

  三. 扩展

  通过分析sys_getdents64代码的实现,我们可以了解到通过hook内核函数的方法,来完成rootkit的功能是很简单和方便的。 关键你能了解它的实现逻辑。 对linux平台来说,阅读内核源代码是开发rootkit的根本。 如何hook? 最简单的就是修改函数的前几个字节,jmp到我们的新函数中去, 在新函数完成类似函数的功能。 根本不必在跳回原函数了, 有了内核源代码在手,原函数怎么实现,我们就怎么copy过去给它在实现一次。 所在linux实现rk也有很方便的一点,就是它的内核源代码是公开的, 好好阅读源代码吧, 你会有更多的收获。
 
 
 
作者:yan 一. 概述 目前通用的隐藏文件方法还是hooksys_getdents64系统调用, 大致流程就是先调用原始的sys_getdents64系统调用,然后在在buf中做过滤。修改sys_call_table是比较原始的rk技术了,碰到好点的管理员, 基本上gdb一下vmlinux就能检测出来。 如何想做到更加隐蔽的话,就要寻找新的技术。 inline hook也是目前比较流行的做法,不容易检测。本文通过讲解一种利用inline hook内核中某函数, 来达到隐藏文件的方法。 二. 剖析sys_getdnts64系统调用 想隐藏文件, 还是要从sys_dents64系统调用下手。 去看下它在内核中是如何实现的。 代码在linux-2.6.26/fs/readdir.c中: asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64    __user * dirent, unsigned int count) { struct file * file; struct linux_dirent64 __user * lastdirent; struct getdents_callback64 buf; int error; error = -EFAULT; if (!access_ok(VERIFY_WRITE, dirent, count)) goto out; error = -EBADF; file = fget(fd); if (!file) goto out; buf.current_dir = dirent; buf.previous = NULL; buf.count = count; buf.error = 0; error = vfs_readdir(file, filldir64, &buf); if (error < 0) goto out_putf; error = buf.error; lastdirent = buf.previous; if (lastdirent) { typeof(lastdirent->d_off) d_off = file->f_pos; error = -EFAULT; if (__put_user(d_off, &lastdirent->d_off)) goto out_putf; error = count - buf.count; } out_putf: fput(file); out: return error; } 首先调用access_ok来验证是下用户空间的dirent地址是否越界,是否可写。 接着根据fd,利用fget找到对应的file结构。 接着出现了一个填充buf数据结构的操作,先不管它是干什么的,接着往下看。 vfs_readdir(file, filldir64, &buf); 函数最终还是调用vfs层的vfs_readdir来获取文件列表的。 到这,我们可以是否通过hookvfs_readdir来达到隐藏文件的效果呢。 继续跟踪vfs_readdir看看这个想法是否可行。 源代码在同一文件中: int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_path.dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); file_accessed(file); } mutex_unlock(&inode->i_mutex); out: return res; }   EXPORT_SYMBOL(vfs_readdir); 它有3个参数,第一个是通过fget得到的file结构指针, 第2个通过结合上下文可得知,这是一个回调函数用来填充第3个参数开始的用户空间的指针。 接着看看它具体是怎么实现的。 通过security_file_permission()验证后, 在用mutex_lock_killable()对inode结构加了锁。然后调用ile->f_op->readdir(file, buf, filler);通过进一步的底层函数来对buf进行填充。这个buf就是用户空间strcut dirent64结构的开始地址。 所以到这里我们可以断定通过hook vfs_readdir函数对buf做过滤还是可以完成隐藏文件的功能。而且vfs_readdir的地址是导出的, 这样就不用复杂的方法找它的地址了。 但是还有没有更进一步的方法呢? 前面不是提到过有个filldir64函数吗, 它用来填充buf结构的。也许通过hook它来做更隐蔽的隐藏文件方法。 继续跟踪filldir64,看看它是怎么实现的。 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { struct linux_dirent64 __user *dirent; struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64)); buf->error = -EINVAL; if (reclen > buf->count) return -EINVAL; dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; if (__put_user(ino, &dirent->d_ino)) goto efault; if (__put_user(0, &dirent->d_off)) goto efault; if (__put_user(reclen, &dirent->d_reclen)) goto efault; if (__put_user(d_type, &dirent->d_type)) goto efault; if (copy_to_user(dirent->d_name, name, namlen)) goto efault; if (__put_user(0, dirent->d_name + namlen)) goto efault; buf->previous = dirent; dirent = (void __user *)dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; efault: buf->error = -EFAULT; return -EFAULT; } 先把参数buf转换成struct getdents_callback64的结构指针。 struct getdents_callback64 { struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; current_dir始终指向当前的struct dirent64结构,filldir64每次只填充一个dirent64结构。 它是被file->f_op->readdir循环调用的。 通过代码可以看出是把dirent64结构的相关项拷贝到用户空间的dirent64结构中, 然后更新相应的指针。 所以通过分析filldir64代码, 可以判定通过判断参数name,看它是否是我们想隐藏的文件,是的话,return 0就好了。 三. 扩展 通过分析sys_getdents64代码的实现,我们可以了解到通过hook内核函数的方法,来完成rootkit的功能是很简单和方便的。 关键你能了解它的实现逻辑。 对linux平台来说,阅读内核源代码是开发rootkit的根本。 如何hook? 最简单的就是修改函数的前几个字节,jmp到我们的新函数中去, 在新函数完成类似函数的功能。 根本不必在跳回原函数了, 有了内核源代码在手,原函数怎么实现,我们就怎么copy过去给它在实现一次。 所在linux实现rk也有很方便的一点,就是它的内核源代码是公开的, 好好阅读源代码吧, 你会有更多的收获。
󰈣󰈤
 
 
 
>>返回首頁<<
 
 
 
 
 
 熱帖排行
 
 
王朝网络微信公众号
微信扫码关注本站公众号 wangchaonetcn
 
  免责声明:本文仅代表作者个人观点,与王朝网络无关。王朝网络登载此文出于传递更多信息之目的,并不意味著赞同其观点或证实其描述,其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有