您的位置:首页 > 理论基础 > 数据结构算法

NDIS LWF网络过滤驱动开发(一):LWF简介及数据结构说明

2016-09-27 21:44 615 查看
写在之前:换工作了,做Win驱动开发,还是网络过滤驱动。之前从未接触过这些,只是做着单机的桌面应用程序,所以一切是从头开始。从驱动到网络,很多的不懂,一步步走来,现在多少有些进展了,现在就总结下这段过程.(驱动开发确实麻烦,现在看见蓝屏依然会心惊肉跳)

一.什么是ndis lwf驱动:

NDIS: Network Driver Interface Specification.就是微软的一套网络驱动接口规范,包含好几个部分。LWF只是其中的一小部分,属于过滤型驱动。虽然说它是驱动,其实在安装的时候还是以服务的形式加载。其实网络过滤型驱动有还有其他的方式,如IM中间层驱动,不过因为种种原因,可能是效率方面的考虑吧,微软已不再推荐使用IM驱动,WIN10上IM驱动是无法使用的,所以为了将来考虑,网络过滤型驱动还是选择LWF较好。不过LWF是无法在XP及以下的系统中使用的。

二.:LWF驱动能做什么:

简单说LWF驱动就是过滤网卡上网络数据流,由于是绑定在网卡上,所以得到的数据其实都是以太网的帧。所以得到这些数据后你就可以修改它们再进行传输,或者什么都不干。

LWF驱动在系统中的位置关系如下:



可以看到,lwf是在协议层和网卡驱动之间的驱动。所以在数据传输出去和传输给应用之前它可以随意修改数据。需要说明一下的是,为了统一概念,数据向上传输,指的是数据由物理网卡传输至协议层,数据向下传输,指的是数据由协议层传输至物理网卡。

三.LWF中的数据结构

LWF中用它自己的方式存储以太网中的数据帧,这就是NBL(NET_BUFFER_LIST),也就是在代码中,通过你注册的回调函数的参数系统将数据以PNET_BUFFER_LIST形式会传递给你,这时候你就能做数据过滤的操作了。这是个LIST,其中每个NBL又包含若干个NB(NET_BUFFER),每个NB有包含若干个MDL。真正的数据在这MDL中。其实他们的数据结构都挺复杂的,我没仔细研究过,网上有得到MDL数据的方法,拿来用之。如果想完全弄明白他们中各个成员变量的意义,可以参考http://codemachine.com/article_ndis6nbls.html,写的很详细。我这里大概说下,其实来来回回用的属性也就那些了。



可以看到,系统会传递给你一个NBL,这个LIST可能包含多个NBL。每个NBL中的数据是独立的。所以只需要遍历NBL LIST就行了。

其中,NBL又是有一个或多个NB,以太网的数据帧是以NB为单位存储的。也就是说如果一个TCP包(包含MAC头和IP头)大小为3000Bytes。有可能这一个TCP包放在一个NB中,也有可能会按照你网卡MTU的大小分为若干个包含相同目的地址的TCP包放在一个NBL的不同NB中,每个NB是一个数据帧。分不分包取决于LWF的设置,分包过程是由系统完成的,你收到的NBL是一个正常的数据帧,理论上是不需要你在做任何分包操作的。理解了这些之后就看MDL,其实真正的数据是放在MDL中的,但是这个MDL也是个LIST并且包含有效数据区和无效数据区,要找到有效数据区的起始地址和长度就能得到真正的数据了。可以看到图中的NB.DataOffset就是NB中MDL的有效数据区的起始地址,NB.DadaLength就是有效数据区的长度。

NBL,NB,MDL都是List,得到第一个的地址后通过->Next就可以遍历所有对象了。大致代码如下:

得到一个NB中所有MDL有效数据如下,(次代码是网上拷贝而来)

/*******************************************************************
* GetNetBufferData函数的功能:
* 从1个NET_BUFFER里面获取数据。1个NET_BUFFER里面含有1个或者多个的MDL
*******************************************************************/
VOID GetNetBufferData(PNET_BUFFER NetBuffer, PUCHAR OutputBuffer, ULONG OutputBufferSize, PULONG OutputBytesCopied)
{

PMDL Mdl = NetBuffer->CurrentMdl;
*OutputBytesCopied = 0;

if (NetBuffer->DataLength > OutputBufferSize)
{
return;
}

NdisMoveMemory(OutputBuffer, (PUCHAR)MmGetSystemAddressForMdlSafe(Mdl, LowPagePriority) + NetBuffer->CurrentMdlOffset, Mdl->ByteCount - NetBuffer->CurrentMdlOffset);

OutputBuffer += Mdl->ByteCount - NetBuffer->CurrentMdlOffset;
*OutputBytesCopied += Mdl->ByteCount - NetBuffer->CurrentMdlOffset;

/*
* 循环 MDL链表,获取每一个结点的数据,数据被保存到 OutputBuffer里面OutputBuffer的空间不断地扩大。
* 当链表不为空, 并且 OutputBuffer的长度 < 1个NET_BUFFER的总长度。
*/
while (((Mdl = Mdl->Next) != NULL) && (*OutputBytesCopied < NetBuffer->DataLength))
{
NdisMoveMemory(OutputBuffer, MmGetSystemAddressForMdlSafe(Mdl, LowPagePriority), Mdl->ByteCount);
OutputBuffer += Mdl->ByteCount; /* 数据被保存到 OutputBuffer里面 */
*OutputBytesCopied += Mdl->ByteCount; /* OutputBuffer的空间不断地扩大 */
}

if (Mdl != NULL)
{
NdisMoveMemory(OutputBuffer, MmGetSystemAddressForMdlSafe(Mdl, LowPagePriority), NetBuffer->DataLength);
OutputBuffer += Mdl->ByteCount;
*OutputBytesCopied += Mdl->ByteCount;
}
}遍历NBL及NB
/* Get data from NBL. */
for (PNET_BUFFER_LIST pList = pSrcNetBufferLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
{
for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
{
ULONG bytesCopied = 0;
PUCHAR pOriMDLData = (PUCHAR)NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle, MAX_BUFFER_SIZE, FILTER_ALLOC_TAG, LowPoolPriority);
GetNetBufferData(pBuf, pOriMDLData, MAX_BUFFER_SIZE, &bytesCopied);
}
}这样在你注册的回调函数中就就能解析系统传进来的NBL了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: