利用Winpcap捕获发送数据包
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
利用winpcap捕获数据包、发送数据包
在上一章里面,我们学会了如何获取适配器的相关配置信息,在这一章里面,我们将继续更有意义的内容,就是捕获和发送数据包。
3.1 winpcap捕获数据包流程与相关函数
计算机是通过网卡和网络中其他的主机进行通信的,网卡相当于数据包进出的大门,我们平时讲的数据包的捕获相当于大门的门卫在检查进出的行人一样。在网络基础我们学习过,数据包的发送是一个封装的过程,而数据包的接收则是解封装的过程,但是封装和解封装都是在OS内核来完成的,一般的应用程序没办法获取数据包原始的内容,而Winpcap却能提供这样的功能,在数据链路层捕获数据包,提供最原始的信息。其中Winpcap捕获数据的原理在第一章已经介绍过了,大家可以回顾下。
另外,数据捕获只能捕获通过本主机网卡的数据,没法捕获其他主机上网卡的数据。
下面先看看Winpcap捕获数据时的工作流程。
数据捕获的流程
1. 发现网络设备的函数(find_dev_ex)以前已经介绍过了。
2.打开网卡的函数
打开设备的函数是pcap_open()。下面是参数snaplen, flags 和to_ms 的解释说明
pcap_t* pcap_open ( const char * source, // 指定的网卡的名称
int snaplen, // 帧的长度
int flags, // 网卡捕获的模式
int read_timeout, // 超时
struct pcap_rmtauth * auth, // 是否要求认证
char * errbuf // 错误信息存储
)
snaplen 制定要捕获数据包中的哪些部分。在一些操作系统中(比如xBSD 和Win32),驱动可以被配置成只捕获数据包的初始化部分:这样可以减少应用程序间复制数据的量,从而提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。
flags: 最最重要的flag是用来指示适配器是否要被设置成混杂模式。一般情况下,适配器只接收发给它自己的数据包,而那些在其他机器之间通讯的数据包,将会被丢弃。相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,
我会去捕获所有的数据包。这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他主机的所有的数据包。大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。
to_ms 指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用pcap_dispatch() 或pcap_next_ex()) 都会在to_ms 毫秒时间内响应,即使在网络上没有可用的数据包。在统计模式下,to_ms 还可以用来定义统计的时间间隔。将to_ms 设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。如果设置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。
3.通过回调方式捕获数据的函数
程序功能:打开指定的网卡捕获数据帧,输出数据帧的捕获时间和数据帧的大小等信息。
程序运行结果如下图所示:
代码如下:
函数pcap_loop用户捕获数据,有一个回调参数,packet_handler指向一个可以接收数据包的函数。这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用( 与函数pcap_loop() 和pcap_dispatch() 中的user参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。注意:冗余校验码CRC 不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误
图1 Winpcap捕获数据帧时的过程
其中pcap_pkthdr为Winpcap添加上的头部,在pcap.h头文件中定义,其定义如下:Struct pcap_pkthdr {
struct timeval ts; // 时间
bpf_u_int32 caplen; // 长度
bpf_u_int32 len; // 帧长度
};
其中timeval的结构如下:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
Bpf_u_int32 结构如下:
typedef u_int bpf_u_int32;
为了以后简化相关的程序编写过程,我们对上述程序的相关功能做了一个函数封装,简化了主函数的代码。
主要包含以下3个函数:
(1)pcap_if* GetAllAdapters();
/<**
函数功能:获取本主机网络列表的头指针
参数:
返回值: 函数成功,返回网卡节点的头指针,失败返回NULL
*/
(2) pcap_if* SelectAdapter(pcap_if_t *alldev);
/<**
函数功能: 显示网卡的列表,并获取用户的要选择侦听的网卡
参数: pcap_if *p 指向网卡列表的头指针
返回值: 返回用户选择的网卡的pcap_if指针
*/
(3) pcap_t* OpenAdapter(pcap_if* p, int mode = 0);
/<**
函数功能:打开选定的网卡,可以进行侦听或者在该网卡发送数据
参数: pcap_if* p 网卡列表的头指针
int index 要操作的网卡索引号
int mode 设置网卡的模式, 0表示普通模式,1表示混杂模式,默认为普通模式返回值: 成功返回该网卡的句柄, 失败返回NULL
*/
那么主程序的代码如下: