如何实现一个Firewall-Hook驱动

来源:互联网 发布:淘宝发短信的店铺链接 编辑:程序博客网 时间:2024/05/16 05:28

如何实现一个Firewall-Hook驱动


简介

或许,Firewall-Hook驱动是开发人员在Windows系统中开发包过滤程序时可用的最缺少文档的方法之一。Microsoft没有给出任何关于它的文档,而唯一可以知道一点东西的地方就是在DDK(Driver Development Kit,设备驱动程序开发包――译者注)的头文件(ipFirewall.h)中。事实上,当我安装完Windows 2000 DDK时,我非常惊讶于发现了这个.h文件(以及它的内容),因为没有任何关于这个Firewall-Hook的文档可以读到。在DDK的下一个版本中,微软加入了关于它的很少一些文档:“虽然有这样的方法,但是不推荐使用。”

然而,由于它是一个简单的实现防火墙解决方案的方法,所以我还是对Firewall-Hook驱动是如何工作的很感兴趣。

Firewall Hook 驱动

我不明白微软为什么不推荐使用Firewall-Hook 驱动做开发。尽管我不建议使用它来实现一个完整的防火墙解决方案,但对于较小的应用,它将是一个不错的选择。实际上Firewall-Hook驱动可以完成和Filter-Hook驱动同样的工作(从我的文章“Developing Firewalls with Filter-Hook driver”可以得到更多信息),却有着更少的限制。

还记得吧,Filter-Hook驱动只允许在系统中安装一个过滤函数。如果一个程序已经使用了这个功能,你的程序就不能正常工作了。而对于Firewall-Hook驱动,就不存在这个问题。你可以安装所有需要的过滤函数。每个过滤函数都被赋予一个优先级,这样系统就可以接连调用这些函数(按优先级顺序)直到一个函数返回"DROP PACKET"。如果所有的函数都返回"ALLOW PACKET",这个包就会被允许通过。你可以把它想象成一个过滤函数链。当它们其中的一个返回"DROP PACKET"的时候这个链就会被破坏。链中函数的顺序是由它的优先级数来给定。


在下图中描述了下面的步骤:

1。主机接收到了一个包。IP驱动有一个按优先级排好序的过滤函数列表(Filter Function 1有最高优先级)。

2。首先,IP驱动会把包传给优先级最高的过滤函数,并等待它的返回值。

3。Filter Function 1返回"ALLOW PACKET"。

4。因为Filter Function 1允许这个包通过,IP驱动会把这个包传给下一个过滤函数:Filter Function 2。

5。这次,Filter Function 2返回"DROP PACKET"。因此,IP驱动丢弃这个包并且不再继续调用后面的过滤函数。

有关Filter-Hook驱动的另一个问题就是包的发送:你不能存取包的内容数据。但是,使用Firewall-Hook驱动就可以存取所有的数据。一个Firewall-Hook驱动过滤函数接收的数据结构要比Filter-Hook驱动接收的复杂的多。它更像在NIDS(Network Intrusion Detection System,网络入侵检测系统――译者注)驱动中的包的结构:整个包由一系列的缓冲区所组成。不要着急,我们稍后会对它作进一步的了解。

和Filter-Hook驱动一样,Firewall-Hook驱动只是一个用来安装回调函数的内核模式(kernel mode)的驱动(但是Firewall-Hook驱动在IP驱动中安装回调函数)。实际上,安装一个Firewall-Hook驱动的进程和安装一个Filter-Hook驱动所使用的进程相类似。在ipFirewall.h文件里,你可以找到下面几行程序:

typedef struct _IP_SET_FIREWALL_HOOK_INFO 

{     

// Packet filter callout.     

IPPacketFirewallPtr FirewallPtr;     

// Priority of the hook    

UINT Priority;        

// if TRUE then ADD else DELETE          

BOOLEAN Add;

} IP_SET_FIREWALL_HOOK_INFO, *PIP_SET_FIREWALL_HOOK_INFO;    


#define DD_IP_DEVICE_NAME L"\\Device\\IP"

