计算机语言:[转帖][翻译][NDIS]NDIS_PACKET结构讨论[一]

来源:百度文库 编辑:九乡新闻网 时间:2024/07/14 07:24:32

这篇文章的目的是探讨一下在网络上截取的包(如IP包)与在NDIS驱动中代表相同内容的NDIS_PACKET之间的关系。

 

标准化组织:

我们经常在新闻组上看到如下的内容:

         1.微软的开发文档是如何描述Window2000网络包的?

         2.有谁知道在哪可以找到Window2000 IP包的详细描述?

         3.我想知道NDIS_PACKET的基本结构,例如:我想知道哪部分是源IP地址,哪部分是端口号与数据等

 

这些问题可以分以下两个独立问题:

1.在哪儿可以找到网络协议的详细说明?如IP协议

2.网络数据是怎样封装在NDIS_PACKET中的?

第一个问题的答案是:广泛使用的协议是标准化组织制定的,不是微软。以下列出一些标准化组织(这些组织读者自己去找中文翻译吧):

         ETSI - European Telecommunications Standards Institute

        IEEE - Institute of Electrical and Electronics Engineers

        IETF - Internet Engineering Task Force

        IPv6 - IPv6 Forum

        ISO - International Standards Organization

        OMG - Object Management Group Open Group

        World Wide Web Consortium 

IP协议是世界上广泛使用的网络协议。IP的详细说明由IETF来维护。IETF是由网络设计者,使用者,厂商,研究人员组成的一个开放组织,他们共同关注网络的结构以及网络的顺畅运行,它对所有感兴趣的个人开放。

         关于IP协议的相关信息可以在IETF的网站上找到:RFC791

你应该访问该网站来获取其它协议的相关信息。

 

当然,IP不是网络的唯一协议,IETF也不是唯一的标准化机构。那么,我们还能在哪儿找到相关信息呢?

 

就近的网站有Protocols.com,它们发布了一个协议目录,其中包括了各种协议及它们的标准化机构。

 

接下来我们来看看数据是怎样封装在NDIS_PACKET结构中的。

 

网络上的数据包:

接下来我们来真正理解一下在网络上观测到的数据包。下面是一个ICMP包的HEX Dump

000000: 00 A0 CC 63 08 1B 00 40 : 95 49 03 5F 08 00 45 00 ...c...@.I._..E.
000010: 00 3C 82 47 00 00 20 01 : 94 C9 C0 A8 01 20 C0 A8 .<.G.. ...... ..
000020: 01 40 08 00 48 5C 01 00 : 04 00 61 62 63 64 65 66 .@..H\....abcdef
000030: 67 68 69 6A 6B 6C 6D 6E : 6F 70 71 72 73 74 75 76 ghijklmnopqrstuv
000040: 77 61 62 63 64 65 66 67 : 68 69                   wabcdefghi......

Ping是由下面的命令发起的:

C:> ping 192.168.1.64

发送ICMP回显请求,带32字节的数据。Ping的总长度是74字节,上面的数据中没包含帧前导(用于同步的部分)以及FCS(帧校验和)。

 

以上是用PCAUSA Rawether for Windows HookPeek程序捕获的,HOOKPEER不是网络监控程序,但它有这方面的功能。

 

PING包的解包在下面的链接中有详细说明,可以作为参考:

         http://www.ndis.com/papers/ndispacket/ndispacket_decode.htm

 

数据包的NDIS表示:

 

当然,我们感兴趣的信息是包数据。即包含有74字节VM(虚拟内存)的地方,它表示的是在网络上观察到的74字节的数据。首先,NDIS用来管理包数据的机制看起来似乎有点复杂而且不必要。但是,这种基础的机制是经常深思熟虑的,它给编写协议的作者提供了许多灵活性。

 

NDIS_PACKET的简单表示

 

通常,最好将NDIS_PACKET结构(与之关联的NDIS_BUFFER结构也一样)认为是“透明”的-除了一些在以后将要讨论的特殊保留域。这意味着在结构中定义的域不能直接访问,并且这些结构可能随着NDIS的版本不同而不同。

 

然而,对这些结构以及它们与包数据是如何关联的有一个大概的了解是很有用的。

如下的图表向我们展示了NDIS_PACKET封装包数据的最简单的方法:

        

1.一个简单NDIS_PACKET展示

 

在这种简单情形下,所有的74字节的包数据均位于连续的74字节的数组中。

NDIS_PACKET包描述:

NDIS_PACKET包有一个链接的NDIS_BUFFERNDIS_BUFFER描述了一个包含了所有包数据的74字节的虚拟内存范围。

 

NDIS_PACKET更复杂的表示

 

