首 页文章中心

Linux学习网

您的位置Linux学习网 > Linux综合 > 文章内容

深入Linux网络核心堆栈!

作者:佚名  来源:不详  发布时间:2007-12-21 16:49:00

深入Linux网络核心堆栈!dnzLinux联盟
dnzLinux联盟
创建时间:2003-08-22dnzLinux联盟
文章属性:翻译dnzLinux联盟
文章提交:raodan (raod_at_30san.com)dnzLinux联盟
出自:http://www.xfocus.net/articles/200308/599.htmldnzLinux联盟
dnzLinux联盟
==Phrack Inc.==dnzLinux联盟
dnzLinux联盟
       
卷标 0x0b, 期刊号 0x3d, Phile #0x0d of 0x0fdnzLinux联盟
dnzLinux联盟
|=---------------------=[
深入Linux网络核心堆栈 ]=-----------------------=|dnzLinux联盟
|=-----------------------------------------------------------------------=|dnzLinux联盟
|=------------------=[ bioforge <alkerr@yifan.net> ]=--------------------=|dnzLinux联盟
|=------------------------=[
翻译 : raodan ]=----------------------------=|dnzLinux联盟
dnzLinux联盟
目录dnzLinux联盟
dnzLinux联盟
1 -
简介dnzLinux联盟
1.1 -
本文涉及的内容dnzLinux联盟
1.2 -
本文不涉及的内容dnzLinux联盟
2 -
各种Netfilter hook及其用法dnzLinux联盟
2.1 - Linux
内核对数据包的处理dnzLinux联盟
2.2 - Netfilter
IPv4hookdnzLinux联盟
3 -
注册和注销Netfilter hookdnzLinux联盟
4 - Netfilter
基本的数据报过滤技术[1]dnzLinux联盟
4.1 -
深入hook函数dnzLinux联盟
4.2 -
基于接口进行过滤dnzLinux联盟
4.3 -
基于地址进行过滤dnzLinux联盟
4.4 -
基于TCP端口进行过滤dnzLinux联盟
5 - Netfilter hook
的其它可能用法dnzLinux联盟
5.1 -
隐藏后门的守护进程dnzLinux联盟
5.2 -
基于内核的FTP密码嗅探器dnzLinux联盟
  5.2.1 -
源代码 : nfsniff.cdnzLinux联盟
  5.2.2 -
