WinPcap权威指南(二)
上一节我们简单介绍了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;
附件下载:
本节代码
本节仅简单的解释了ARP和UDP协议,下一节再结合发包深入网络的连接过程。例如:ARP的欺骗、路由器的实质、数据的修改等。
分类:网络相关
发表评论
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 for comments on this post.