如下的图表向展示了NDIS_PACKET封装包数据的另一种方法。

2:多缓冲区的NDIS_PACKET展示

 

在这种情形下,包数据分布在两个分开的虚拟内存范围中。

以下是第二种NDIS_PACKET包的描述:

NDIS_PACKET包拥有两个链接的NDIS_BUFFER,第一个NDIS_BUFFER描述了一个14字节的虚拟内存范围,它包含了Ethernet头。第二个NDIS_BUFFER描述了一个60字节范围的虚拟内存,它包含了Ethernet的负载(净荷域)。

 

很明显,没一种关于NDIS_PACKET包构成的安全假设。包结构首先是由构建包的软件来决定的。

 

理解NDIS_PACKET

 

尽管以上的图表给我们提供了一个NDIS_PACKET包是如何使用的画面,但是你必须记住,这些结构是透明的(即你最好别改它)。你决不能直接访问这些结构的某些域。相反,你应该使用NDIS包提供的访问函数。

 

在接下来的话题中,我们讨论部分NDIS库函数。我们的目光主要集中在那一小部分函数,如果给你一个包让你检查,你就可以使用这些函数来检查和解释包数据。

 

你用来检查NDIS_PACKET和与之相链接的NDIS_BUFFER的函数仅有一小部分,它们是:

NdisQueryPacket-  返回给定的一个包描述符的信息。

BufferCount – 包描述符上的缓冲区描述符个数

FirstBuffer – 指向链接在包描述上的第一个缓冲区描述符。

TotalPacketLength – 所有链接的缓冲区描述符所映射的包数据总数。

NdisQueryBuffer 返回所给的缓冲区描述符的相关信息

VirtualAddress – 缓冲区描述符所描述的地址范围的基地址指针

Length – 缓冲区描述符所描述的地址范围中所包含的字节数。


NdisGetNextBuffer – 提供当前缓冲区描述符指针,返回链上的下一个缓冲区描述

NDIS 5.1 注:NDIS 5.1Windows XP)引入几个NDIS函数的安全版本。这些函数的安全版本只能用在NDIS 5.1驱动中。

NdisQueryBufferSafe –NdisQueryBuffer的安全版本

 

以下是如何用NdisQueryPacket函数来检查pNdisPacket指针所指向的NDIS_PACKET

   PNDIS_BUFFER    pCurrentBuffer;
   UINT            nBufferCount, TotalPacketLength;
   //
   // Query Packet
   //
   NdisQueryPacket(
      (PNDIS_PACKET )pNdisPacket,
      (PUINT )NULL,           // Physical Buffer Count
      (PUINT )&nBufferCount,  // Buffer Count
      &pCurrentBuffer,        // First Buffer
      &TotalPacketLength      // TotalPacketLength
      );

这段代码先获取链在包上的NDIS_BUFFER的个数和通过缓冲区描述符映射的包数据长度。同时,它还取得了指向第一个NDIS_BUFFER的指针。

NdisQueryBuffer函数可以用来从NDIS_BUFFER中提取信息,如下所示:

          PUCHAR    VirtualAddress;
   UINT      CurrentLength, CurrentOffset;
   //
   // Query The First Buffer
   //
   NdisQueryBuffer(
      CurrentBuffer,
      &VirtualAddress,
      &CurrentLength
      );

以上代码获取缓冲区描述符所描述的虚拟内存的长度和虚拟地址。可以通过VirtualAddress指针,像操作普通无符号字符数组那样来修改内存的内容(VirtualAddress所指)。

 

但是,我们必须明白,有可能第一个缓冲区指针并不包含所有的包数据。如果NDIS_PACKET是如上图一所示的简单情形,那么CurrentLength应为74,所有的数据都在VirtualAddress所指的内存中。

如果pNdisPacket指向的是如图二所示的包,那情况将有所不同。NdisQueryPacket返回的TotalPacketLength就该为74;但是,当用NdisQueryPacket查询第一个缓冲区时返回的应该是14

这就意味着我们必须检查链接在包描述符上的其它缓冲区,以便查看余下的包数据。这时我们可以使用NdisGetNextBuffer 来获取下一个缓冲区描述符。这种迭代方法(有时也叫做遍历缓冲区链)是检查包数据所必须的。

 

UTIL_ReadOnPacket 函数展示了通过任一包描述符指针来遍历缓冲区链表读数据的方法。PCAUSA用这个函数来安全“PEEK”示知结构的包描述符。它可能不是所有情况下的最优的方法;例如,如果你试图拷贝一大片的网络数据(如:在MTU较大的网络上的IP包),那么需要一个更有效的函数来完成。

你可以通过这个链接引用该篇文章:http://feikoo.bokee.com/viewdiary.10774705.html