#define _IP_CTL_CODE(function, method, access) \         

CTL_CODE(FSCTL_IP_BASE, function, method, access) 


#define IOCTL_IP_SET_FIREWALL_HOOK \         

_IP_CTL_CODE(12, METHOD_BUFFERED, FILE_WRITE_ACCESS)


这几行程序告诉了你如何安装一个回调函数。你只需把你的回调函数添入IP_SET_FIREWALL_HOOK_INFO 结构中,并且安装它使其发送IOCTL IOCTL_IP_SET_FIREWALL_HOOK到IP设备(IP device)。这很容易,如果你以前与驱动且与有文档的Filter-Hook驱动打过交道的话。在这个结构中最重要的一个参数就是Priority。每个Priority域都包含着过滤函数的优先级,值越大优先级就越大。

PDEVICE_OBJECT ipDeviceObject=NULL; 

IP_SET_FIREWALL_HOOK_INFO filterData; 

//..... 

// Init structure 

filterData.FirewallPtr = filterFunction; 

filterData.Priority = 1; 

filterData.Add = TRUE; 

//.... 

// Send the commando to ip driver

IoCallDriver(ipDeviceObject, irp);

如果你想卸载一个过滤函数的话,你可以用相同的代码,只是将filterData.Add赋成FALSE就行了


过滤函数

用在Firewall-Hook驱动中的过滤函数比用在Filter-Hook中的要复杂些。并且由于没有这个函数及其参数的文档,复杂性就更高了。这个函数有着下面的签名:

FORWARD_ACTION cbFilterFunction(VOID **pData, 
                                UINT RecvInterfaceIndex, 
                                UINT *pSendInterfaceIndex, 
                                UCHAR *pDestinationType, 
                                VOID *pContext, 
                                UINT ContextLength, 
                                struct IPRcvBuf **pRcvBuf);

我耐着性子,使用调试的方法(还有对这些参数的名字的解释:)),得到了以下关于这些参数的信息:

pData

*pData指向了一个含有包缓冲的(struct IPRcvBuf *)结构体。

RecvInterfaceIndex

接收数据的接口

pSendInterfaceIndex

