WinPcap教程(3):打开网卡抓包

前篇:WinPcap教程(2):获取设备高级信息

我们已经知道如何获取网卡信息了,现在让我们开始进行真正的开发工作,即打开一个网卡并抓取数据包。本文中,我们将写一个程序来打印出流经网卡的每个包的相关信息。

打开抓包设备的函数是pcap_open(),该函数的参数中,snaplenflagsto_ms这三个值得解释一下:

snaplen

该参数设定了包需要抓取的部分。在一些操作系统上(xBSDWin32),包驱动程序可以配置成只抓取包的初始部分:这降低了应用拷贝数据的数量,从而提高扎包的效率。在我们使用高于我们可能碰到的最大MTU(Maximum Transmission Unit,最大传输单元,以字节为单位) 65536的情况下,这将确保应用总是接收到包的全部。

flags

最重要的标致应该是否将网卡设置成混杂模式(promiscuous mode)。在正常模式下,网卡只抓取来自网络的,以它为目的地的数据包,其他机器之间交换的包将被忽略。而在混杂模式下,将抓取所有的包,而不管该网卡是不是这些包的目的地。这意味着在共享介质(如未经交换的以太网,non-switched Ethernet)的情况下,WinPcap能够抓取到其他机器的包。多数抓包应用,混杂模式都是缺省的模式,下面的示例程序中,我们也采用混杂模式。

to_ms

该参数指定了读超时,以毫秒为单位。从网卡读数据时(比如,用pcap_dispatch()或者pcap_next_ex()),如果网络上无包可读,超时后(即过了to_ms毫秒后)总会返回。to_ms也定义了统计报告的时间间隔。将to_ms设为0,表示无超时,即在读网卡时,如果没有数据包到来,将永不返回;另一方面,如果设为-1,会使得对网卡的读操作立即返回。

#include "pcap.h"

/* prototype of the packet handler */

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int main()

{

pcap_if_t *alldevs;

pcap_if_t *d;

int inum;

int i=0;

pcap_t *adhandle;

char errbuf[PCAP_ERRBUF_SIZE];

/* Retrieve the device list on the local machine */

if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)

{

fprintf(stderr,"Error in pcap_findalldevs: %s/n", errbuf);

exit(1);

}

/* Print the list */

for(d=alldevs; d; d=d->next)

{

printf("%d. %s", ++i, d->name);

if (d->description)

printf(" (%s)/n", d->description);

else

printf(" (No description available)/n");

}

if(i==0)

{

printf("/nNo interfaces found! Make sure WinPcap is installed./n");

return -1;

}

printf("Enter the interface number (1-%d):",i);

scanf_s("%d", &inum);

if(inum < 1 || inum > i)

{

printf("/nInterface number out of range./n");

/* Free the device list */

pcap_freealldevs(alldevs);

return -1;

}

/* Jump to the selected adapter */

for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

/* Open the device */

if ( (adhandle= pcap_open(d->name, // name of the device

65536, // portion of the packet to capture

// 65536 guarantees that the whole packet will be captured on all the link layers

PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode

1000, // read timeout

NULL, // authentication on the remote machine

errbuf // error buffer

) ) == NULL)

{

fprintf(stderr,"/nUnable to open the adapter. %s is not supported by WinPcap/n", d->name);

/* Free the device list */

pcap_freealldevs(alldevs);

return -1;

}

printf("/nlistening on %s.../n", d->description);

/* At this point, we don't need any more the device list. Free it */

pcap_freealldevs(alldevs);

/* start the capture */

pcap_loop(adhandle, 0, packet_handler, NULL);

return 0;

}

/* Callback function invoked by libpcap for every incoming packet */

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)

{

struct tm ltime;

char timestr[16];

time_t local_tv_sec;

/*

* unused variables

*/

(VOID)(param);

(VOID)(pkt_data);

/* convert the timestamp to readable format */

local_tv_sec = header->ts.tv_sec;

localtime_s(&ltime, &local_tv_sec);

strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

printf("%s,%.6d len:%d/n", timestr, header->ts.tv_usec, header->len);

}

打开网卡后,就可以用pcap_dispatch()或者pcap_loop()开始抓包了。这两个函数非常相似,不同的是当超时到了后,pcap_dispatch()会返回(尽管不能保证),而pcap_loop()则不会返回,直到抓取了cnt个包(译注:cntpcap_loop()函数的一个参数,其含义见pcap_loop()的原型),因此在比较空闲网络情况下,会被阻塞很长时间。在本例中,pcap_loop()就够用了,pcap_dispatch()通常用于更复杂的程序。

这两个函数都各有一个callback参数,即packet_handler,它指向用于处理包数据的函数。每当有新的包从网络上到达时,该函数就会自动被libpcap调用,以接收一个通用状态(generic status,和pcap_loop()pcap_dispatch()中的user参数对应);一个含有信息包头,诸如时间戳和长度;以及包括协议头在内的真正包数据。注意CRC帧一般不提供,因为网卡在帧完整性验证后会将其删除。另外,大多数网卡在收到错误的CRC后,会将相应的包丢弃。因此,这些被丢弃的包,WinPcap是抓取不到的。

上面的例子从pcap_pkthdr头提取了时间戳和每个包的长度,并打印在屏幕上。

请注意到,上面的pcap_loop()或许存在一个不足之处,主要是基于这样一个事实:处理函数(handler)是由抓包驱动程序调用的,因此用户的应用程序不能直接控制它。另外一个方法就是使用pcap_next_ex()函数,将在下一个示例程序说明。

后篇:待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值