源代码 : getpass.cdnzLinux联盟
6 -
Libpcap中隐藏网络通信dnzLinux联盟
6.1 - SOCK_PACKET
SOCK_RAWLibpcapdnzLinux联盟
6.2 -
给狼披上羊皮dnzLinux联盟
7 -
结束语dnzLinux联盟
A -
轻量级防火墙dnzLinux联盟
A.1 -
概述dnzLinux联盟
A.2 -
源代码 : lwfw.cdnzLinux联盟
A.3 -
头文件 : lwfw.hdnzLinux联盟
B -
6节中的源代码dnzLinux联盟
dnzLinux联盟
--[ 1 -
简介dnzLinux联盟
dnzLinux联盟
 
本文将向你展示,Linux的网络堆栈的一些怪异行为(并不一定是弱点)如何被用于邪恶的或者是其它形形色色的目的。在这里将要讨论的是将表面上看起来合法的Netfilter hook用于后门的通信,以及一种使特定的网络通信在运行于本机的基于Libpcap的嗅探器中消声匿迹的技术。dnzLinux联盟
  Netfilter
Linux 2.4内核的一个子系统,Netfiler使得诸如数据包过滤、网络地址转换(NAT)以及网络连接跟踪等技巧成为可能,这些功能仅通过使用内核网络代码提供的各式各样的hook既可以完成。这些hook位于内核代码中,要么是静态链接的,要么是以动态加载的模块的形式存在。可以为指定的网络事件注册相应的回调函数,数据包的接收就是这样一个例子。dnzLinux联盟
dnzLinux联盟
dnzLinux联盟
----[ 1.1 -
本文涉及的内容dnzLinux联盟
  dnzLinux联盟
 
本文讨论模块编写者如何利用Netfilter hook来实现任意目的以及如何将将网络通信在基于Libpcap的应用程序中隐藏。虽然Linux 2.4支持对IPv4IPv6以及DECnethook,但在本文中将只讨论关于IPv4的话题,虽然如此,大部分关于IPv4的内容都同样可以运用于其它几种协议。出于教学的目的,附录A提供了一个可用的、提供基本的包过滤的内核模块。本文中所有的开发和试验都在运行于Intel主机上的Linux 2.4.5中完成。对Netfilter hook功能的测试在环回接口、以太网接口以及调制解调器点对点接口上完成。dnzLinux联盟
  dnzLinux联盟
 
本文也是出于我对Netfilter完全理解的尝试的兴趣而写的。我并不能保证文中附带的任何代码100%的没有错误,但是我已经测试了所有在这里提供的代码。我已经受够了核心错误的折磨,因此真诚的希望你不会再如此。同样,我不会为任何按照本文所述进行的操作中可能发生的损害承担责任。本文假定读者熟悉C语言编程并且有一定的关于可加载模块的经验。dnzLinux联盟
  dnzLinux联盟
 
欢迎对本文中出现的错误进行批评指正,我同时开诚布公的接受对本文的改进以及其它各种关于Netfilter的优秀技巧的建议。dnzLinux联盟
  dnzLinux联盟
  dnzLinux联盟
---- [ 1.2 -
本文不涉及的内容dnzLinux联盟
dnzLinux联盟
 
本文不是一个完全的关于Netfilter的细节上的参考资料,同样,也不是一个关于iptables的命令的参考资料。如果你想了解更多的关于iptables的命令,请参考相关的手册页。dnzLinux联盟
  dnzLinux联盟
 
好了,让我们从Netfilter的使用介绍开始 ...dnzLinux联盟
  dnzLinux联盟
dnzLinux联盟
--[ 2 -
各种Netfilter hook及其用法dnzLinux联盟
----[ 2.1 - Linux
内核对数据包的处理dnzLinux联盟
  dnzLinux联盟
 
看起来好像是我很喜欢深入到诸如Linux的数据包处理以及事件的发生以及跟踪每一个Netfilter hook这样的血淋淋的细节中,事实并非如此!原因很简单,Harald Welte已经写了一篇关于这个话题的优秀的文章—— Journey of a Packet Through the Linux 2.4 Network Stack》。如果你想了解更多的关于 Linux数据包处理的内容,我强烈推荐你去拜读这篇文章。现在,仅需要理解:当数据包游历Linux内核的网络堆栈时,它穿过了几个hook点,在这里,数据包可以被分析并且选择是保留还是丢弃,这些hook点就是Netfilter hookdnzLinux联盟
  dnzLinux联盟
dnzLinux联盟
----[ 2.2 - Netfilter
IPv4hookdnzLinux联盟
dnzLinux联盟
  Netfilter
中定义了五个关于IPv4hook,对这些符号的声明可以在linux/netfilter_ipv4.h中找到。这些hook列在下面的表中:dnzLinux联盟
  dnzLinux联盟
1 : 可用的IPv4 hookdnzLinux联盟
dnzLinux联盟
  Hook          
调用的时机dnzLinux联盟
NF_IP_PRE_ROUTING  
在完整性校验之后,选路确定之前dnzLinux联盟
NF_IP_LOCAL_IN    
在选路确定之后,且数据包的目的是本地主机dnzLinux联盟
NF_IP_FORWARD    
目的地是其它主机地数据包dnzLinux联盟
NF_IP_LOCAL_OUT    
来自本机进程的数据包在其离开本地主机的过程中dnzLinux联盟
NF_IP_POST_ROUTING  
在数据包离开本地主机上线之前dnzLinux联盟
dnzLinux联盟
  NF_IP_PRE_ROUTING
这个hook是数据包被接收到之后调用的第一个hook,这个hook既是稍后将要描述的模块所用到的。当然,其它的hook同样非常有用,但是在这里,我们的焦点是在NF_IP_PRE_ROUTING这个hook上。dnzLinux联盟
dnzLinux联盟
 
hook函数完成了对数据包所需的任何的操作之后,它们必须返回下列预定义的Netfilter返回值中的一个:dnzLinux联盟
  dnzLinux联盟
2 : Netfilter返回值dnzLinux联盟
dnzLinux联盟
 
返回值           含义dnzLinux联盟
NF_DROP          
丢弃该数据包dnzLinux联盟
NF_ACCEPT        
保留该数据包dnzLinux联盟
NF_STOLEN        
忘掉该数据包dnzLinux联盟
NF_QUEUE        
将该数据包插入到用户空间dnzLinux联盟
NF_REPEAT        
再次调用该hook函数dnzLinux联盟
dnzLinux联盟
  NF_DROP
这个返回值的含义是该数据包将被完全的丢弃,所有为它分配的资源都应当被释放。NF_ACCEPT这个返回值告诉 Netfilter:到目前为止,该数据包还是被接受的并且该数据包应当被递交到网络堆栈的下一个阶段。NF_STOLEN是一个有趣的返回值,因为它告诉Netfilter忘掉这个数据包。这里告诉Netfilter的是:该hook函数将从此开始对数据包的处理,并且Netfilter应当放弃对该数据包做任何的处理。但是,这并不意味着该数据包的资源已经被释放。这个数据包以及它独自的sk_buff数据结构仍然有效,只是hook函数从 Netfilter获取了该数据包的所有权。不幸的是,我还不是完全的清楚NF_QUEUE到底是如果工作的,因此在这里我不讨论它。最后一个返回值 NF_REPEAT请求Netfilter再次调用这个hook函数。显然,使用者应当谨慎使用NF_REPEAT这个返回值,以免造成死循环。dnzLinux联盟
  dnzLinux联盟
--[3 -
注册和注销Netfilter hookdnzLinux联盟
dnzLinux联盟
 
注册一个hook函数是围绕nf_hook_ops数据结构的一个非常简单的操作,nf_hook_ops数据结构在linux/netfilter.h中定义,该数据结构的定义如下:dnzLinux联盟
dnzLinux联盟
[code:1:15d652b2b8]       struct nf_hook_ops {dnzLinux联盟
            struct list_head list;dnzLinux联盟
dnzLinux联盟
            /*
此下的值由用户填充 */dnzLinux联盟
            nf_hookfn *hook;dnzLinux联盟
            int pf;dnzLinux联盟
            int hooknum;dnzLinux联盟
            /* Hook
以升序的优先级排序 */dnzLinux联盟
            int priority;dnzLinux联盟
      };[/code:1:15d652b2b8]dnzLinux联盟
 
该数据结构中的list成员用于维护Netfilter hook的列表,并且不是用户在注册hook时需要关心的重点。hook成员是一个指向nf_hookfn类型的函数的指针,该函数是这个hook被调用时执行的函数。nf_hookfn同样在linux/netfilter.h中定义。pf这个成员用于指定协议族。有效的协议族在linux/socket.h中列出,但对于IPv4我们希望使用协议族PF_INET hooknum这个成员用于指定安装的这个函数对应的具体的hook类型,其值为表1中列出的值之一。最后,priority这个成员用于指定在执行的顺序中,这个hook函数应当在被放在什么地方。对于IPv4,可用的值在linux/netfilter_ipv4.h nf_ip_hook_priorities枚举中定义。出于示范的目的,在后面的模块中我们将使用NF_IP_PRI_FIRSTdnzLinux联盟
  dnzLinux联盟
 
注册一个Netfilter hook需要调用nf_register_hook()函数,以及用到一个nf_hook_ops数据结构。nf_register_hook()函数以一个nf_hook_ops数据结构的地址作为参数并且返回一个整型的值。但是,如果你真正的看了在 net/core/netfilter.c中的nf_register_hook()函数的实现代码,你会发现该函数总是返回0。以下提供的是一个示例代码,该示例代码简单的注册了一个丢弃所有到达的数据包的函数。该代码同时展示了Netfilter的返回值如何被解析。dnzLinux联盟
dnzLinux联盟
 
示例代码1 : Netfilter hook的注册dnzLinux联盟
[code:1:15d652b2b8]/* dnzLinux联盟
*
安装一个丢弃所有到达的数据包的Netfilter hook函数的示例代码 dnzLinux联盟
*/dnzLinux联盟
dnzLinux联盟
#define __KERNEL__dnzLinux联盟
#define MODULEdnzLinux联盟
dnzLinux联盟
#include <linux/module.h>dnzLinux联盟
#include <linux/kernel.h>dnzLinux联盟
#include <linux/netfilter.h>dnzLinux联盟
#include <linux/netfilter_ipv4.h>dnzLinux联盟
dnzLinux联盟
/*
用于注册我们的函数的数据结构 */dnzLinux联盟
static struct nf_hook_ops nfho;dnzLinux联盟
dnzLinux联盟
/*
注册的hook函数的实现 */dnzLinux联盟
unsigned int hook_func(unsigned int hooknum,dnzLinux联盟
              struct sk_buff **skb,dnzLinux联盟
              const struct net_device *in,dnzLinux联盟
              const struct net_device *out,dnzLinux联盟
              int (*okfn)(struct sk_buff *))dnzLinux联盟
{dnzLinux联盟
  return NF_DROP;       /*
丢弃所有的数据包 */dnzLinux联盟
}dnzLinux联盟
dnzLinux联盟
/*
初始化程序 */dnzLinux联盟
int init_module()dnzLinux联盟
{dnzLinux联盟
  /*
填充我们的hook数据结构 */dnzLinux联盟
  nfho.hook = hook_func;       /*
处理函数 */dnzLinux联盟
  nfho.hooknum = NF_IP_PRE_ROUTING; /*
使用IPv4的第一个hook */dnzLinux联盟
  nfho.pf     = PF_INET;dnzLinux联盟
  nfho.priority = NF_IP_PRI_FIRST;   /*
让我们的函数首先执行 */dnzLinux联盟
dnzLinux联盟
  nf_register_hook(&nfho);dnzLinux联盟
dnzLinux联盟
  return 0;dnzLinux联盟
}dnzLinux联盟
dnzLinux联盟
/*
清除程序 */dnzLinux联盟
void cleanup_module()dnzLinux联盟
{dnzLinux联盟
  nf_unregister_hook(&nfho);dnzLinux联盟
}[/code:1:15d652b2b8]  
这就是全部内容,从示例代码1中,你可以看到,注销一个Netfilter hook是一件很简单事情,只需要调用nf_unregister_hook()函数,并且以你之前用于注册这个hook时用到的相同的数据结构的地址作为参数。dnzLinux联盟
  dnzLinux联盟
dnzLinux联盟
-- [4 - Netfilter
基本的数据报过滤技术dnzLinux联盟
---- [4.1 -
深入hook函数dnzLinux联盟
dnzLinux联盟
 
现在是到了看看什么数据被传递到hook函数中以及这些数据如何被用于做过滤选择的时候了。那么,让我们更深入的看看nf_hookfn函数的原型吧。这个函数原型在linux/netfilter.h中给出,如下:dnzLinux联盟
[code:1:15d652b2b8]dnzLinux联盟
      typedef unsigned int nf_hookfn(unsigned int hooknum,dnzLinux联盟
                          struct sk_buff **skb,dnzLinux联盟
                          const struct net_device *in,dnzLinux联盟
                          const struct net_device *out,dnzLinux联盟
                          int (*okfn)(struct sk_buff *));dnzLinux联盟
dnzLinux联盟
  [/code:1:15d652b2b8]nf_hookfn
函数的第一个参数用于指定表1中给出的hook类型中的一个。第二个参数更加有趣,它是一个指向指针的指针,该指针指向的指针指向一个sk_buff数据结构,网络堆栈用sk_buff数据结构来描述数据包。这个数据结构在 linux/skbuff.h中定义,由于它的内容太多,在这里我将仅列出其中有意义的部分。dnzLinux联盟
dnzLinux联盟
  sk_buff
数据结构中最有用的部分可能就是那三个描述传输层包头(例如:UDP, TCP, ICMP, SPX)、网络层包头(例如:IPv4/6, IPX, RAW)以及链路层包头(例如:以太网或者RAW)的联合(union)了。这三个联合的名字分别是hnh以及mac。这些联合包含了几个结构,依赖于具体的数据包中使用的协议。使用者应当注意:传输层包头和网络层包头可能是指向内存中的同一个位置。这是TCP数据包可能出现的情况,其中hnh都应当被看作是指向IP头结构的指针。这意味着尝试通过h->th获取一个值,并认为该指针指向一个TCP头,将会得到错误的结果。因为h->th实际上是指向的IP头,与nh->iph得到的结果相同。dnzLinux联盟
dnzLinux联盟
 
接下来让我们感兴趣的其它部分是lendata这两个域。len指定了从data开始的数据包中的数据的总长度。好了,现在我们知道如何在sk_buff数据结构中分别访问协议头和数据包中的数据了。Netfilter hook函数中有用的信息中其它的有趣的部分是什么呢?dnzLinux联盟
dnzLinux联盟
 
紧跟在skb之后的两个参数是指向net_device数据结构的指针,net_device数据结构被Linux内核用于描述所有类型的网络接口。这两个参数中的第一个——in,用于描述数据包到达的接口,毫无疑问,参数out用于描述数据包离开的接口。必须明白,在通常情况下,这两个参数中将只有一个被提供。例如:参数in只用于NF_IP_PRE_ROUTINGNF_IP_LOCAL_IN hook,参数out只用于 NF_IP_LOCAL_OUTNF_IP_POST_ROUTING hook。在这一个阶段中,我还没有测试对于 NF_IP_FORWARD hook,这两个参数中哪些是有效的,但是如果你能在使用之前先确定这些指针是非空的,那么你是非常优秀的!dnzLinux联盟
dnzLinux联盟
 
最后,传递给hook函数的最后一个参数是一个命名为okfn函数指针,该函数以一个sk_buff数据结构作为它唯一的参数,并且返回一个整型的值。我不是很确定这个函数是干什么用的,在net/core/netfilter.c中查看,有两个地方调用了这个okfn函数。这两个地方是分别在函数nf_hook_slow()中以及函数nf_reinject()中,在其中的某个位置,当Netfilter hook的返回值为 NF_ACCEPT时被调用。如果任何人有更多的关于okfn函数的信息,请务必告知。dnzLinux联盟
  dnzLinux联盟
  **
译注:Linux核心网络堆栈中有一个全局变量 : struct list_head nf_hooks[NPROTO] [NF_MAX_HOOKS],该变量是一个二维数组,其中第一维用于指定协议族,第二维用于指定hook的类型(表1中定义的类型)。注册一个 Netfilter hook实际就是在由协议族和hook类型确定的链表中添加一个新的节点。dnzLinux联盟
  dnzLinux联盟
 
以下代码摘自 net/core/netfilternf_register_hook()函数的实现:dnzLinux联盟
[code:1:15d652b2b8]int nf_register_hook(struct nf_hook_ops *reg)dnzLinux联盟
{dnzLinux联盟
  struct list_head *i;dnzLinux联盟
dnzLinux联盟
  br_write_lock_bh(BR_NETPROTO_LOCK);dnzLinux联盟
  for (i = nf_hooks[reg->pf][reg->hooknum].next; dnzLinux联盟
      i != &nf_hooks[reg->pf][reg->hooknum]; dnzLinux联盟
      i = i->next) {dnzLinux联盟
    if (reg->priority < ((struct nf_hook_ops *)i)->priority)dnzLinux联盟
        break;dnzLinux联盟
  }dnzLinux联盟
  list_add(&->list, i->prev);dnzLinux联盟
  br_write_unlock_bh(BR_NETPROTO_LOCK);dnzLinux联盟
  return 0;dnzLinux联盟
}[/code:1:15d652b2b8]dnzLinux联盟
  Netfilter
中定义了一个宏NF_HOOK,作者在前面提到的nf_hook_slow()函数实际上就是NF_HOOK宏定义替换的对象,在NF_HOOK中执行注册的hook函数。NF_HOOKLinux核心网络堆栈的适当的地方以适当的参数调用。例如,在ip_rcv() 函数(位于net/ipv4/ip_input.c)的最后部分,调用NF_HOOK函数,执行NF_IP_PRE_ROUTING类型的hook ip_rcv()