赞
踩
上一节我们简单介绍了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内部实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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的内部状态。
现在我们获取了数据,但这个数据就是一个指针,里面是什么东西呢?其实跟我们的《张曼玉与指针》里面说的,数据就是一切,你说它是什么,它就是什么。实际上,我们捕获的都是以太网帧数据,帧头结构如下:
1
2
3
4
5
6
7
8
|
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判断是什么类型的帧,再作进一步解释:
1
2
3
4
5
6
7
8
9
10
11
12
|
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的欺骗、路由器的实质、数据的修改等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。