sp; if (skb->nh.iph->saddr == *(unsigned int *)deny_ip) { return NF_DROP; }
return NF_ACCEPT; } 这样,如果数据包的源地址与我们设定的丢弃数据包的地址匹配,那么该数据包将被丢弃。为了使这个函数能按预期的方式工作,deny_ip的值应当以网络字节序(Big-endian,与Intel相反)存放。虽然这个函数不太可能以一个空的指针作为参数来调用,带一点点偏执狂从来不会有什么坏处。当然,如果错误确实发生了,那么该函数将会返回NF_ACCEPT。这样Netfilter可以继续处理这个数据包。示例代码4展现了用于演示将基于接口的过滤略做修改以丢弃匹配给定IP地址的数据包的简单模块。 示例代码4 : 基于数据包源地址的过滤 /* 安装丢弃所有来自指定IP地址的数据包的Netfilter hook的示例代码 */
#define __KERNEL__ #define MODULE
#include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/ip.h> /* For IP header */ #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h>
/* 用于注册我们的函数的数据结构 */ static struct nf_hook_ops nfho;
/* 我们要丢弃的数据包来自的地址,网络字节序 */ static unsigned char *drop_ip = "\x7f\x00\x00\x01";
/* 注册的hook函数的实现 */ unsigned int hook_func(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *sb = *skb; // 译注:作者提供的代码中比较地址是否相同的方法是错误的,见注释掉的部分 if (sb->nh.iph->saddr == *(unsigned int *)drop_ip) { // if (sb->nh.iph->saddr == drop_ip) { printk("Dropped packet from... %d.%d.%d.%d\n", *drop_ip, *(drop_ip + 1), *(drop_ip + 2), *(drop_ip + 3)); return NF_DROP; } else { return NF_ACCEPT; } }
/* 初始化程序 */ int init_module() { /* 填充我们的hook数据结构 */ nfho.hook = hook_func; /* 处理函数 */ nfho.hooknum = NF_IP_PRE_ROUTING; /* 使用IPv4的第一个hook */ nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; /* 让我们的函数首先执行 */
nf_register_hook(&nfho);
return 0; }
/* 清除程序 */ void cleanup_module() { nf_unregister_hook(&nfho); }
----[ 4.4 - 基于TCP端口进行过滤
另一个要实现的简单规则是基于数据包的TCP目的端口进行过滤。这只比检查IP地址的要求要高一点点,因为我们需要自己创建一个TCP头的指针。还记得我们前面讨论的关于传输层包头与网络层包头的内容吗?获取一个TCP头的指针是一件简单的事情——分配一个tcphdr数据结构(在linux/tcp.h中定义)的指针,并将它指向我们的数据包中IP头之后的数据。或许一个例子的帮助会更大一些,示例代码5给出了检查数据包的TCP目的端口是否与某个我们要丢弃数据包的端口匹配的代码。与示例代码3一样,这些代码摘自LWFW。 示例代码5 : 检查收到的数据包的TCP目的端口 unsigned char *deny_port = "\x00\x19"; /* port 25 */
...
static int check_tcp_packet(struct sk_buff *skb) { struct tcphdr *thead;
/* We don''''t want any NULL pointers in the chain * to the IP header. */ if (!skb ) return NF_ACCEPT; if (!(skb->nh.iph)) return NF_ACCEPT;
/* Be sure this is a TCP packet first */ if (skb->nh.iph->protocol != IPPROTO_TCP) { return NF_ACCEPT; }
thead = (struct tcphdr *)(skb->data + (skb->nh.iph->ihl * 4));
/* Now check the destination port */ if ((thead->dest) == *(unsigned short *)deny_port) { return NF_DROP; } return NF_ACCEPT; }
确实很简单!不要忘了,要让这个函数工作,deny_port必须是网络字节序。这就是数据包过滤的基础了,你应当已经清楚的理解了对于一个特定的数据包,如何获取你想要的信息。现在,是该进入更有趣的内容的时候了!
--[ 5 - Netfilter hook的其它可能用法
在这里,我将提出其它很酷的利用Netfilter hook的点子,5.1节将简单的给出精神食粮,而5.2节将讨论和给出可以工作的基于内核的FTP密码嗅探器的代码,它的远程密码获取功能是确实可用的。事实上,它工作的令我吃惊的好,并且我编写了它。 ----[ 5.1 - 隐藏后门的守护进程
核心模块编程也许是Linux开发中最有趣的部分之一了,在内核中编写代码意味着你在一个仅受限于你的想象力的地方写代码。以恶意的观点来看,你可以隐藏文件、进程,并且做各式各样很酷的,任何的rootkit能够做的事情。那么,以不太恶意的观点来看(是的,持这中观点人们的确存在),你可以隐藏文件、进程以及干各式各样的事情。内核真是一个迷人的乐园! 有了赋予内核级程序员的强大力量,很多事情成为可能。其中最有趣的(也是让系统管理员恐慌的)一个就是嵌入到内核中的后门。毕竟,如果后门不作为一个进程运行,那么我们怎么知道它的运行?当然,还是有办法让你的内核揪出这样的后门来,但是它们可不像运行ps命令一样容易和简单。现今,将后门代码放到内核中去的点子已经并不新鲜了。但是,我在这里所提出的是安放一个用作内核后门的简单的网络服务。你猜对了,正是Netfilter hook! 如果你已经具备必要的技能并且情愿以做试验的名义使你的内核崩溃,那么你就可以构建简单但是有用的,完全位于内核中的,可以远程访问的网络服务了。基本上一个Netfilter hook可以通过观察收到的数据包来查找一个“魔法”数据包,并且当接收到这个“魔法”数据包时干指定的事情。结果可以通过Netfilter hook来发送。并且该hook函数可以返回NF_STOLEN,以使得收到的“魔法”数据包可以走得更远。但是要注意,当以这种方式来发送时,输出数据包对于输出Netfilter hook仍然是可见的。因此用户空间完全不知道这个“魔法”数据包的曾经到达,但是它们还是能看到你送所出的。当心!因为在泄密主机上的嗅探器不能看到这个包并不意味着在其它中间宿主主机上的嗅探器也看不到这个包。 kossak与lifeline曾为Phrack写了一篇精彩的文章,该文描述了如何通过注册数据包类型处理器来完成这样的功能。虽然本文涉及的是Netfilter hook,我仍然建议阅读他们的这篇文章(第55期,文件12),因为它是一篇给出了一些非常有趣的点子的有趣读物。 那么,后门Netfilter hook可以干些什么工作呢?以下是一些建议: -- 远程访问击键记录器(key-logger)。模块记录击键,并且当远程主机发送一个PING请求时,结果被送到该主机。这样,可以生成一个类似于稳定的(非洪水的)PING应答流的击键信息的流。当然,你可能想要实现一个简单的加密,这样,ASCII键不会立即暴露它们自己,并且某些警觉的系统管理员会想:“坚持,我以前都是通过我的SSH会话来键入那些的!Oh $%@T%&!”。 -- 各种简单的管理员任务,例如获取当前登录到主机的用户的列表或责获取打开的网络连接的信息。 -- 并非一个真正的后门,而是位于网络边界的模块,并且阻挡任何被疑为来自特洛伊木马、ICMP隐蔽通道或者像KaZaa这样的文件共享工具的通信。 -- 文件传输“服务器”。我最近已经实现了这个主意,由此引起的Linux核心编程是数小时的乐趣:) -- 数据包跳跃。重定向目的为木马主机指定端口的数据包到其它的IP主机和端口,并且从那台主机发回数据包到发起者。没有进程被派生,并且最妙的是,没有网络套接字被打开。 -- 上面描述的数据包跳跃用于与网络中的关键系统以半隐蔽方式通信。例如:配置路由器等。 -- FTP/POP3/Telnet密码嗅探器。嗅探输出的密码并保存相关信息,直到进入的“魔法”数据包要求获取它们。 以上只是一些想法的简短的列表,其中最后一个想法是我们在接下来的一节中将要真正详细讨论的。它 提供了一个很好的了解更多的深藏于核心网络代码中的函数的机会。
----[ 5.2 - 基于内核的FTP密码嗅探器
在这里展现的是一个简单的,原理性的,用做Netfilter后门的模块。该模块嗅探输出的FTP数据包,查找对于一个FTP服务器一个USER于PASS命令对。当这样一个命令对被发现后,该模块接下来将等待一个“魔法”ICMP ECHO(ping)数据包,该数据包应当足够大,使其能返回服务器的IP地址、用户名以及密码。同时提供了一个快速的发送一个“魔法”数据包,获取返回然后打印返回信息的技巧。一旦用户名/密码对从模块读取后,模块将接着查找下一对。注意,模块每次只保存一个对。以上是简要的浏览,是该展示更多的细节,来看模块如何做到这些的时候了。 当模块加载时,模块的init_module()函数简单的注册了两个Netfilter hook。第一个用于查看输入的数据包(在NF_IP_PRE_ROUTING处),尝试发现“魔法”ICMP数据包。接下来的一个用于查看离开该模块被安装的主机的数据包(在NF_IP_POST_ROUTING处),这个函数正是搜索和捕获FTP的USER和PASS数据包的地方。cleanup_module()函数只是简单的注销这两个hook。 watch上一页 [1] [2] [3] [4] [5] [6] 下一页 |