在Linux下我们在使用设备的时候,都会用到write这个函数,通过这个函数我们可以象使
用文件那样向设备传送数据。可是为什么用户使用write函数就可以把数据写到设备里面
去,这个过程到底是怎么实现的呢?
这个奥秘就在于设备驱动程序的write实现中,这里我结合一些源代码来解释如何使得一
个简简单单的write函数能够完成向设备里面写数据的复杂过程。
这里的源代码主要来自两个地方。第一是oreilly出版的《Linux device driver》中的
实例,第二是Linux Kernel 2.2.14核心源代码。我只列出了其中相关部分的内容,如果
读者有兴趣,也可以查阅其它源代码。不过我不是在讲解如何编写设备驱动程序,所以不
会对每一个细节都进行说明,再说有些地方我觉得自己还没有吃透。
由于《Linux device driver》一书中的例子对于我们还是复杂了一些,我将其中的一个
例程简化了一下。这个驱动程序支持这样一个设备:核心空间中的一个长度为10的数组
kbuf[10]。我们可以通过用户程序open它,read它,write它,close它。这个设备的名
字我称为short_t。
现在言归正传。
对于一个设备,它可以在/dev下面存在一个对应的逻辑设备节点,这个节点以文件的形式
存在,但它不是普通意义上的文件,它是设备文件,更确切的说,它是设备节点。这个节
点是通过mknod命令建立的,其中指定了主设备号和次设备号。主设备号表明了某一类设
备,一般对应着确定的驱动程序;次设备号一般是区分是标明不同属性,例如不同的使用
方法,不同的位置,不同的操作。这个设备号是从/proc/devices文件中获得的,所以一
般是先有驱动程序在内核中,才有设备节点在目录中。这个设备号(特指主设备号)的主
要作用,就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的,当你打开一
个设备文件时,操作系统就已经知道这个设备所对应的驱动程序是哪一个了。这个"知道"
的过程后面就讲。
我们再说说驱动程序的基本结构吧。这里我只介绍动态模块型驱动程序(就是我们使用
insmod加载到核心中并使用rmmod卸载的那种),因为我只熟悉这种结构。
模块化的驱动程序由两个函数是固定的:int init_module(void) ;void
cleanup_module(void)。前者在insmod的时候执行,后者在rmmod的时候执行。
init_nodule在执行的时候,进行一些驱动程序初始化的工作,其中最主要的工作有三
件:注册设备;申请I/O端口地址范围;申请中断IRQ。这里和我们想知道的事情相关的只
有注册设备。
下面是一个典型的init_module函数:
int init_module(void){
int result = check_region(short_base,1);
……
request_region(short_base,1,"short");
……
result = register_chrdev(short_major, "short", &short_fops);
……
result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short",
NULL);
……
return 0;
}
上面这个函数我只保留了最重要的部分,其中最重要的函数是
result = register_chrdev(short_major, "short", &short_fops);
这是一个驱动程序的精髓所在!!当你执行indmod命令时,这个函数可以完成三件大事:
第一,申请主设备号(short_major),或者指定,或者动态分配;第二,在内核中注册设
备的名字("short");第三,指定fops方法(&short_fops)。其中所指定的fops方法就是
我们对设备进行操作的方法(例如read,write,seek,dir,open,release等),如何实现
这些方法,是编写设备驱动程序大部分工作量所在。
现在我们就要接触关键部分了--如何实现fops方法。
我们都知道,每一个文件都有一个file的结构,在这个结构中有一个file_operations的
结构体,这个结构体指明了能够对该文件进行的操作。
下面是一个典型的file_operations结构:
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned
long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *);
int (*fasync) (int, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};
我们可以看到它实际上就是许多文件操作的函数指针,其中就有write,其它的我们就不
去管它了。这个write指针在实际的驱动程序中会以程序员所实现的函数名字出现,它指
向程序员实现的设备write操作函数。下面就是一个实际的例子,这个write函数可以向核
心内存的一个数组里输入一个字符串。
int short_write (struct inode *inode, struct file *filp, const char *buf,
int count){
int retval = count;
extern unsigned char kbuf[10];
if(count>10)
count=10;
copy_from_user(kbuf, buf, count);
return retval;
}
设备short_t对应的fops方法是这样声明的:
struct file_operations short_fops = {
NULL,
short_read,
short_write,
NULL,
NULL,
NULL,
NULL,
short_open,
short_release,
NULL,
NULL,
};
其中NULL的项目就是不提供这个功能。所以我们可以看出short_t设备只提供了
read,write,open,release功能。其中write功能我们在上面已经实现了,具体的实现函
数起名为short_write。这些函数就是真正对设备进行操作的函数,这就是驱动程序的一
大好处:不管你实现的时候是多么的复杂,但对用户来看,就是那些常用的文件操作函数。
但是我们可以看到,驱动程序里的write函数有四个参数,函数格式如下:
short_write (struct inode *inode, struct file *filp, const char *buf, int count)
而用户程序中的write函数只有三个参数,函数格式如下:
write(inf fd, char *buf, int count)
那他们两个是怎么联系在一起的呢?这就要靠操作系统核心中的函数sys_write了,下面
是Linux Kernel 2.2.14中sys_write中的源代码:
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
{
ssize_t ret;
struct file * file;
struct inode * inode;
ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
lock_kernel();
ret = -EBADF;
file = fget(fd);
if (!file)
goto bad_file;
if (!(file->f_mode & FMODE_WRITE))
goto out;
inode = file->f_dentry->d_inode;
ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos,
count);
if (ret)
goto out;
ret = -EINVAL;
if (!file->f_op || !(write = file->f_op->write))
goto out;
down(&inode->i_sem);
ret = write(file, buf, count, &file->f_pos);
up(&inode->i_sem);
out:
fput(file);
bad_file:
unlock_kernel();
return ret;
}
我写了一个简单的程序来测试这个驱动程序,该程序源代码节选如下(该省的我都省了):
main(){
int fd,count=0;
unsigned char buf[10];
fd=open("/dev/short_t",O_RDWR);
printf("input string:");
scanf("%s",buf);
count=strlen(buf);
if(count>10)
count=10;
count=write(fd,buf,count);
close(fd);
return 1;
}
现在我们就演示一下用户使用write函数将数据写到设备里面这个过程到底是怎么实现的:
1,insmod驱动程序。驱动程序申请设备名和主设备号,这些可以在/proc/devIEces中获得。
2,从/proc/devices中获得主设备号,并使用mknod命令建立设备节点文件。这是通过主
设备号将设备节点文件和设备驱动程序联系在一起。设备节点文件中的file属性中指明了
驱动程序中fops方法实现的函数指针。
3,用户程序使用open打开设备节点文件,这时操作系统内核知道该驱动程序工作了,就
调用fops方法中的open函数进行相应的工作。open方法一般返回的是文件标示符,实际
上并不是直接对它进行操作的,而是有操作系统的系统调用在背后工作。
4,当用户使用write函数操作设备文件时,操作系统调用sys_write函数,该函数首先通
过文件标示符得到设备节点文件对应的inode指针和flip指针。inode指针中有设备号信
息,能够告诉操作系统应该使用哪一个设备驱动程序,flip指针中有fops信息,可以告诉
操作系统相应的fops方法函数在那里可以找到。
5,然后这时sys_write才会调用驱动程序中的write方法来对设备进行写的操作。
其中1-3都是在用户空间进行的,4-5是在核心空间进行的。用户的write函数和操作系统
的write函数通过系统调用sys_write联系在了一起。
注意:
对于块设备来说,还存在写的模式的问题,这应该是由GNU C库来解决的,这里不予讨
论,因为我没有看过GNU C库的源代码。
另外,这是一个测试版的文章,请各位朋友们多提意见和建议,非常感谢!
Know Your Enemy
Script KiddIE使用的工具和方法论
Honeynet Project
http://project.honeynet.org
http://www.xfocus.org
Last Modified: 21 July, 2000
我的长官经常对我说:在敌人面前好很好的保护自己,你必须先了解敌人先。(孙子兵法:知己知彼,百战百胜)。这句军事用语现在也很好的应用于网络安 全领域中了,就象作战一样,你要保护自己的资源,你需要知道谁是你最大的威胁和他们会怎样攻击。在这个系列中的第一篇文章,就是讨论一种很流行很广泛的来 自 Scritp Kiddie所使用的工具和方法,如果你或者你的组织有任何资源连接到Internet上,你就有此类的威胁。
Know Your Enemy文章主要是涉及到blackhatck团体使用的工具,策略和动机。而Know Your Enemy:II主要集中于你怎样能探测这些威胁,判断他们所使用的工具和他们在你系统上寻找什么样的漏洞。Know Your Enemy:III集中讨论攻击者获得ROOT后在系统上怎样操作,特别是他们是如何掩盖他们的踪迹和他们下一步主要干什么。Know Your Enemy: Forensics 涉及了你怎样分析一种攻击。Know Your Enemy: Motives 通过捕获black-hat团体之间的通信和联系来分析他们的动机和心理状态。Know Your Enemy: Worms at War 描述了WORM蠕虫是怎样自动攻击WINDOW系统的。
什么叫Script Kiddie
Script kiddie是一些专门找寻一些容易下手资源的人,他们不专门针对某种特定信息或者目标特定公司,他们的目标是尽可能的用最简单的方法获得ROOT,他们 通过搜集一些公开的exploit信息并搜索整个Internet来找寻有这种exploit漏洞的资源,这样,不管怎样,总有某些人会被他们操作。
其中一些高级点的家伙会开发他们自己的工具,并留下一些复杂的后门,另外一些根本就不知道他们做什么,就知道怎样在命令行打"go"的人。忽略他们的技术水平不说,Script kiddie就是共享一些公共策略,随机搜索某个特殊漏洞并利用这个特殊漏洞的人。
他们形成的威胁在哪里?
由于Script kiddie是随机选择目标,所以存在的威胁是你的系统迟早会被扫描到,我知道管理员很惊讶他们的系统在设置以后没几天也没有告诉任何人的时候就被扫描到了,其实这一点也不值得惊讶,因为Scritp kiddie一般是扫描一段网络来操作的。
如果扫描只能限制在几个独立的资源,你可能会很安心,因为Internet上千千万万的机器,扫描到你的机器的几率少之又少。但是,事实不是你想象的这 样,目前多数工具能很方面的使用大范围扫描并广泛传播,任何人可以使用他们,使用这些工具的人数增长率呈现惊人的速率。Internet是一个无国界的区 域,这种威胁就很快转播到世界各个地方,有这么多人使用这些工具,你被探测就不是问题了。
试图以含糊其词来搪塞你的安全问题会害了你:你或许会认为没有人知道你的系统,你就会安全,或者你认为你的系统没有价值,他们为何要探测你,其实这些系统正是scritp kiddies搜寻的目标--没有任何保护的系统,非常容易得手的系统。
具体方法讨论
Scritp kiddie的方法很简单,扫描Internet有特定缺陷的系统,一但查找到,便对它下手,他们用的许多工具会自动操作,不需要很多的交互。你只要打开 工具,然后过几天回来看看你的结果就可以了。没有两个工具是相同的就象没有两个漏洞是一样的,但是虽然如此,许多工具的策略是一样的,第一,开发要扫描的 IP段,然后扫描这些IP段中特定的漏洞。
例如:我们假定一个用户有一个工具可以利用Linux系统上的imap漏洞,如imapd_exploit.c,开始,他们开发一IP数据库来扫描,一旦 IP数据库构建好,用户会想判断系统是否运行LINUX系统。目前许多扫描器可以通过发送不正常的信息包到目标系统并查看他们如果响应便可很方便的判断操 作系统,如Fyodor的nmap,然后,工具会判断LINUX系统是否运行着imap服务,最后就是利用imapd_exploit.c程序来进入系统 了。
你会想所以这些扫描会有很大的动静,很容易引起注意,但是,很多人没有很好的监视他们的系统,并不认识到他们正被扫描,而且,许多script kiddies在查看他们所要利用的系统时也会保持相当的安静,一旦他们利用这个漏洞进入系统,他们就会使用这个系统作为跳板,并不带任何包袱的扫描整个 系统,因为如果这种扫描被抓获,责任是系统管理员而不是那些script-kiddie.
所有这些扫描的结果经常被用来归档或者在其他用户中共享,以便在以后的日子里使用,如用户在最初为了某个漏洞扫描出来的LINUX系统开了那些端口的数据 库后,过一点时间,一个新的漏洞被发现以后,用户可以不用重新构建或者扫描新的IP段,他可以很方便的来查看以前归档的数据库并来利用这个新发现的漏洞。 其他变相的,用户可以交流或者买卖有漏洞系统的数据库。你可以看Know Your Enemy: Motives文章中的例子,这样造成scritp kiddie可以不扫描系统而破坏你的资源。
有些Black-hats会采用木马或者后门来种植在破坏的系统中,后门允许方便的随时的让攻击者来访问你的系统,而木马使入侵者难于被发现,这些技术可以让他们的操作不显示在任何LOG记录,系统进程或者文件结构上,他可以构建一个舒适安全的环境来扫描Internet,跟详细的信息请看:Know Your Enemy: III。
这些攻击没有限制在一天中的任何时间,许多管理员搜索他们的LOG记录来查询当晚发生了什么,并相信这是攻击者的攻击时间,其实script kiddies在任意时间进行攻击,他们一天24小时的进行扫描,你根本不能考虑到你什么时候会被探测到。而且由于Internet的无边界性,时间也就 不确定了,攻击者当地在午夜在攻击,而你这里可能是在当地时间下午一点种。以上对系统漏洞的扫描可以用于多种用途,近来,一种新的拒绝服务攻击--分布式 拒绝服务攻击DDos,就是攻击者一个人控制了很多台有漏洞的系统,他可以遥控这些控制的系统来共同对目标系统执行拒绝服务攻击。由于多个系统被使用,所 以防卫和判断源攻击地也变的非常困难。要控制多个系统,Script kiddie的策略就变的很有用,有漏洞的系统随机被判断并用来作为DDOS的垫板,越多的系统被控制,DDOS攻击的强度就越大。如 stacheldraht,要了解关于更多的分布式拒绝服务攻击和怎样保护自己,请查看Paul Ferguson站上的Denialinfo。
工具
这些工具一般使用起来很见大,许多工具一般只是几个选项来完成单个目标,开始工具用来构建IP数据库,这些工具很随机的扫描Internet,如一个工具 有一个单一的选项,A,B和C,你可以选择一个字母来决定要扫描的网络大小,这工具然后就选择A,B,C相应的IP网络进行扫描。另一个工具使用域名如 z0ne,这个工具通过对域名和子域名的区域传输操作来构造IP数据库,用户通过扫描整个.com或者.edu域来获得2百万或者更多的IP数据库,一旦 发现这些IP,它们就被会被工具用户判断版本名字,操作系统,所运行的服务,如果发现系统有漏洞,black-hat就会马上进行攻击。要更好的理解这些 工具,请看Know Your Enemy: Forensics。
怎样防止这类威胁
下面的一些步骤你可以比较好的保护你的系统,第一,script kiddie一般找寻容易下手的对象,如一些很公开很容易得手的漏洞系统,保证你的系统和网络不受这些漏洞的影响,www.cert.org 和www.ciac.org是了解这些漏洞很好的资料库。同样地,bugtraq (securityfocus.com的一个邮件列表 ) 也是获得这些漏洞信息很好的地方。另一个保护你自己的方法是只运行你需要的服务,如果你不需要某个服务,关掉它,如果你确实要使用某个服务,确保你的服务 版本是最新的。要怎样操作,请看这些文章:Armoring Solaris , Armoring Linux 或者Armoring NT.
上面知道,DNS服务器是 经常被用来找寻IP数据库的对象之一,你必须在你的名字服务器上限制区域传送的操作,记录任何未认证的区域传输并跟踪他们。我强烈建议升级BIND到最新 的版本,你可以在下面的地址找到:www.isc.org/bind.HTML. 最后监视你被探测的系统,你可以跟踪这些探测操作获得更多对你网络有威胁的举动。
总结
Scritp kiddie会对所有系统有威胁,他们没有任何偏爱,任何系统他们都挑,不计较地点和价值。不管怎样,你迟早会被扫描到。通过了解他们的动机和方法,你可以很好的对付这些威胁而更好的保护你的系统。
Know Your Enemy: II
跟踪Blackhat的举动
Honeynet Project
http://project.honeynet.org
http://www.xfocus.org
Last ModifIEd: July 7, 2000
此文章是系列中的第二篇文章,在第一篇Know Your Enemy, 我们讲述了Script kiddie相关的工具和方法,特别是他们是怎样探测漏洞然后攻击的。在第三篇Know YourEnemy III中我们将会描述Script kiddie在获得ROOT的时候将会做的事情,特别是他们是怎样覆盖踪迹和他们下一步做的是什么。当前这文章,将涉及到有关则怎样跟踪他们的行为。我们 会讲述到通过你的系统记录来判断你所需的操作,和你被扫描了以后,你需要了解被探测了以后主要被干什么用,他们使用了那些工具;这里的有些例子主要是基于 Linux操作系统,但是很容易移植到其他Unix系统中,记住,没有绝对的方法能跟踪敌人的每一步,但是,这文章会是一个好的开始。
加强系统LOG记录的安全性
这文章没有主要不是针对入侵检测进行讨论,关于IDS,internet上有很多优秀的源程序供你选择,如果你对入侵检测感兴趣,我建议你使用如 Network Flight Recorder 和snort程序来尝试。此文主要集中在智力收集信息上,特别是,怎样通过查看你的系统记录来获取攻击者操作信息,可能你对你能在自己的LOG记录上能发 现多少信息感到很惊讶,但是,在我们讲述查看你记录前,我们首先必须讨论下加强你的系统LOG安全性,如果你不能信任你系统记录的完整性那这些记录将会一 文不值,多数black-aht在进入系统之后第一件事情就是怎样更改记录文件,网上非常多类型的Rootkit工具可以清楚记录文件中他们的留下的踪迹 (如cloak),或者阻止所有系统的记录(如伪造过的syslogd),因此,要想查看系统记录,你必须保护好你的记录文件。
这意味着你需要使用远程的LOG服务器,先不管你有多少能力保护自己的系统,在一台被入侵的系统中你不能相信你的任何记录,即使你最好的保护被入侵系统的LOG记录,black-hat也可以简单的使用rm -fr /*来完全清理你的硬盘。 要保护这些文件,你必须使你所有系统的LOG记录既有本地记录也发向远程LOG服务器中,这里建立你一个只记录LOG的服务器来收集其他服务器上的信息, 如果牵涉到钱的问题,你可以简单使用Linux服务器来充当你的LOG服务器,不过这台服务器必须保证非常安全,需要所有服务关闭,只允许控制台访问(如 Armoring Linux所描述),还有必须保证UDP 514口没有对外连接,这样可以保护你的LOG服务器不接受从外界来的不好的或者未认证的LOG信息。
由于上述原因,这里建议你重编译syslogd 程序,并让syslogd读取不同的配置文件,如/var/tmp/.conf,此方法能让black- hat没有注意到真实的配置文件位置,这项操作你可以简单的在源代码中修改"/etc/syslog.conf"条目,接着我们可以设置我们新的配置文件 把信息记录到本地和远程服务器, 如syslog.txt。这里请你维持一标准的配置文件/etc/syslog.conf指向所有本地LOG,虽然这份配置文件没有用,但可以让攻击者相 信记录没有发忘远程记录。另一个选择方法就是让你的系统使用更安全的日志记录工具,如使用某些有完整性检查和其他方面安全加强的系统日志记录工具,如 syslog-ng。
把记录都记录到远程服务器中,将想上面提到的,我们可以基本上相信这些LOG的完整性,而且由于所有系统都记录在单一资源中,就比较容易的判断这些LOG的样式。我们可以在一台机器上记录所有系统记录,你所做的是对比下本地系统和远程系统的不一致性。
类型匹配
通过检查你的记录条目,你可以用来判断那些端口被扫描,许多Script kidde扫描整个网络只为一个漏洞,如你的记录显示你多数系统有来自同一远程系统的连接和同一端口,这就很可能以为着是一次漏洞的扫描,多数LINUX 系统中,TCP Wrapper默认安装的,所以你可以在/var/log/secure里找到多数连接,在其他UNIX系统中,我们可以通过启动inetd后增加-t标志就可以记录所有Inetd连接。下面是一个典型的漏洞扫描,是为了扫描wu-ftpd漏洞:
/var/log/secure
Apr 10 13:43:48 mozart in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:51 bach in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:54 hadyen in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:57 vivaldi in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:58 brahms in.ftpd[6613]: connect from 192.168.11.200
上面我们可以看到源主机192.168.11.200在扫描我们的网络,注意为何源主机连续扫描每个IP,这些记录就归功于LOG服务器,你可以方便的判 断每个类型,连续的连接端口21,FTP,就暗示着攻击者在寻找wu-ftpd漏洞。一般来说,扫描是倾向于阶段性的,某些人发布了一个 imap漏洞的利用代码,你就会发现记录里有imap扫描突然增多,下一个月如果有FTP利用程序发布,记录就会转向ftp突然增多,你可以在这个地址 http://www.cert.org/advisories/获得当前最新的漏洞建议。有时,一个工具也会在同一时间里扫描多种漏洞,因此你也会看到 一个源主机连接多个端口。
记住,如果你没有记录这些服务,你就不会知道你被扫描,例如,多数RPC连接没有被记录,但是服务记录是一件简单的事情,你可以通过在 /etc/inetd.conf增加条目来让TCP WRAPPER进行记录,如你在/etc/inetd.conf里增加NetBus条目,你就可以通过定义TCP Wrapper来安全的拒绝和记录NETBUS的连接(更多信息请查看ids).
判断使用工具
有些时候你可以判断什么样的工具在扫描你的系统,因为一般工具都是扫描特殊的漏洞,如ftp-scan.c,如果你发现只是一个端口被扫描,一般他们使用 的是单任务工具,但是也存在很多工具扫描多种系统漏洞和薄弱处,举两个非常有用的工具如jsbach写的sscan和Fyodor写的nmap,我只所以 选择了这两个工具是因为他们能代表两种类别的扫描工具,这里强烈建议你用这些工具扫描下你自己的网络,或许你会得到让你吃惊的结果。
注:sscan工具是一个比较老的工具了,在这里只是把sscan拿来讨论,要扫描你网络系统的漏洞,这里建议使用Nessus。
sscan代表着以"所有目标"为目的的Script kiddie扫描工具,它扫描网络一套的网络漏洞,它可以让你定制规则来对新漏洞的增加,你只要传递工具一个网络和网络掩码,其他的事情它来做,不过这个 工具需要有ROOT权利才能使用,它的输出很容易理解,它会提供一个简洁的对漏洞服务的描述,所有你要做的就是让sscan扫描网络,然后你提取 "VULN"的值,然后运行"exploit du jour",下面是sscan对系统mozart (172.17.6.30)的扫描:
otto #./sscan -o 172.17.6.30
--------------------------
用文件那样向设备传送数据。可是为什么用户使用write函数就可以把数据写到设备里面
去,这个过程到底是怎么实现的呢?
个简简单单的write函数能够完成向设备里面写数据的复杂过程。
实例,第二是Linux Kernel 2.2.14核心源代码。我只列出了其中相关部分的内容,如果
读者有兴趣,也可以查阅其它源代码。不过我不是在讲解如何编写设备驱动程序,所以不
会对每一个细节都进行说明,再说有些地方我觉得自己还没有吃透。
由于《Linux device driver》一书中的例子对于我们还是复杂了一些,我将其中的一个
例程简化了一下。这个驱动程序支持这样一个设备:核心空间中的一个长度为10的数组
kbuf[10]。我们可以通过用户程序open它,read它,write它,close它。这个设备的名
字我称为short_t。
现在言归正传。
对于一个设备,它可以在/dev下面存在一个对应的逻辑设备节点,这个节点以文件的形式
存在,但它不是普通意义上的文件,它是设备文件,更确切的说,它是设备节点。这个节
点是通过mknod命令建立的,其中指定了主设备号和次设备号。主设备号表明了某一类设
备,一般对应着确定的驱动程序;次设备号一般是区分是标明不同属性,例如不同的使用
方法,不同的位置,不同的操作。这个设备号是从/proc/devices文件中获得的,所以一
般是先有驱动程序在内核中,才有设备节点在目录中。这个设备号(特指主设备号)的主
要作用,就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的,当你打开一
个设备文件时,操作系统就已经知道这个设备所对应的驱动程序是哪一个了。这个"知道"
的过程后面就讲。
我们再说说驱动程序的基本结构吧。这里我只介绍动态模块型驱动程序(就是我们使用
insmod加载到核心中并使用rmmod卸载的那种),因为我只熟悉这种结构。
模块化的驱动程序由两个函数是固定的:int init_module(void) ;void
cleanup_module(void)。前者在insmod的时候执行,后者在rmmod的时候执行。
init_nodule在执行的时候,进行一些驱动程序初始化的工作,其中最主要的工作有三
件:注册设备;申请I/O端口地址范围;申请中断IRQ。这里和我们想知道的事情相关的只
有注册设备。
下面是一个典型的init_module函数:
int init_module(void){
int result = check_region(short_base,1);
……
request_region(short_base,1,"short");
……
result = register_chrdev(short_major, "short", &short_fops);
……
result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short",
NULL);
……
return 0;
}
上面这个函数我只保留了最重要的部分,其中最重要的函数是
result = register_chrdev(short_major, "short", &short_fops);
这是一个驱动程序的精髓所在!!当你执行indmod命令时,这个函数可以完成三件大事:
第一,申请主设备号(short_major),或者指定,或者动态分配;第二,在内核中注册设
备的名字("short");第三,指定fops方法(&short_fops)。其中所指定的fops方法就是
我们对设备进行操作的方法(例如read,write,seek,dir,open,release等),如何实现
这些方法,是编写设备驱动程序大部分工作量所在。
现在我们就要接触关键部分了--如何实现fops方法。
我们都知道,每一个文件都有一个file的结构,在这个结构中有一个file_operations的
结构体,这个结构体指明了能够对该文件进行的操作。
下面是一个典型的file_operations结构:
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned
long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *);
int (*fasync) (int, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};
我们可以看到它实际上就是许多文件操作的函数指针,其中就有write,其它的我们就不
去管它了。这个write指针在实际的驱动程序中会以程序员所实现的函数名字出现,它指
向程序员实现的设备write操作函数。下面就是一个实际的例子,这个write函数可以向核
心内存的一个数组里输入一个字符串。
int short_write (struct inode *inode, struct file *filp, const char *buf,
int count){
int retval = count;
extern unsigned char kbuf[10];
if(count>10)
count=10;
copy_from_user(kbuf, buf, count);
return retval;
}
设备short_t对应的fops方法是这样声明的:
struct file_operations short_fops = {
NULL,
short_read,
short_write,
NULL,
NULL,
NULL,
NULL,
short_open,
short_release,
NULL,
NULL,
};
其中NULL的项目就是不提供这个功能。所以我们可以看出short_t设备只提供了
read,write,open,release功能。其中write功能我们在上面已经实现了,具体的实现函
数起名为short_write。这些函数就是真正对设备进行操作的函数,这就是驱动程序的一
大好处:不管你实现的时候是多么的复杂,但对用户来看,就是那些常用的文件操作函数。
但是我们可以看到,驱动程序里的write函数有四个参数,函数格式如下:
short_write (struct inode *inode, struct file *filp, const char *buf, int count)
而用户程序中的write函数只有三个参数,函数格式如下:
write(inf fd, char *buf, int count)
那他们两个是怎么联系在一起的呢?这就要靠操作系统核心中的函数sys_write了,下面
是Linux Kernel 2.2.14中sys_write中的源代码:
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
{
ssize_t ret;
struct file * file;
struct inode * inode;
ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
lock_kernel();
ret = -EBADF;
file = fget(fd);
if (!file)
goto bad_file;
if (!(file->f_mode & FMODE_WRITE))
goto out;
inode = file->f_dentry->d_inode;
ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos,
count);
if (ret)
goto out;
ret = -EINVAL;
if (!file->f_op || !(write = file->f_op->write))
goto out;
down(&inode->i_sem);
ret = write(file, buf, count, &file->f_pos);
up(&inode->i_sem);
out:
fput(file);
bad_file:
unlock_kernel();
return ret;
}
我写了一个简单的程序来测试这个驱动程序,该程序源代码节选如下(该省的我都省了):
main(){
int fd,count=0;
unsigned char buf[10];
fd=open("/dev/short_t",O_RDWR);
printf("input string:");
scanf("%s",buf);
count=strlen(buf);
if(count>10)
count=10;
count=write(fd,buf,count);
close(fd);
return 1;
}
现在我们就演示一下用户使用write函数将数据写到设备里面这个过程到底是怎么实现的:
1,insmod驱动程序。驱动程序申请设备名和主设备号,这些可以在/proc/devIEces中获得。
2,从/proc/devices中获得主设备号,并使用mknod命令建立设备节点文件。这是通过主
设备号将设备节点文件和设备驱动程序联系在一起。设备节点文件中的file属性中指明了
驱动程序中fops方法实现的函数指针。
3,用户程序使用open打开设备节点文件,这时操作系统内核知道该驱动程序工作了,就
调用fops方法中的open函数进行相应的工作。open方法一般返回的是文件标示符,实际
上并不是直接对它进行操作的,而是有操作系统的系统调用在背后工作。
4,当用户使用write函数操作设备文件时,操作系统调用sys_write函数,该函数首先通
过文件标示符得到设备节点文件对应的inode指针和flip指针。inode指针中有设备号信
息,能够告诉操作系统应该使用哪一个设备驱动程序,flip指针中有fops信息,可以告诉
操作系统相应的fops方法函数在那里可以找到。
5,然后这时sys_write才会调用驱动程序中的write方法来对设备进行写的操作。
其中1-3都是在用户空间进行的,4-5是在核心空间进行的。用户的write函数和操作系统
的write函数通过系统调用sys_write联系在了一起。
注意:
对于块设备来说,还存在写的模式的问题,这应该是由GNU C库来解决的,这里不予讨
论,因为我没有看过GNU C库的源代码。
另外,这是一个测试版的文章,请各位朋友们多提意见和建议,非常感谢!
Know Your Enemy
Script KiddIE使用的工具和方法论
Honeynet Project
http://project.honeynet.org
http://www.xfocus.org
Last Modified: 21 July, 2000
我的长官经常对我说:在敌人面前好很好的保护自己,你必须先了解敌人先。(孙子兵法:知己知彼,百战百胜)。这句军事用语现在也很好的应用于网络安 全领域中了,就象作战一样,你要保护自己的资源,你需要知道谁是你最大的威胁和他们会怎样攻击。在这个系列中的第一篇文章,就是讨论一种很流行很广泛的来 自 Scritp Kiddie所使用的工具和方法,如果你或者你的组织有任何资源连接到Internet上,你就有此类的威胁。
Know Your Enemy文章主要是涉及到blackhatck团体使用的工具,策略和动机。而Know Your Enemy:II主要集中于你怎样能探测这些威胁,判断他们所使用的工具和他们在你系统上寻找什么样的漏洞。Know Your Enemy:III集中讨论攻击者获得ROOT后在系统上怎样操作,特别是他们是如何掩盖他们的踪迹和他们下一步主要干什么。Know Your Enemy: Forensics 涉及了你怎样分析一种攻击。Know Your Enemy: Motives 通过捕获black-hat团体之间的通信和联系来分析他们的动机和心理状态。Know Your Enemy: Worms at War 描述了WORM蠕虫是怎样自动攻击WINDOW系统的。
什么叫Script Kiddie
Script kiddie是一些专门找寻一些容易下手资源的人,他们不专门针对某种特定信息或者目标特定公司,他们的目标是尽可能的用最简单的方法获得ROOT,他们 通过搜集一些公开的exploit信息并搜索整个Internet来找寻有这种exploit漏洞的资源,这样,不管怎样,总有某些人会被他们操作。
其中一些高级点的家伙会开发他们自己的工具,并留下一些复杂的后门,另外一些根本就不知道他们做什么,就知道怎样在命令行打"go"的人。忽略他们的技术水平不说,Script kiddie就是共享一些公共策略,随机搜索某个特殊漏洞并利用这个特殊漏洞的人。
他们形成的威胁在哪里?
由于Script kiddie是随机选择目标,所以存在的威胁是你的系统迟早会被扫描到,我知道管理员很惊讶他们的系统在设置以后没几天也没有告诉任何人的时候就被扫描到了,其实这一点也不值得惊讶,因为Scritp kiddie一般是扫描一段网络来操作的。
如果扫描只能限制在几个独立的资源,你可能会很安心,因为Internet上千千万万的机器,扫描到你的机器的几率少之又少。但是,事实不是你想象的这 样,目前多数工具能很方面的使用大范围扫描并广泛传播,任何人可以使用他们,使用这些工具的人数增长率呈现惊人的速率。Internet是一个无国界的区 域,这种威胁就很快转播到世界各个地方,有这么多人使用这些工具,你被探测就不是问题了。
试图以含糊其词来搪塞你的安全问题会害了你:你或许会认为没有人知道你的系统,你就会安全,或者你认为你的系统没有价值,他们为何要探测你,其实这些系统正是scritp kiddies搜寻的目标--没有任何保护的系统,非常容易得手的系统。
具体方法讨论
Scritp kiddie的方法很简单,扫描Internet有特定缺陷的系统,一但查找到,便对它下手,他们用的许多工具会自动操作,不需要很多的交互。你只要打开 工具,然后过几天回来看看你的结果就可以了。没有两个工具是相同的就象没有两个漏洞是一样的,但是虽然如此,许多工具的策略是一样的,第一,开发要扫描的 IP段,然后扫描这些IP段中特定的漏洞。
例如:我们假定一个用户有一个工具可以利用Linux系统上的imap漏洞,如imapd_exploit.c,开始,他们开发一IP数据库来扫描,一旦 IP数据库构建好,用户会想判断系统是否运行LINUX系统。目前许多扫描器可以通过发送不正常的信息包到目标系统并查看他们如果响应便可很方便的判断操 作系统,如Fyodor的nmap,然后,工具会判断LINUX系统是否运行着imap服务,最后就是利用imapd_exploit.c程序来进入系统 了。
你会想所以这些扫描会有很大的动静,很容易引起注意,但是,很多人没有很好的监视他们的系统,并不认识到他们正被扫描,而且,许多script kiddies在查看他们所要利用的系统时也会保持相当的安静,一旦他们利用这个漏洞进入系统,他们就会使用这个系统作为跳板,并不带任何包袱的扫描整个 系统,因为如果这种扫描被抓获,责任是系统管理员而不是那些script-kiddie.
所有这些扫描的结果经常被用来归档或者在其他用户中共享,以便在以后的日子里使用,如用户在最初为了某个漏洞扫描出来的LINUX系统开了那些端口的数据 库后,过一点时间,一个新的漏洞被发现以后,用户可以不用重新构建或者扫描新的IP段,他可以很方便的来查看以前归档的数据库并来利用这个新发现的漏洞。 其他变相的,用户可以交流或者买卖有漏洞系统的数据库。你可以看Know Your Enemy: Motives文章中的例子,这样造成scritp kiddie可以不扫描系统而破坏你的资源。
有些Black-hats会采用木马或者后门来种植在破坏的系统中,后门允许方便的随时的让攻击者来访问你的系统,而木马使入侵者难于被发现,这些技术可以让他们的操作不显示在任何LOG记录,系统进程或者文件结构上,他可以构建一个舒适安全的环境来扫描Internet,跟详细的信息请看:Know Your Enemy: III。
这些攻击没有限制在一天中的任何时间,许多管理员搜索他们的LOG记录来查询当晚发生了什么,并相信这是攻击者的攻击时间,其实script kiddies在任意时间进行攻击,他们一天24小时的进行扫描,你根本不能考虑到你什么时候会被探测到。而且由于Internet的无边界性,时间也就 不确定了,攻击者当地在午夜在攻击,而你这里可能是在当地时间下午一点种。以上对系统漏洞的扫描可以用于多种用途,近来,一种新的拒绝服务攻击--分布式 拒绝服务攻击DDos,就是攻击者一个人控制了很多台有漏洞的系统,他可以遥控这些控制的系统来共同对目标系统执行拒绝服务攻击。由于多个系统被使用,所 以防卫和判断源攻击地也变的非常困难。要控制多个系统,Script kiddie的策略就变的很有用,有漏洞的系统随机被判断并用来作为DDOS的垫板,越多的系统被控制,DDOS攻击的强度就越大。如 stacheldraht,要了解关于更多的分布式拒绝服务攻击和怎样保护自己,请查看Paul Ferguson站上的Denialinfo。
工具
这些工具一般使用起来很见大,许多工具一般只是几个选项来完成单个目标,开始工具用来构建IP数据库,这些工具很随机的扫描Internet,如一个工具 有一个单一的选项,A,B和C,你可以选择一个字母来决定要扫描的网络大小,这工具然后就选择A,B,C相应的IP网络进行扫描。另一个工具使用域名如 z0ne,这个工具通过对域名和子域名的区域传输操作来构造IP数据库,用户通过扫描整个.com或者.edu域来获得2百万或者更多的IP数据库,一旦 发现这些IP,它们就被会被工具用户判断版本名字,操作系统,所运行的服务,如果发现系统有漏洞,black-hat就会马上进行攻击。要更好的理解这些 工具,请看Know Your Enemy: Forensics。
怎样防止这类威胁
下面的一些步骤你可以比较好的保护你的系统,第一,script kiddie一般找寻容易下手的对象,如一些很公开很容易得手的漏洞系统,保证你的系统和网络不受这些漏洞的影响,www.cert.org 和www.ciac.org是了解这些漏洞很好的资料库。同样地,bugtraq (securityfocus.com的一个邮件列表 ) 也是获得这些漏洞信息很好的地方。另一个保护你自己的方法是只运行你需要的服务,如果你不需要某个服务,关掉它,如果你确实要使用某个服务,确保你的服务 版本是最新的。要怎样操作,请看这些文章:Armoring Solaris , Armoring Linux 或者Armoring NT.
上面知道,DNS服务器是 经常被用来找寻IP数据库的对象之一,你必须在你的名字服务器上限制区域传送的操作,记录任何未认证的区域传输并跟踪他们。我强烈建议升级BIND到最新 的版本,你可以在下面的地址找到:www.isc.org/bind.HTML. 最后监视你被探测的系统,你可以跟踪这些探测操作获得更多对你网络有威胁的举动。
总结
Scritp kiddie会对所有系统有威胁,他们没有任何偏爱,任何系统他们都挑,不计较地点和价值。不管怎样,你迟早会被扫描到。通过了解他们的动机和方法,你可以很好的对付这些威胁而更好的保护你的系统。
Know Your Enemy: II
跟踪Blackhat的举动
Honeynet Project
http://project.honeynet.org
http://www.xfocus.org
Last ModifIEd: July 7, 2000
此文章是系列中的第二篇文章,在第一篇Know Your Enemy, 我们讲述了Script kiddie相关的工具和方法,特别是他们是怎样探测漏洞然后攻击的。在第三篇Know YourEnemy III中我们将会描述Script kiddie在获得ROOT的时候将会做的事情,特别是他们是怎样覆盖踪迹和他们下一步做的是什么。当前这文章,将涉及到有关则怎样跟踪他们的行为。我们 会讲述到通过你的系统记录来判断你所需的操作,和你被扫描了以后,你需要了解被探测了以后主要被干什么用,他们使用了那些工具;这里的有些例子主要是基于 Linux操作系统,但是很容易移植到其他Unix系统中,记住,没有绝对的方法能跟踪敌人的每一步,但是,这文章会是一个好的开始。
加强系统LOG记录的安全性
这文章没有主要不是针对入侵检测进行讨论,关于IDS,internet上有很多优秀的源程序供你选择,如果你对入侵检测感兴趣,我建议你使用如 Network Flight Recorder 和snort程序来尝试。此文主要集中在智力收集信息上,特别是,怎样通过查看你的系统记录来获取攻击者操作信息,可能你对你能在自己的LOG记录上能发 现多少信息感到很惊讶,但是,在我们讲述查看你记录前,我们首先必须讨论下加强你的系统LOG安全性,如果你不能信任你系统记录的完整性那这些记录将会一 文不值,多数black-aht在进入系统之后第一件事情就是怎样更改记录文件,网上非常多类型的Rootkit工具可以清楚记录文件中他们的留下的踪迹 (如cloak),或者阻止所有系统的记录(如伪造过的syslogd),因此,要想查看系统记录,你必须保护好你的记录文件。
这意味着你需要使用远程的LOG服务器,先不管你有多少能力保护自己的系统,在一台被入侵的系统中你不能相信你的任何记录,即使你最好的保护被入侵系统的LOG记录,black-hat也可以简单的使用rm -fr /*来完全清理你的硬盘。 要保护这些文件,你必须使你所有系统的LOG记录既有本地记录也发向远程LOG服务器中,这里建立你一个只记录LOG的服务器来收集其他服务器上的信息, 如果牵涉到钱的问题,你可以简单使用Linux服务器来充当你的LOG服务器,不过这台服务器必须保证非常安全,需要所有服务关闭,只允许控制台访问(如 Armoring Linux所描述),还有必须保证UDP 514口没有对外连接,这样可以保护你的LOG服务器不接受从外界来的不好的或者未认证的LOG信息。
由于上述原因,这里建议你重编译syslogd 程序,并让syslogd读取不同的配置文件,如/var/tmp/.conf,此方法能让black- hat没有注意到真实的配置文件位置,这项操作你可以简单的在源代码中修改"/etc/syslog.conf"条目,接着我们可以设置我们新的配置文件 把信息记录到本地和远程服务器, 如syslog.txt。这里请你维持一标准的配置文件/etc/syslog.conf指向所有本地LOG,虽然这份配置文件没有用,但可以让攻击者相 信记录没有发忘远程记录。另一个选择方法就是让你的系统使用更安全的日志记录工具,如使用某些有完整性检查和其他方面安全加强的系统日志记录工具,如 syslog-ng。
把记录都记录到远程服务器中,将想上面提到的,我们可以基本上相信这些LOG的完整性,而且由于所有系统都记录在单一资源中,就比较容易的判断这些LOG的样式。我们可以在一台机器上记录所有系统记录,你所做的是对比下本地系统和远程系统的不一致性。
类型匹配
通过检查你的记录条目,你可以用来判断那些端口被扫描,许多Script kidde扫描整个网络只为一个漏洞,如你的记录显示你多数系统有来自同一远程系统的连接和同一端口,这就很可能以为着是一次漏洞的扫描,多数LINUX 系统中,TCP Wrapper默认安装的,所以你可以在/var/log/secure里找到多数连接,在其他UNIX系统中,我们可以通过启动inetd后增加-t标志就可以记录所有Inetd连接。下面是一个典型的漏洞扫描,是为了扫描wu-ftpd漏洞:
/var/log/secure
Apr 10 13:43:48 mozart in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:51 bach in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:54 hadyen in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:57 vivaldi in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:58 brahms in.ftpd[6613]: connect from 192.168.11.200
上面我们可以看到源主机192.168.11.200在扫描我们的网络,注意为何源主机连续扫描每个IP,这些记录就归功于LOG服务器,你可以方便的判 断每个类型,连续的连接端口21,FTP,就暗示着攻击者在寻找wu-ftpd漏洞。一般来说,扫描是倾向于阶段性的,某些人发布了一个 imap漏洞的利用代码,你就会发现记录里有imap扫描突然增多,下一个月如果有FTP利用程序发布,记录就会转向ftp突然增多,你可以在这个地址 http://www.cert.org/advisories/获得当前最新的漏洞建议。有时,一个工具也会在同一时间里扫描多种漏洞,因此你也会看到 一个源主机连接多个端口。
记住,如果你没有记录这些服务,你就不会知道你被扫描,例如,多数RPC连接没有被记录,但是服务记录是一件简单的事情,你可以通过在 /etc/inetd.conf增加条目来让TCP WRAPPER进行记录,如你在/etc/inetd.conf里增加NetBus条目,你就可以通过定义TCP Wrapper来安全的拒绝和记录NETBUS的连接(更多信息请查看ids).
判断使用工具
有些时候你可以判断什么样的工具在扫描你的系统,因为一般工具都是扫描特殊的漏洞,如ftp-scan.c,如果你发现只是一个端口被扫描,一般他们使用 的是单任务工具,但是也存在很多工具扫描多种系统漏洞和薄弱处,举两个非常有用的工具如jsbach写的sscan和Fyodor写的nmap,我只所以 选择了这两个工具是因为他们能代表两种类别的扫描工具,这里强烈建议你用这些工具扫描下你自己的网络,或许你会得到让你吃惊的结果。
注:sscan工具是一个比较老的工具了,在这里只是把sscan拿来讨论,要扫描你网络系统的漏洞,这里建议使用Nessus。
sscan代表着以"所有目标"为目的的Script kiddie扫描工具,它扫描网络一套的网络漏洞,它可以让你定制规则来对新漏洞的增加,你只要传递工具一个网络和网络掩码,其他的事情它来做,不过这个 工具需要有ROOT权利才能使用,它的输出很容易理解,它会提供一个简洁的对漏洞服务的描述,所有你要做的就是让sscan扫描网络,然后你提取 "VULN"的值,然后运行"exploit du jour",下面是sscan对系统mozart (172.17.6.30)的扫描:
otto #./sscan -o 172.17.6.30
--------------------------