WinPcap权威指南(二)

2014-08-03 admin

        上一节我们简单介绍了WinPcap的一些基础知识,同时也枚举到所有网卡的设备名称,现在我们就可以操作网卡了。WinPcap有一个结构叫TADAPTER,你可以把它想象为一个句柄,我们平时操作文件,一般是先打开/创建一个文件,如果成功,则返回一个句柄,然后读文件就可以使用ReadFile,写文件可以使用WriteFile,操作完毕后,CloseHandle关闭这个句柄,WinPcap下的操作也是类似的:

1、打开一个网卡,返回句柄:function PacketOpenAdapter(AdapterName: PAnsiChar): LPADAPTER;

        其中AdapterName就是上一节获取的网卡设备名称,打开成功则返回该网卡的句柄,失败返回nil。一般地说,打开句柄后,还可以做一些初始化工作。例如:调用PacketSetHwFilter将网卡设置为混杂模式(只有网卡处于混杂模式,才可以获取不是发送给本机的数据);调用PacketSetBuff设置缓冲区大小;调用PacketSetReadTimeout设置接收超时。具体请参考演示代码。

2、从网卡获取数据:function PacketReceivePacket(AdapterObject: LPADAPTER; pPacket: LPPACKET; Sync: Byte): Byte;

        AdapterObject: 就是网卡的句柄,pPacket则对应一个TPACKET结构的指针。一般是先通过函数PacketAllocatePacket来分配一个pPacket,再调用PacketInitPacket将自己的接收缓冲区和它关联,使用完毕后调用PacketFreePacket释放掉。对应的WinPcap内部实现如下:


function PacketAllocatePacket(): LPPACKET;
var
  pPacket: LPPACKET;
begin
  pPacket := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_ZEROINIT, sizeof(TPACKET));
  if (pPacket = nil) then
  begin
    //TRACE_PRINT("PacketAllocatePacket: GlobalAlloc Failed");
  end;

  Result := pPacket;
end;

procedure PacketInitPacket(var pPacket: LPPACKET; Buffer: Pointer; Length: UINT);
begin
  //TRACE_ENTER("PacketInitPacket");

  pPacket^.Buffer := Buffer;//关联用户缓冲区
  pPacket^.Length := Length;//用户缓冲区大小
  pPacket^.ulBytesReceived := 0;
  pPacket^.bIoComplete := 0;

  //TRACE_EXIT("PacketInitPacket");
end;

procedure PacketFreePacket(var pPacket: LPPACKET);
begin
  //TRACE_ENTER("PacketFreePacket");
  GlobalFreePtr(pPacket);
  //TRACE_EXIT("PacketFreePacket");
end;

3、通过网卡发送数据:function PacketSendPacket(AdapterObject: LPADAPTER; pPacket: LPPACKET; Sync: Byte): Byte;

4、关闭网卡句柄:procedure PacketCloseAdapter(lpAdapter: LPADAPTER);

        我们的程序使用了两个线程来处理数据,其中TRecvPackThread用于从网卡获取数据,然后添加到全局的g_List;而TAnalysePacketsThread则从g_List里面取出数据来处理。为了界面更友好,再添加一个TTimer来获取WinPcap的内部状态。

        现在我们获取了数据,但这个数据就是一个指针,里面是什么东西呢?其实跟我们的《张曼玉与指针》里面说的,数据就是一切,你说它是什么,它就是什么。实际上,我们捕获的都是以太网帧数据,帧头结构如下:

type
  _ETHERNET_HDR = packed record
    DestMac: array[0..5] of Byte; //目的MAC地址
    SourceMac: array[0..5] of Byte; //源MAC地址
    EthernetType: Word; //帧类型
  end;
  TEthernetHeader = _ETHERNET_HDR;
  LPEthernetHeader = ^_ETHERNET_HDR;

        实际上,不管上层是什么协议,到了底层,都会封装为以太帧,然后发送出去。例如,TCP协议,是基于IP协议的,那么到了底层,实质上会封装为:以太帧头+IP头+TCP头+实际数据(如果存在)。所以我们在这里根据EthernetType判断是什么类型的帧,再作进一步解释:

    pEthernetHeader := LPEthernetHeader(pBuffer);
    case ntohs(pEthernetHeader^.EthernetType) of //判断以太帧类型
      ETHERTYPE_ARP: ProcessARPPacket(LPARPPacket(pBuffer));
      ETHERTYPE_IP: //IP帧
        begin
          pIPHeader := LPIPHeader(pBuffer + sizeof(TEthernetHeader)); //判断是TCP、UDP还是ICMP等
          case pIPHeader^.Protocol of
            IPPROTO_TCP: ProcessTCPPacket(LPTCPPacket(pBuffer));
            IPPROTO_UDP: ProcessUDPPacket(LPUDPPacket(pBuffer));
          end;
        end;
    end;

winpcap2

附件下载:
本节代码

        本节仅简单的解释了ARP和UDP协议,下一节再结合发包深入网络的连接过程。例如:ARP的欺骗、路由器的实质、数据的修改等。

分类:网络相关

发表评论

(必填)

(必填), (Hidden)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

TrackBack URL  |  这篇文章上的评论的RSS feed


近期文章

近期评论

文章归档

分类目录