现在的位置: 首页 > 综合 > 正文

庖丁解牛—winpcap源码彻底解密系列续集(13)

2013年08月03日 ⁄ 综合 ⁄ 共 21023字 ⁄ 字号 评论关闭

 

如何从网卡读取数据包:

Winpcap从网卡获取数据是从NPF_tap开始,NPF_tap是一个回调函数,当网卡有数据包到来时,NDis调用该函数接收数据包,该函数中首先进行了过滤,源码如下;

NDIS_STATUS NPF_tap (IN NDIS_HANDLE ProtocolBindingContext,IN NDIS_HANDLE MacReceiveContext,IN PVOID HeaderBuffer,IN UINT HeaderBufferSize,IN PVOID LookaheadBuffer,IN UINT LookaheadBufferSize,IN UINT PacketSize)

{

     /*参数说明其中,参数ProtocolBindingContext为Ndis的上下文,指向一个OPEN_INSTANCE结构体,标识数据包去往的NPF实例;参数MacReceiveContext为标识生成请求的底层NIC驱动句柄,当使用NdisTransferData函数从NIC驱动传输数据包时该参数必须被使用;参数HeaderBuffer指向NIC驱动中包含数据包头的内存;参数HeaderBufferSize为数据包头的字节长度;参数LookAheadBuffer指向NIC驱动中包含进入的对NPF有用的数据包的内存(前视缓冲区)。该值并不一定需要与数据包的实际大小一致,既然此时只有一部分可能是有用的。余下部分可以通过NDIS库的NdisTransferData函数获取;参数LookaheadBufferSize为前视缓冲区(中可用数据)的字节长度;参数PacketSize为进入的数据包总的大小,除了数据包头外。HeaderBuffer为包头缓冲,为以太网头,LookaheadBuffer为IP头开始的数据缓冲区:*/

    POPEN_INSTANCE      Open;

    PNDIS_PACKET        pPacket;

    ULONG               SizeToTransfer;

    NDIS_STATUS         Status;

    UINT                BytesTransfered;

    ULONG               BufferLength;

    PMDL                pMdl1,pMdl2;

     LARGE_INTEGER      CapTime;

     LARGE_INTEGER      TimeFreq;

     UINT               fres;

     USHORT                 NPFHdrSize;

 

     CpuPrivateData         *LocalData;

     ULONG                  Cpu;

     struct PacketHeader    *Header;

     ULONG                  ToCopy;

     ULONG                  increment;

     ULONG                  i;

     BOOLEAN                ShouldReleaseBufferLock;

 

    IF_VERY_LOUD(DbgPrint("NPF: tap\n");)

     IF_VERY_LOUD(DbgPrint("HeaderBufferSize=%u, LookAheadBuffer=%p, LookaheadBufferSize=%u, PacketSize=%u\n",

     HeaderBufferSize,

     LookaheadBuffer,

     LookaheadBufferSize,

     PacketSize);)

 

     Open= (POPEN_INSTANCE)ProtocolBindingContext;  //获取绑定上下文

    

    Cpu = KeGetCurrentProcessorNumber();       //获取cpu数

     LocalData = &Open->CpuData[Cpu];

 

     LocalData->Received++;

     IF_LOUD(DbgPrint("Received on CPU %d \t%d\n",Cpu,LocalData->Received);)

//   Open->Received++;      // Number of packets received by filter ++

 

     NdisAcquireSpinLock(&Open->MachineLock);    //获取自旋锁

 

     //

     //Check if the lookahead buffer follows the mac header.

     //If the data follow the header (i.e. there is only a buffer) a normal bpf_filter() is

     //executed on the packet.

     //Otherwise if there are 2 separate buffers (this could be the case of LAN emulation or

     //things like this) bpf_filter_with_2_buffers() is executed.

     /*

/*检查前视缓冲区是否紧接着MAC头

*如果数据紧接着该头(比如,只有一个缓冲区),

*在该数据包上执行一个正常的bpf_filte函数。

*否则,如果有两个分开的缓冲区(这可能在LAN仿真或类似的情况下出现),

*就需要执行bpf_filter_with_2_buffers函数。

*/

*/

     if((UINT)((PUCHAR)LookaheadBuffer-(PUCHAR)HeaderBuffer) != HeaderBufferSize)

     {

#ifdef  HAVE_BUGGY_TME_SUPPORT

         fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram),

                                             HeaderBuffer,

                                             LookaheadBuffer,

                                             HeaderBufferSize,

                                             PacketSize+HeaderBufferSize,

                                             LookaheadBufferSize+HeaderBufferSize,

                                             &Open->mem_ex,

                                             &Open->tme,

                                             &G_Start_Time);

