您的位置:首页 > 其它

浅析基于中间层驱动的包过滤原理与实现

2007-04-30 18:15 597 查看
[align=center] 李大勇[/align]
[align=left]随着计算机网络技术的迅猛发展,网络安全的问题已经日益突出地摆在各类用户的面前。仅从掌握的资料表明,目前在互联网上大约有将近20%以上的用户曾经遭受过黑客的困扰。虽然网络防火墙技术日趋成熟,但大多数基于SPI的数据报拦截技术是在用户级的。用户级的拦截有其优势,实现方便、便于移植、通用性强,但是用户级并不能得到所有的数据报,本文将探讨基于IMD中间层驱动的防火墙包过滤原理与实现。[/align]
[align=left]让我们先来了解一下IMD,在windows 2000中支持三种基本的内核级网络驱动,分别是:[/align]
[align=left]1. Miniport NIC drivers:微端口网卡驱动,位于最底层,直接操纵网卡并且对高层驱动提供接口。[/align]
[align=left]2. Intermediate drivers:IMD中间层驱动,位于1和3之间,具体的作用下面就会介绍。[/align]
[align=left]3. Protocol drivers:高层协议驱动,俗称为TDI(传输驱动程序接口),高于前面两层,直接面向用户级,为用户提供网络服务,也就是绝大多数程序所用到的网络接口。[/align]
[align=left]IMD中间层,它的实质很简单,中间层插入网卡和协议层之间,对上面的协议层表现为一个虚拟的微端口网卡结构,而对下面的网卡则表现为一个协议层的结构。所以,无论是网卡接收并上传的数据报,还是上层要下送至网卡发送的数据报,无一例外地要经过中间层。既然所有的数据报都要经过中间层,我们可以在中间层加入我们想要过滤的数据报的特征,从而实现基于中间层驱动的内核级包过滤。[/align]
[align=left]这样做的优势非常明显,首先,在驱动级别上做过滤,无须组包,速度快,效率自然就高;其次,所有的数据报无一例外,只要网卡上传的数据报均可以截获,避免了用户级无法得到所有数据报的缺点。[/align]
[align=left]那么怎样来实现基于IMD的包过滤防火墙呢?其实微软在提出这项技术之后,其DDK中附带了一个中间层驱动的例程,就是passthru。passthru实现了一个中间层的基本功能,对下表现为一个协议层的驱动,对上表现为一个虚拟网卡,安装passthru驱动之后,你可以在硬件管理中的网卡中看到一个虚拟网卡。不过,passthru只是插入到网卡和上层协议中间,让所有的数据报原原本本地流经自己而已。我们要想实现中间层包过滤的功能,需要对passthru进行修改。在中间层接收到数据报的时候进行规则判断就可以了,而passthru中,接收数据报是用protocol.c文件中的PtReceive和PtReceivePacket这两个函数来实现的。我们知道了哪个函数负责接收数据报,那么我们就可以对这个函数进行修改了。从兼容和通用性考虑,我们需要对PtReceive和PtReceivePacket函数进行修改,其中加上我们需要判断的规则进行过滤,下面就贴详细的代码了。[/align]
[align=left]我们的目的是在调用接受数据报函数的时候能执行我们的过滤代码,所以,我们要在函数代码中添加我们自己的代码,下面用过滤特定协议类型的数据报来做演示。[/align]
[align=left]首先修改PtReceive,看一下protocol.c文件中函数的代码,代码中用NdisGetReceivedPacket函数得到一个PNDIS_PACKET的结构Packet,数据报内容就存放在这个结构中的链表内。我们定义一个PUCHAR结构的pPacketContent,然后用下面的代码获得整个数据报的内容:[/align]
[align=left]//----------------------------------------------------[/align]
[align=left]int PacketSize;[/align]
[align=left]PUCHAR pPacketContent;[/align]
[align=left]PUCHAR pBuf;[/align]
[align=left]UINT BufLength;[/align]
[align=left]MDL * pNext;[/align]
[align=left]UINT i;[/align]
[align=left]//把数据包内容从Packet拷贝到pPacketContent[/align]
[align=left]NdisQueryPacket( Packet,NULL,NULL,NULL,&PacketSize);[/align]
[align=left]Status= NdisAllocateMemory( &pPacketContent, 2000, 0,HighestAcceptableMax);[/align]
[align=left]if (Status!=NDIS_STATUS_SUCCESS ) return Status;[/align]
[align=left]NdisZeroMemory (pPacketContent, 2000);[/align]
[align=left]NdisQueryBufferSafe(Packet->Private.Head, &pBuf, &BufLength, 32 );[/align]
[align=left]NdisMoveMemory(pPacketContent, pBuf, BufLength);[/align]
[align=left]i = BufLength;[/align]
[align=left]pNext = Packet->Private.Head;[/align]
[align=left]for(;;)[/align]
[align=left]{[/align]
[align=left]if(pNext == Packet->Private.Tail)[/align]
[align=left]break;[/align]
[align=left]pNext = pNext->Next; //指针后移[/align]
[align=left]if(pNext == NULL) [/align]
[align=left]break;[/align]
[align=left] [/align]
[align=left]NdisQueryBufferSafe(pNext,&pBuf,&BufLength,32);[/align]
[align=left]NdisMoveMemory(pPacketContent+i,pBuf,BufLength);[/align]
[align=left]i+=BufLength;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]//数据拷贝完毕[/align]
[align=left]//-----------------------------------------------------[/align]
[align=left] [/align]
[align=left]现在,我们已经在PtReceive函数中得到了数据报的内容,存放在pPacketContent中,数据报大致是如下结构,以太帧头14个字节,放在pPacketContent[0]到pPacketContent[13]中,其中前六个字节是目的MAC地址,然后六个字节源MAC地址,然后两个字节是协议类型,通常的协议类型有0x08 0x00 ->IP,0x08 0x06 ->ARP,0x08 0x35 ->RARP,所以,可以通过pPacketContent[12]和pPacketContent[13]来判断协议类型。如果是IP包,然后pPacketContent中存放的是IP头,根据IP头的格式,可以得到第23个字节pPacketContent[23]表示传输层协议:1 ->ICMP,2 ->IGMP,6 ->TCP,17 ->UDP,剩下的就是数据报内容了。因为我们只是做演示,所以只要知道这几个标志性的就好了,其他的你可以根据你的需要扩展。我们通过pPacketContent中的内容可以做些规则,比如过滤ICMP包,我们只要比较pPacketContent[12]和pPacketContent[13]还有pPacketContent[23]这三个标志位就可以了,如果不是ICMP包,那么不做任何工作,如果匹配了,那就返回一个NDIS_STATUS_NOT_ACCEPTED,将包丢弃,释放pPacketContent,就可以过滤ICMP包了,下面就是过滤规则的代码。[/align]
[align=left] [/align]
[align=left]//-----------------------------------------------------[/align]
[align=left]//规则标志位(1表示过滤,0表示放行,你可以通过改这个数值来配置规则)[/align]
[align=left]UINT ICMP = 1; //ICMP数据报规则[/align]
[align=left]UINT IGMP = 0; //IGMP数据报规则[/align]
[align=left]UINT TCP = 0; //TCP数据报规则[/align]
[align=left]UINT UDP = 0; //UDP数据报规则[/align]
[align=left] [/align]
[align=left]//规则判断[/align]
[align=left]if (ICMP == 1)[/align]
[align=left]{[/align]
[align=left]if(((char *)pPacketContent)[12] == 8 && [/align]
[align=left]((char *)pPacketContent)[13] == 0 && [/align]
[align=left]((char *)pPacketContent)[23] == 1)[/align]
[align=left]{[/align]
[align=left]DbgPrint("ICMP被拦截!/n"); [/align]
[align=left]NdisFreeMemory(pPacketContent, 2000, 0);[/align]
[align=left]return NDIS_STATUS_NOT_ACCEPTED;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]if (IGMP == 1)[/align]
[align=left]{[/align]
[align=left]if(((char *)pPacketContent)[12] == 8 && [/align]
[align=left]((char *)pPacketContent)[13] == 0 && [/align]
[align=left]((char *)pPacketContent)[23] == 2)[/align]
[align=left]{[/align]
[align=left]DbgPrint("IGMP被拦截!/n"); [/align]
[align=left]NdisFreeMemory(pPacketContent, 2000, 0);[/align]
[align=left]return NDIS_STATUS_NOT_ACCEPTED;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]if (TCP == 1)[/align]
[align=left]{[/align]
[align=left]if(((char *)pPacketContent)[12] == 8 && [/align]
[align=left]((char *)pPacketContent)[13] == 0 && [/align]
[align=left]((char *)pPacketContent)[23] == 6)[/align]
[align=left]{[/align]
[align=left]DbgPrint("TCP被拦截!/n"); [/align]
[align=left]NdisFreeMemory(pPacketContent, 2000, 0);[/align]
[align=left]return NDIS_STATUS_NOT_ACCEPTED;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]if (UDP == 1)[/align]
[align=left]{[/align]
[align=left]if(((char *)pPacketContent)[12] == 8 && [/align]
[align=left]((char *)pPacketContent)[13] == 0 && [/align]
[align=left]((char *)pPacketContent)[23] == 17)[/align]
[align=left]{[/align]
[align=left]DbgPrint("UDP被拦截!/n"); [/align]
[align=left]NdisFreeMemory(pPacketContent, 2000, 0);[/align]
[align=left]return NDIS_STATUS_NOT_ACCEPTED;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]//规则判断结束[/align]
[align=left]//-----------------------------------------------------[/align]
[align=left] [/align]
[align=left]到这里,PtReceive函数已经修改完了,只要调用PtReceive函数接收数据报的时候就可以执行我们的规则了,修改PtReceivePacket函数,其实上面的修改代码内容都是一样的,只不过PtReceivePacket函数跟PtReceive函数不太一样,PtReceivePacket直接在入口参数中就得到了PNDIS_PACKET的结构Packet,存放了数据报的所有内容,所以,直接将上面的代码粘到函数PtReceivePacket的代码中就可以了。[/align]
[align=left]上面所定义的规则很简单只是作为介绍,其实在pPacketContent已经得到了所有的数据报内容,我们可以任意扩展规则。比如,过滤指定IP、指定端口的数据报。 [/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: