深入Linux网络核心堆栈 (二)
转自:http://www.xfocus.net 创建时间:2003-08-22 文章属性:翻译 文章提交:raodan (raod_at_30san.com)
--[ 6 - 在Libpcap中隐藏网络通信
这一节简短的描述,如何在修改Linux的内核,使与匹配预先定义的条件的网络通信对运行于本机的数据包嗅探工具不可见。列在本文最后的是可以正常运行的代码,它实现了隐藏所有来自或者是去往指定的IP地址的数据包的功能。好了,让我们开始... ----[ 6.1 - SOCK_PACKET、SOCK_RAW与Libpcap
对系统管理员来说,最有用的软件莫过于哪些在广义分类下被称为“数据包嗅探器”的软件了。两个最典型的通用数据包嗅探器是tcpdump(1)以及ethereal(1)。这两个软件都利用了Libpcap库(随着参考文献[1]中的tcpdump发布)来抓取原始数据包。网络入侵检测系统(NIDS)也利用了Libpcap库。SNORT需要Libpcap,Libnids——一个提供IP重组和TCP流跟踪的NIDS开发库(参见参考文献[2]),也是如此。 在Linux系统下,Libpcap库使用SOCK_PACKET接口。Packet套接字是一种特殊的套接字,它可以用于发生和接收链路层的原始数据包。关于Paket套接字有很多话题,但是由于本节讨论的是关于如何隐藏它们而不是如何利用它们,感兴趣的读者可以直接去看packet(7)手册页。对于本文中的讨论,只需要理解packet套接字被Libpcap应用程序用于获取进入或者离开本地主机的原始数据包。 当核心网络堆栈收到一个数据包的时候,检查该数据包是否是某个packet套接字感兴趣的数据包。如果是,则将该数据递交给那些对其感兴趣的套接字。如果不是,该数据包继续它的旅程,进入TCP、UDP或者其它类型的套接字。对于SOCK_RAW类型的套接字同样如此。原始套接字很类似于packet套接字,只是原始套接字不提供链路层的包头。一个利用原始套接字的实用程序的例子是我的SYNalert程序,参见参考文献[3](请原谅我在这儿插入的题外话 :)。 到此,你应该已经了解了Linux下的数据包嗅探软件使用了Libpcap库。Libpcap在Linux下利用packet套接字接口来获取包含链路层包头的原始数据包。同时提到了原始套接字,它提供给用户空间的应用程序获取包含IP头的数据包的方法。下一节将讨论如何通过Linux核心模块来隐藏来自这些packet套接字以及原始套接字的网络通信。 ------[ 6.2 给狼披上羊皮
当收到数据包并将其送到一个packet套接字时,packet_rcv()函数被调用。这个函数可以在net/packet/af_packet.c中找到,packet_rcv()负责使数据包经过所有应用于目的套接字的套接字过滤器,并最终将其递交到用户空间。为了隐藏来自packet套接字的数据包,我们需要阻止所有特定数据包调用packet_rcv()函数。我们如何做到这一点?当然是优秀的ol式的函数劫持了。 函数劫持的基本操作是:如果我们知道一个内核函数,甚至是那些没有被导出的函数,的入口地址,我们可以在使实际的代码运行前将这个函数重定位到其他的位置。为了达到这样的目的,我们首先要从这个函数的开始,保存其原来的指令字节,然后将它们换成跳转到我们的代码处执行的绝对跳转指令。例如以i386汇编语言实现该操作如下: movl (address of our function), %eax jmp *eax
这些指令的16进制代码如下(假设我们的函数地址为0): 0xb8 0x00 0x00 0x00 0x00 0xff 0xe0
如果我们在Linux核心模块的初始化时将上例中的函数地址替换为我们的hook函数的地址,我们就能够使我们的hook函数先运行。当我们想运行原来的函数时,我们只需要在开始时恢复函数原来的指令,调用该函数并且替换我们的劫持代码。简单而有效。Silvio Cesare 不久前写过一篇文章,讲述如何实现内核函数劫持,参见参考文献[4]。 要从packet套接字隐藏数据包,我们首先要写一个hook函数,用于检查数据包是否满足我们隐藏的标准。如果满足,那么我们的hook函数简单的向它的调用函数返回0,packet_rcv()永远不会被调用。如果packet_rcv()永远不被调用,那么这个数据包也永远都不会递交给用户空间的packet套接字。注意,只是对于"packet"套接字来说,该数据包被丢弃了。如果我们要过滤送到packet套接字的FTP数据包,那么FTP服务器的TCP套接字仍然能收到这些数据包。我们所做的一切只是使运行在本机上的嗅探软件无法看到这些数据包。FTP服务器仍然能够处理和记录连接。 理论上就是这么多,关于原始套接字的用法同理可得。不同的是我们需要hook的是raw_rcv()函数(在net/ipv4/raw.c中可以找到)。下一节将给出并讨论一个Linux核心模块的示例代码,该代码劫持packet_rcv()函数和raw_rcv()函数,隐藏任何来自或去往我们指定的IP地址的数据包。
--[ 7 - 结束语
希望你现在至少对Netfilter有了一个初步的了解,如何使用它以及你能用它来做什么。你同样也应当有了一些使特定的网络通信从运行在本机的嗅探软件中隐藏的知识了。如果你需要本文中涉及的源代码的tar包,请直接给我发email。我同样很乐意接收任何的指正、批评或者建议。好了,把一切都留给你和你的想象力,来做一些我在这儿展现的有趣的事吧!
--[ A - 轻量级防火墙 ----[ A.1 - 概述
轻量级防火墙(LWFW)是一个简单的内核模块,用于演示我们在第4节中涉及的基本的数据包过录技术。LWFW也通过ioctl()系统调用提供了一个控制接口。 由于LWFW的源代码已经有足够的文档了,我在这儿只给出它如何工作的简单概述。当LWFW模块被加载后,它的第一个任务就是尝试注册控制设置。注意在LWFW的ioctl()控制接口可用之前,需要在/dev下创建一个字符设备文件。如果控制设备注册成功,"in use"标志被清除并且对NF_IP_PRE_ROUTE进行hook的函数被注册。清除函数执行相反的操作。 LWFW对数据包丢弃提供三个基本的选项。按照处理的顺序列出如下: -- 源接口 -- 源IP地址 -- 目的TCP端口 这些规则的设置由ioctl()接口完成。当一个数据包被接收,LWFW按照我们设定的规则进行检查。如果匹配了其中的任意一条规则,那么hook函数将返回NF_DROP,然后Netfilter将悄无声息的丢弃这个数据包。否则,hook函数返回NF_ACCEPT,数据包将继续它的旅程。 最后,有必要提一下的是LWFW的统计日志。无论任何时候数据包进入hook函数,LWFW都将收到的数据包的计数累加。单独的规则检查函数负责增加它们各自的丢弃的数据包的计数。注意,当规则的值被改变时,它的丢弃数据包的计数被重置为0。lwfwstats程序利用LWFW_GET_STATS这个IOCTL来获取统计数据结构的一个副本并显示其内容。
----[ A.2 - 源代码 : lwfw.c
<++> lwfw/lwfw.c /* Light-weight Fire Wall. Simple firewall utility based on * Netfilter for 2.4. Designed for educational purposes. * * Written by bioforge - March 2003. */
#define MODULE #define __KERNEL__
#include <linux/module.h> #include <linux/kernel.h> #include <linux/net.h> #include <linux/types.h> #include <linux/skbuff.h> #include <linux/string.h> #include <linux/malloc.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h>
#include <asm/errno.h> #include <asm/uaccess.h>
#include "lwfw.h"
/* Local function prototypes */ static int set_if_rule(char *name); static int set_ip_rule(unsigned int ip); static int set_port_rule(unsigned short port); static int check_ip_packet(struct sk_buff *skb); static int check_tcp_packet(struct sk_buff *skb); static int copy_stats(struct lwfw_stats *statbuff);
/* Some function prototypes to be used by lwfw_fops below. */ static int lwfw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int lwfw_open(struct inode *inode, struct file *file); static int lwfw_release(struct inode *inode, struct file *file);
/* Various flags used by the module */ /* This flag makes sure that only one instance of the lwfw device * can be in use at any one time. */ static int lwfw_ctrl_in_use = 0;
/* This flag marks whether LWFW should actually attempt rule checking. * If this is zero then LWFW automatically allows all packets. */ static int active = 0;
/* Specifies options for the LWFW module */ static unsigned int lwfw_options = (LWFW_IF_DENY_ACTIVE | LWFW_IP_DENY_ACTIVE | LWFW_PORT_DENY_ACTIVE);
static int major = 0; /* Control device major number */
/* This struct will describe our hook procedure. */ struct nf_hook_ops nfkiller;
/* Module statistics structure */ static struct lwfw_stats lwfw_statistics = {0, 0, 0, 0, 0};
/* Actual rule ''''definitions''''. */ /* TODO: One day LWFW might actually support many simultaneous rules. * Just as soon as I figure out the list_head mechanism... */ static char *deny_if = NULL; /* Interface to deny */ static unsigned int deny_ip = 0x00000000; /* IP address to deny */ static unsigned short deny_port = 0x0000; /* TCP port to deny */
/* * This is the interface device''''s file_operations structure */ struct file_operations lwfw_fops = { NULL, NULL, NULL, NULL, NULL, NULL, lwfw_ioctl, NULL, lwfw_open, NULL, lwfw_release, NULL /* Will be NULL''''ed from here... */ };
MODULE_AUTHOR("bioforge"); MODULE_DESCRIPTION("Light-Weight Firewall for Linux 2.4");
/* * This is the function that will be called by the hook */ unsigned int lwfw_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { unsigned int ret = NF_ACCEPT; /* If LWFW is not currently active, immediately return ACCEPT */ if (!active) return NF_ACCEPT; lwfw_statistics.total_seen++; /* Check the interface rule first */ if (deny_if && DENY_IF_ACTIVE) { if (strcmp(in->name, deny_if) == 0) { /* Deny this interface */ lwfw_statistics.if_dropped++; lwfw_statistics.total_dropped++; return NF_DROP; } } /* Check the IP address rule */ if (deny_ip && DENY_IP_ACTIVE) { ret = check_ip_packet(
[1] [2] [3] [4] 下一页 [SyBase](转载)深入Linux网络核心堆栈 (一) [MySql]Linux做一个功能完备的路由器 二
|