#else // HAVE_BUGGY_TME_SUPPORT

         fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram),

                                             HeaderBuffer,

                                             LookaheadBuffer,

                                             HeaderBufferSize,

                                             PacketSize+HeaderBufferSize,

                                             LookaheadBufferSize+HeaderBufferSize);

#endif // HAVE_BUGGY_TME_SUPPORT

     }   

     else

//

// the jit filter is available on x86 (32 bit) only(一个缓冲区的情况, JIT编译器生成的过滤器只在x86(32位)的平台上有效)

//

#ifdef _X86_

 

         if(Open->Filter != NULL)

         {

              if (Open->bpfprogram != NULL)

              {

                   fres=Open->Filter->Function(HeaderBuffer,

                                          PacketSize+HeaderBufferSize,

                                          LookaheadBufferSize+HeaderBufferSize);

NPF_tap函数中Open->Filter->Function实际上调用的就是BPFtoX86函数,执行过滤操作。

              }

              else

                   fres = -1;

         }

         else

#endif //_X86_

     //无JIT编译器生成的过滤器,则调用BPF过滤器

#ifdef HAVE_BUGGY_TME_SUPPORT

              fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),

                         HeaderBuffer,

                            PacketSize+HeaderBufferSize,

                            LookaheadBufferSize+HeaderBufferSize,

                            &Open->mem_ex,

                            &Open->tme,

                            &G_Start_Time);

#else //HAVE_BUGGY_TME_SUPPORT

              fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),

                         HeaderBuffer,

                            PacketSize+HeaderBufferSize,

                            LookaheadBufferSize+HeaderBufferSize);

#endif //HAVE_BUGGY_TME_SUPPORT

 

     NdisReleaseSpinLock(&Open->MachineLock);  //释放锁

 

//

// The MONITOR_MODE (aka TME extensions) is not supported on

// 64 bit architectures

//

#ifdef HAVE_BUGGY_TME_SUPPORT

     if(Open->mode==MODE_MON)

     // we are in monitor mode

     {

         if (fres==1)

         {

              if (Open->ReadEvent != NULL)

              {

                   KeSetEvent(Open->ReadEvent,0,FALSE);

              }

         }

         return NDIS_STATUS_NOT_ACCEPTED;

 

     }

#endif //HAVE_BUGGY_TME_SUPPORT

 

     if(fres==0)

     {

          // Packet not accepted by the filter, ignore it.

         return NDIS_STATUS_NOT_ACCEPTED;

     }

 

     //if the filter returns -1 the whole packet must be accepted

     if(fres == -1 || fres > PacketSize+HeaderBufferSize)

         fres = PacketSize+HeaderBufferSize;

 

     if(Open->mode & MODE_STAT)       //统计模式

     {

     // we are in statistics mode

         NdisAcquireSpinLock( &Open->CountersLock );

         Open->Npackets.QuadPart++;

        

         if(PacketSize+HeaderBufferSize<60)

              Open->Nbytes.QuadPart+=60;

         else

              Open->Nbytes.QuadPart+=PacketSize+HeaderBufferSize;

         // add preamble+SFD+FCS to the packet

         // these values must be considered because are not part of the packet received from NDIS

         Open->Nbytes.QuadPart+=12;

 

         NdisReleaseSpinLock( &Open->CountersLock );

        

         if(!(Open->mode & MODE_DUMP))        //直接保存文件

         {

              return NDIS_STATUS_NOT_ACCEPTED;

         }

     }

 

     if(Open->Size == 0)

     {

         LocalData->Dropped++;

         return NDIS_STATUS_NOT_ACCEPTED;

     }

 