指向含有数据被发送到的索引值的unsigned int的指针◎。尽管它是一个指针,但是改变它的值并不能使包重新被路由:(

pDestinationType

指向标明目的地类型的unsigned int 的指针: 本地网络, 远程, 广播,多播等等。

pContext

是一个指向 FIREWALL_CONTEXT_T 结构的指针,在这个结构中你可以找到关于包的信息,比如这是个收到还是发出的包。

ContextLength

由pContext指向的缓冲区的大小。它的值总是sizeof(FIREWALL_CONTEXT_T)。

pRcvBuf

*pRcvBuf 总是指向NULL。

◎原文如此:Pointer to unsigned int containing the value of the index where data is sent.――译者注

这些信息可能在将来的Windows版本中有所变化,因为至今没有任何关于它的官方文档。我只能保证对这些域的解释在我对Windows 2000和Windows XP试验中是正确的。

对于每一个包,我们的函数都会被调用,并且根据函数的返回值,这个包或被丢弃或被允许通过。在过滤函数中你可以返回的值为:

FORWARD

包被允许通过

DROP

包被丢弃

ICMP_ON_DROP

包被丢弃并将一个ICMP包发送到远程主机

释放缓冲区

与Filter-Hook驱动不同,你不能在Firewall-Hook驱动过滤函数中直接得到含有数据包头以及其内容的缓冲区。通过几次试验,我了解了缓冲区的内部结构。正如我前面所讲,发送/接收的数据包是通过pData这个参数来传送的。*pData是指向一个IPRcvBuf结构:

struct IPRcvBuf 
{
    // Point to the next buffer in the chain
    struct IPRcvBuf *ipr_next;    // Always 0 
    UINT ipr_owner;     // Buffer data 
    UCHAR *ipr_buffer;       // Buffer data size
    UINT ipr_size;      // In my tests always a pointer to NULL.
    // Maybe the system could use MDLs instead of IPRcvBuf structures (but
    // i never have seen it).
    PMDL ipr_pMdl;        // Always a pointer to NULL.
    UINT *ipr_pClientCnt;     // Always a pointer to NULL.
    UCHAR *ipr_RcvContext;        // Always 0. I suppose this field is a offset into buffer data
    // but because I haven't a value different from 0, I can affirm it.
    UINT ipr_RcvOffset;     // In Windows 2003 DDK the name of this field have changed to flags.
    // In my tests I always get 0 value for local traffic and 2 for remote.
    // It's the only thing I can tell you about this field.
    ULONG ipr_promiscuous;
};

对于我们的用途,我们只需要了解ipr_next, ipr_buffer 以及ipr_size域。ipr_buffer域中包含有ipr_size字节的包的数据。然而,整个包不必都存储在一个缓冲区中,系统会链接起几个缓冲区(来存储包)。ipr_next就是因为这个而被使用的。这个域指向下一个含有这个包的数据的数据结构。当这个数据结构的ipr_next域指向NULL时我们就得到了整个包。因此,我们在Firewall-Hook驱动中找到了一个和NDIS驱动中一样的链式缓冲区结构。在我的试验中,对于所有接收的包,函数只接收到了一个在其数据缓冲区中带有所有数据的结构,而对于发送的包,我发现了一些缓冲区链,每个链都包含了某一协议的信息。我的意思是,如果我发送一个ICMP包,那么就会有三个缓冲区链:一个包含IP头部,一个包含ICMP头部,还有一个包含有数据字段。但是,就像在NDIS驱动中一样,我们不能依赖于系统如何填充这些缓冲区。

在下面的图中,你可以看到一个在Firewall-Hook如何创建包的例子:


作为例子(原文为As a simple,疑为sample之误――译者注),你可以在下面的代码中了解到如何在一个缓冲区链中得到包内容的线性缓冲(lineal buffer)。

char *pPacket = NULL;
int iBufferSize;
struct IPRcvBuf *pBuffer = (struct IPRcvBuf *) *pData;// First, I calculate the total size of the packet
iBufferSize = buffer->ipr_size;

while(pBuffer->ipr_next != NULL)
{
    pBuffer = pBuffer->ipr_next;
    iBufferSize += pBuffer->ipr_size;
} // Reserve memory to the lineal buffer.
pPacket = (char *) ExAllocatePool(NonPagedPool, iBufferSize);
if(pPacket != NULL)
{
    unsigned int iOffset = 0;
    pBuffer = (struct IPRcvBuf *) *pData;        // we are going to copy each buffer of the chain in the lineal buffer.
    memcpy(pPacket, pBuffer->ipr_buffer, pBuffer->ipr_size);
    while(pBuffer->ipr_next != NULL)
    { 
        iOffset += pBuffer->ipr_size;
        pBuffer = pBbuffer->ipr_next;
        memcpy(pPacket + iOffset, pBuffer->ipr_buffer, pBbuffer->ipr_size);
    } 
}

对于那些好奇的人(在你们问我之前先说啦:P),你们可以冒险去改动包的数据。目前还没有工具(在Firewall-Hook驱动中――译者注)让你去做这种类型的软件,如果要做这样的事,我推荐你去实现一个NDIS IM驱动或者TDI Filter驱动。关于这个我没有做很多试验,但是我不会对一个改变包的内容的Firewall-Hook驱动的稳定性过分依赖。为什么呢?因为我们并不知道IP 驱动是如何去管理这些缓冲区的,也不知道更改它们会造成什么风险。总之,我的建议是:看看就行了


http://hi.baidu.com/fandunqiu/item/1d08033289c347312e0f81b2

http://hi.baidu.com/fandunqiu/item/e4c8f3304bd4c7f62784f4b2

http://hi.baidu.com/fandunqiu/item/9b483e8aad5452c8b171548e

http://hi.baidu.com/fandunqiu/item/da12bf6459fe9290c5d2498e

原创粉丝点击