if(Open->mode & MODE_DUMP && Open->MaxDumpPacks)  //其中MaxDumpPacks为最大保存文件的大小

     {

         ULONG Accepted=0;

         for(i=0;i<g_NCpu;i++)

              Accepted+=Open->CpuData[i].Accepted;

        

         if(  Accepted > Open->MaxDumpPacks)

         {

              // Reached the max number of packets to save in the dump file. Discard the packet and stop the dump thread.

              Open->DumpLimitReached = TRUE; // This stops the thread

              // Awake the dump thread

              NdisSetEvent(&Open->DumpEvent);

 

              // Awake the application

              if (Open->ReadEvent != NULL)

                   KeSetEvent(Open->ReadEvent,0,FALSE);  // ReadEvent唤醒应用程序,让应用程序去读数据包

              return NDIS_STATUS_NOT_ACCEPTED;

         }

     }

 

     //////////////////////////////COPIA.C//////////////////////////////////////////77

 

     ShouldReleaseBufferLock = TRUE;

     NdisDprAcquireSpinLock(&LocalData->BufferLock);

 

     do

     {

 

         if (fres + sizeof(struct PacketHeader) > LocalData->Free)

         {

              LocalData->Dropped++;  //缓冲区不够,丢弃

              break;

         }

 

         if (LocalData->TransferMdl1 != NULL)

         {

              //

              //if TransferMdl is not NULL, there is some TransferData pending (i.e. not having called TransferDataComplete, yet)

              //in order to avoid buffer corruption, we drop the packet

              //

              LocalData->Dropped++;  //调用时为避免数据覆盖,丢弃该包

              break;

         }

 

 

         if (LookaheadBufferSize + HeaderBufferSize >= fres)

         {

 

              //

              // we do not need to call NdisTransferData, either because we need only the HeaderBuffer, or because the LookaheadBuffer

              // contains what we need

              //

 

              Header = (struct PacketHeader*)(LocalData->Buffer + LocalData->P);

              LocalData->Accepted++;

              GET_TIME(&Header->header.bh_tstamp,&G_Start_Time);

              Header->SN = InterlockedIncrement(&Open->WriterSN) - 1;

 

              Header->header.bh_caplen = fres;

              Header->header.bh_datalen = PacketSize + HeaderBufferSize;

              Header->header.bh_hdrlen=sizeof(struct bpf_hdr);

 

              LocalData->P +=sizeof(struct PacketHeader);

              if (LocalData->P == Open->Size)

                   LocalData->P = 0;

 

              if ( fres <= HeaderBufferSize || (UINT)( (PUCHAR)LookaheadBuffer - (PUCHAR)HeaderBuffer ) == HeaderBufferSize )

              {

                  //

                   //we can consider the buffer contiguous, either because we use only the data

                   //present in the HeaderBuffer, or because HeaderBuffer and LookaheadBuffer are contiguous

                   // ;-))))))

                   //

                   if (Open->Size - LocalData->P < fres)

                   {

                       //the packet will be fragmented in the buffer (aka, it will skip the buffer boundary)

                       //two copies!!

                       ToCopy = Open->Size - LocalData->P;

                       NdisMoveMappedMemory(LocalData->Buffer + LocalData->P,HeaderBuffer, ToCopy);

                       NdisMoveMappedMemory(LocalData->Buffer + 0 , (PUCHAR)HeaderBuffer + ToCopy, fres - ToCopy);

                       LocalData->P = fres-ToCopy;

                   }

                   else

                   {

                       //the packet does not need to be fragmented in the buffer (aka, it doesn't skip the buffer boundary)

                       // ;-)))))) only ONE copy

                       NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, fres);

                       LocalData->P += fres;

                   }

              }

              else

              {

                   //HeaderBuffer and LookAhead buffer are NOT contiguous,

                   //AND, we need some bytes from the LookaheadBuffer, too

                   if (Open->Size - LocalData->P < fres)

                   {

                       //the packet will be fragmented in the buffer (aka, it will skip the buffer boundary)

                       if (Open->Size - LocalData->P >= HeaderBufferSize)

                       {

                            //HeaderBuffer is NOT fragmented

                            NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, HeaderBufferSize);

                            LocalData->P += HeaderBufferSize;

 

                            if (LocalData->P == Open->Size)

                            {

                                 //the fragmentation of the packet in the buffer is the same fragmentation

                                 //in HeaderBuffer+LookaheadBuffer

                                 LocalData->P=0;   

                                 NdisMoveMappedMemory(LocalData->Buffer + 0, LookaheadBuffer, fres - HeaderBufferSize);

                                 LocalData->P += (fres - HeaderBufferSize);

                            }

                            else

                            {

                                //LookAheadBuffer is fragmented, two copies

                                 ToCopy = Open->Size - LocalData->P;

                                 NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, ToCopy);

                                 LocalData->P=0;

                                 NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)LookaheadBuffer+ ToCopy, fres - HeaderBufferSize - ToCopy);

                                 LocalData->P = fres - HeaderBufferSize - ToCopy;

                            }

                       }

                       else

                       {

                            //HeaderBuffer is fragmented in the buffer (aka, it will skip the buffer boundary)

                            //two copies to copy the HeaderBuffer

                            ToCopy = Open->Size - LocalData->P;

                            NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, ToCopy);

                            LocalData->P = 0;

                            NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)HeaderBuffer + ToCopy, HeaderBufferSize - ToCopy);

                            LocalData->P = HeaderBufferSize - ToCopy;

 

                            //only one copy to copy the LookaheadBuffer

                            NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, fres- HeaderBufferSize);

                            LocalData->P += (fres - HeaderBufferSize);

                       }

                   }

                   else

                   {   

                       //the packet won't be fragmented in the destination buffer (aka, it won't skip the buffer boundary)

                       //two copies, the former to copy the HeaderBuffer, the latter to copy the LookaheadBuffer

                       NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, HeaderBufferSize);

                       LocalData->P += HeaderBufferSize;

                       NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, fres - HeaderBufferSize);

                       LocalData->P += (fres - HeaderBufferSize);

                   }       

              }       

 

              increment = fres + sizeof(struct PacketHeader);

              if (Open->Size - LocalData->P < sizeof(struct PacketHeader))  //we check that the available, AND contiguous, space in the buffer will fit

              {                                                                   //the NewHeader structure, at least, otherwise we skip the producer

                   increment += Open->Size-LocalData->P;                 //at the beginning of the buffer (p = 0), and decrement the free bytes appropriately

                   LocalData->P = 0;

              }

 

              InterlockedExchangeAdd(&LocalData->Free, (ULONG)(-(LONG)increment));

              if(Open->Size - LocalData->Free >= Open->MinToCopy)

              {

                   if(Open->mode & MODE_DUMP)

                       NdisSetEvent(&Open->DumpEvent);

                   else

                   {       

                       if (Open->ReadEvent != NULL)

                       {

                            KeSetEvent(Open->ReadEvent,0,FALSE);

                       }

                   }

              }

 

              break;

         }

         Else   //读取数据

         {

              IF_LOUD(DbgPrint("TransferData!!\n");)

                   //ndisTransferData required

                   LocalData->NewP = LocalData->P;

 

              LocalData->NewP +=sizeof(struct PacketHeader);

              if (LocalData->NewP == Open->Size)

                   LocalData->NewP = 0;

 

              //first of all, surely the header must be copied

              if (Open->Size-LocalData->NewP >= HeaderBufferSize)

              {

                   //1 copy!

                   NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, HeaderBuffer, HeaderBufferSize);

                   LocalData->NewP += HeaderBufferSize;

                   if (LocalData->NewP == Open->Size)

                       LocalData->NewP = 0;

              }

              else

              {

                   ToCopy = Open->Size - LocalData->NewP;

                   NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, HeaderBuffer, ToCopy);

                   NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)HeaderBuffer + ToCopy, HeaderBufferSize - ToCopy);

                   LocalData->NewP = HeaderBufferSize - ToCopy;

              }

 

              //then we copy the Lookahead buffer

              if (Open->Size-LocalData->NewP >= LookaheadBufferSize)

              {

                   //1 copy!

                   NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, LookaheadBuffer, LookaheadBufferSize);

                   LocalData->NewP += LookaheadBufferSize;

                   if (LocalData->NewP == Open->Size)

                       LocalData->NewP = 0;

              }

              else

              {

                   ToCopy = Open->Size - LocalData->NewP;

                   NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, LookaheadBuffer, ToCopy);

                   NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)LookaheadBuffer + ToCopy, LookaheadBufferSize - ToCopy);

                   LocalData->NewP = LookaheadBufferSize - ToCopy;

              }

 

              //Now we must prepare the buffer(s) for the NdisTransferData

              if ((Open->Size - LocalData->NewP) >= (fres - HeaderBufferSize - LookaheadBufferSize))

              {

                   //only 1 buffer

                   pMdl1 = IoAllocateMdl(

                       LocalData->Buffer + LocalData->NewP,

                       fres - HeaderBufferSize - LookaheadBufferSize,

                       FALSE,

                       FALSE,

                       NULL);

 

                   if (pMdl1 == NULL)

                   {

                       IF_LOUD(DbgPrint("Error allocating Mdl1\n");)

                            LocalData->Dropped++;

                       break;

                   }

 

                   MmBuildMdlForNonPagedPool(pMdl1);

                   pMdl2=NULL;

                   LocalData->NewP += fres - HeaderBufferSize - LookaheadBufferSize;

 

 

              }

              else

              {

                   //2 buffers

                   pMdl1 = IoAllocateMdl(

                       LocalData->Buffer + LocalData->NewP,

                       Open->Size - LocalData->NewP,

                       FALSE,

                       FALSE,

                       NULL);

 

                   if (pMdl1 == NULL)

                   {

                       IF_LOUD(DbgPrint("Error allocating Mdl1\n");)

                            LocalData->Dropped++;

                       break;

                   }

 

                   pMdl2 = IoAllocateMdl(

                       LocalData->Buffer + 0,

                       fres - HeaderBufferSize - LookaheadBufferSize - (Open->Size - LocalData->NewP),

                       FALSE,

                       FALSE,

                       NULL);

 

                   if (pMdl2 == NULL)

                   {

                       IF_LOUD(DbgPrint("Error allocating Mdl2\n");)

                            IoFreeMdl(pMdl1);

                       LocalData->Dropped++;

                       break;

                   }

 

                   LocalData->NewP = fres - HeaderBufferSize - LookaheadBufferSize - (Open->Size - LocalData->NewP);

 

                   MmBuildMdlForNonPagedPool(pMdl1);

                   MmBuildMdlForNonPagedPool(pMdl2);

              }

 

 

              NdisAllocatePacket(&Status, &pPacket, Open->PacketPool);

 

              if (Status != NDIS_STATUS_SUCCESS)

              {

                   IF_LOUD(DbgPrint("NPF: Tap - No free packets\n");)

                       IoFreeMdl(pMdl1);

                   if (pMdl2 != NULL)

                       IoFreeMdl(pMdl2);

                   LocalData->Dropped++;

                   break;

              }

 

              if (pMdl2 != NULL)

                   NdisChainBufferAtFront(pPacket,pMdl2);

 

              NdisChainBufferAtFront(pPacket,pMdl1);

 

              RESERVED(pPacket)->Cpu = Cpu;

 

              LocalData->TransferMdl1 = pMdl1;

              LocalData->TransferMdl2 = pMdl2;

 

 

              Header = (struct PacketHeader*)(LocalData->Buffer + LocalData->P);

              Header->header.bh_caplen = fres;

              Header->header.bh_datalen = PacketSize + HeaderBufferSize;

              Header->header.bh_hdrlen=sizeof(struct bpf_hdr);

              //从网卡读取数据

              NdisTransferData(

                   &Status,

                   Open->AdapterHandle,

                   MacReceiveContext,

                   LookaheadBufferSize,

                   fres - HeaderBufferSize - LookaheadBufferSize,

                   pPacket,

                   &BytesTransfered);

 

              if (Status != NDIS_STATUS_PENDING)

              {

                   IF_LOUD(DbgPrint("NdisTransferData, not pending!\n");) 

                       LocalData->TransferMdl1 = NULL;

                   LocalData->TransferMdl2 = NULL;

 

                   IoFreeMdl(pMdl1);

                   if ( pMdl2 != NULL )

                       IoFreeMdl(pMdl2);

 

                   NdisReinitializePacket(pPacket);

                   // Put the packet on the free queue

                   NdisFreePacket(pPacket);

 

                   LocalData->P = LocalData->NewP;

 

                   LocalData->Accepted++;

                   GET_TIME(&Header->header.bh_tstamp,&G_Start_Time);

                   Header->SN = InterlockedIncrement(&Open->WriterSN) - 1;

 

                   increment = fres + sizeof(struct PacketHeader);

                   if (Open->Size - LocalData->P < sizeof(struct PacketHeader))

                   {

                       increment += Open->Size-LocalData->P;

                       LocalData->P = 0;

                   }

 

                   InterlockedExchangeAdd(&LocalData->Free, (ULONG)(-(LONG)increment));

 

                   if(Open->Size - LocalData->Free >= Open->MinToCopy)

                   {

                       if(Open->mode & MODE_DUMP)

                            NdisSetEvent(&Open->DumpEvent);

                       else

                       {

                            if (Open->ReadEvent != NULL)

                            {

                                 KeSetEvent(Open->ReadEvent,0,FALSE);

                            }

                       }

                   }

 

                   break;

              }

              else

              {

                   IF_LOUD(DbgPrint("NdisTransferData, pending!\n");)

                       ShouldReleaseBufferLock = FALSE;

              }

         }

     }

     while(FALSE);

 

     if (ShouldReleaseBufferLock)

     {

         NdisDprReleaseSpinLock(&LocalData->BufferLock);

     }

 

     return NDIS_STATUS_NOT_ACCEPTED;

 

}

 

从winpcap源码中可以看到,内核通过NpF_tap从网卡获取数据包,同时进行过滤,当数据包的大小大于mintocopy,

     KeSetEvent(Open->ReadEvent,0,FALSE); ,这样ReadEvent处于有信号状态,这样应用程序就可以通过ReadFile读取数据包了。

【上篇】
【下篇】

抱歉!评论已关闭.