winpcap编程函数介绍

2023-05-16

1. int pcap_findalldevs(pcap_if_t **, char *)
说明:用来获得网卡的列表
参数: 指向pcap_if_t**类型的列表的指针的指针; char型指针,当打开列表错误时返回错误信息
返回值: 为int型,当显示列表失败时返回-1
pcap_if_t 是pcap_if 重命名而来:typedef struct pcap_if pcap_if_t;
pcap_if结构体如下:

struct pcap_if
{
	struct pcap_if *next; 		/*多个网卡时使用来显示各个网卡的信息*/
	char *name;			/* name to hand to "pcap_open_live()" */
	char *description; 		/* textual description of interface, or NULL 就是网卡的型号、名字等*/
	struct pcap_addr *addresses;	/*pcap_addr 结构体 */
 	bpf_u_int32 flags; 		/* PCAP_IF_ interface flags 接口标志*/
};

pcap_addr 结构体如下:

struct pcap_addr
{
	struct pcap_addr *next;
	struct sockaddr *addr; 		/* address */
	struct sockaddr *netmask; 		/* netmask for that address 子网掩码*/
	struct sockaddr *broadaddr; 	/* broadcast address for that address 广播地址*/
	struct sockaddr *dstaddr; 		/* P2P destination address for that address P2P目的地址*/
};

举例:

pcap_if_t *alldevs;
pcap_if_t *d;
char errbuf[64];
if (pcap_findalldevs(&alldevs, errbuf) == -1)/* 这个API用来获得网卡的列表*/
{
	fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
	exit(1);
}
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");
}

用pcap_findalldevs不能获得网卡的MAC,有两种方法可以实现,一、向自己发送arp包,二、使用IPHelp的API可以获得。
2. void pcap_freealldevs(pcap_if_t *)
说明:与int pcap_findalldevs(pcap_if_t **, char *)配套使用,当不再需要网卡列表时,用此函数free释放空间
参数:打开网卡列表时申请的pcap_if_t型的指针
举例:

pcap_freealldevs(alldevs);

3. pcap_t *pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char ebuf *)
说明:被用来得到一个包抓取得描述符
参数:
device是一个指出要抓取的网络设备的字符串。
snaplen指明最大可抓取的字节长度。
promisc置位表明该接口要被设置成混杂模式。
to_ms以毫秒为单位设置超时时间。当在超时时间内网卡上没有数据到来时对网卡的读操作将返回(如 pcap_dispatch() or pcap_next_ex()等函数)。
ebuf被用来存放当pcap_open_live()调用失败时,返回的错误字符串。
返回值: pcap_t型的指针,供pcap_dispatch() or pcap_next_ex()等函数调用。
pcap_t的结构体:

struct pcap {
	#ifdef WIN32
		ADAPTER *adapter;
		LPPACKET Packet;
		int timeout;
		int nonblock;
	#else
		int fd;
	#endif
	int snapshot;
	int linktype;
	int tzoff; /* timezone offset */
	int offset; /* offset for proper alignment */
	struct pcap_sf sf;
	struct pcap_md md;
	int bufsize; /* Read buffer. */
	u_char *buffer;
	u_char *bp;
	int cc; //Place holder for pcap_next().
	u_char *pkt; //Placeholder for filter code if bpf not in kernel.
	struct bpf_program fcode;
	char errbuf[PCAP_ERRBUF_SIZE + 1];
	int dlt_count;
	int *dlt_list;
	#ifdef REMOTE
	/*! \brief '1' if we're the network client; needed by several functions (like pcap_setfilter() ) to know if
	they have to use the socket or they have to open the local adapter. */
		int rmt_clientside;
		SOCKET rmt_sockctrl; //!< socket ID of the socket used for the control connection
		SOCKET rmt_sockdata; //!< socket ID of the socket used for the data connection
		pthread_t rmt_threaddata; //!< handle to the receiving thread, we need to kill it in case of 'pcap_clos()'
		int rmt_flags; //!< we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_start capture()
		int rmt_capstarted; //!< 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture()
		struct pcap_pkthdr pcap_header; //!< In Linux, you have to copy the packet headers another time before giving them to the user
	#endif
};

举例:

/* Open the adapter */
if ( (adhandle= pcap_open_live(d->name, // name of the device
			65536, // portion of the packet to capture. 65536 grants that the whole packet will be captured on all the MACs.
			1, // 混杂模式
			1000, // 设置超时时间,亳秒为单位
			errbuf // 发生错误时存放错误内容的缓冲区
			) ) == NULL)
{
	fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
	pcap_freealldevs(alldevs);
	return -1;
}

4. int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
说明:捕获数据包;不会响应pcap_open_live()中设置的超时时间
参数:
p是由pcap_open_live()返回的所打开网卡的指针
cnt用于设置所捕获数据包的个数
packet_handler是与void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)配合使用的一个参数。回调函数。
user值一般为NULL
举例:

pcap_loop(adhandle, 0, packet_handler, NULL);
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	struct tm *ltime;
	char timestr[16];
	ltime=localtime(&header->ts.tv_sec); /* 将时间戳转变为易读的标准格式*/
	strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
	printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
}

5. int pcap_dispatch(pcap_t * p, int cnt, pcap_handler, u_char *user)
说明:捕获数据包。可以不被阻塞
参数:与pcap_loop()相同
pcap_dispatch(...)和pcap_loop(...)的比较:
一旦网卡被打开,旧可以调用pcap_dispatch() 或pcap_loop()进行数据的捕获,这两个函数的功能十分相似,不同的是pcap_ dispatch()可以不被阻塞,而pcap_loop()在没有数据流到达时将阻塞。在这个简单的例子里用
pcap_loop()就足够了,而在一些复杂的程序里往往用pcap_dispatch()。这两个函数都有返回的参数,一个指向某个函数(该函数用来接受数据如该程序中的packet_handler)的指针,libpcap调用该函数对每个从网上到来的数据包进行处理和接收数据包。另一个参数是带有时间戳和包长等信息的头部,最后一个是含有所有协议头部数据报的实际数据。注意MAC的冗余校验码一般不出现,因为当一个桢到达并被确认后网卡就把它删除了,同样需要注意的是大多数网卡会丢掉冗余码出错的数据包,所以WinPcap一般不能够捕获这些出错的数据报。
6. int pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data)
说明:捕获数据包,与pcap_ dispatch() pcap_loop()很相似。pcap_next_ex()允许直接调用来接收包,它的参数和pcap_loop()相同:有一个网卡描述副,和两个指针,这两个指针会被初始化并返回给用户,一个是pcap_pkthdr结构,另一个是接收数据的缓冲区。
参数:
p是由pcap_open_live()返回的所打开网卡的指针
pcap_pkthdr型的结构体,存储时间,包的长度
pkt_data存储数据包的内容,为一个char型数组

struct pcap_pkthdr
{
	struct timeval ts; /* time stamp */
	bpf_u_int32 caplen; /* length of portion present */
	bpf_u_int32 len; /* length this packet (off wire) */
};

返回值: 当等于1时成功;等于0时超时;等于-1时说明发生错误,错误信息用pcap_geterr(adhandle)获得
举例:(最重要的一段代码)

#define LINE_LEN 16
main(int argc, char **argv)
{
	pcap_if_t *alldevs, *d;
	pcap_t *fp;
	u_int inum, i=0;
	char errbuf[PCAP_ERRBUF_SIZE];
	int res;
	struct pcap_pkthdr *header;
	u_char *pkt_data;
	printf("pktdump_ex: prints the packets of the network using WinPcap.\n");
	printf("\t Usage: pktdump_ex [-n adapter] | [-f file_name]\n\n");
	if(argc < 3)
	{
		if (pcap_findalldevs(&alldevs, errbuf) == -1)
		{
			fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
			exit(1);
		}
		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("%d", &inum);
		if(inum < 1 || inum > i) //选择网卡
		{
			printf("\nInterface number out of range.\n");
			/* Free the device list */
			pcap_freealldevs(alldevs);
			return -1;
		}
		for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); //跳转到指定的网卡
		if ( (fp= pcap_open_live(d->name, 100, 1, 20, errbuf) ) == NULL)
		{
			fprintf(stderr,"\nError opening adapter\n");
			return -1;
		}
	}
	else
	{
		switch (argv[1] [1]) /* The user provided a packet source: open it */
		{
		case 'n':
			{
			if ( (fp= pcap_open_live(argv[2], 100, 1, 20, errbuf) ) == NULL) //Open a physical device
			{
				fprintf(stderr,"\nError opening adapter\n");
				return -1;
			}
			break;
			}
		case 'f':
			{
			if ( (fp = pcap_open_offline(argv[2], errbuf) ) == NULL) /* Open a capture file */
			{
				fprintf(stderr,"\nError opening dump file\n");
				return -1;
			}
			break;
			}
		}
	}
	while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0)
	{
		if(res == 0)
			continue;
		printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);
		for (i=1; (i < header->caplen + 1 ) ; i++)
		{
			printf("%.2x ", pkt_data[i-1]);
			if ( (i % LINE_LEN) == 0) printf("\n");
		}
		printf("\n\n");
	}
	if(res == -1)
	{
		printf("Error reading the packets: %s\n", pcap_geterr(fp));
		return -1;
	}
	return 0;
}

7. int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize,bpf_u_int32 netmask)
说明:编译一个过滤设备,它通过一个高层的boolean型变量和字串产生一系列的能够被底层驱动所解释的二进制编码。boolean表示语法能够在这个文件的过滤表示语法中找到。与pcap_setfilter()配合使用。
参数:
p是打开网卡时返回的网卡指针
fp用于与pcap_setfilter()传递过滤信息。
str是一个字符串。
optimize指明是否需要优化编译结果。
netmask子网掩码
返回值:
发生错误是返回-1
举例:

if(d->addresses != NULL) /* 获得第一个接口地址的掩码*/
	netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_addr;
else
	netmask=0xffffff; /* 如果这个接口没有地址那么我们假设他为C类地址*/
if(pcap_compile(adhandle, &fcode, "ip and tcp", 1, netmask) <0 )
{
	fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
	pcap_freealldevs(alldevs);
	return -1;
}
if(pcap_setfilter(adhandle, &fcode)<0)
{
	fprintf(stderr,"\nError setting the filter.\n");
	pcap_freealldevs(alldevs);
	return -1;
}

8. int pcap_setfilter ( pcap_t *p, struct bpf_program * fp )
说明:pcap_setfilter() 用来联系一个在内核驱动上过滤的过滤器,这时所有网络数据包都将流经过滤器,并拷贝到应用程序中。
参数:
p是打开网卡时返回的网卡指针
fp是pcap_compile()传递过来的参数
返回值:
错误时返回-1
9. int pcap_sendpacket(pcap_t *p, u_char *buf, int size)
Ø 说明:手工发送一个数据包了。这个函数需要的参数:一个装有要发送数据的缓冲区,要发送的长度,和一个适配器。注意缓冲区中的数据将不被内核协议处理,只是作为最原始的数据流被发送,所以我门必须填充好正确的协议头以便正确的将数据发送。
参数:
p是打开网卡时返回的网卡指针
buf是发送数据包的内容缓冲区首地址
size是发送数据包的大小
举例:

void usage();
void main(int argc, char **argv)
{
	pcap_t *fp;
	char error[PCAP_ERRBUF_SIZE];
	u_char packet[100];
	int i;
	if (argc != 2)
	{
	printf("usage: %s inerface", argv[0]);
	return;
	}
	if((fp = pcap_open_live(argv[1], 100, 1, 1000, error) ) == NULL)
	{
		fprintf(stderr,"\nError opening adapter: %s\n", error);
		return;
	}
	/* 假设网络环境为ethernet,我门把目的MAC设为1:1:1:1:1:1*/
	packet[0]=1;
	packet[1]=1;
	packet[2]=1;
	packet[3]=1;
	packet[4]=1;
	packet[5]=1;
	/* 假设源MAC为2:2:2:2:2:2 */
	packet[6]=2;
	packet[7]=2;
	packet[8]=2;
	packet[9]=2;
	packet[10]=2;
	packet[11]=2;
	/* 填充发送包的剩余部分*/
	for(i=12;i<100;i++){
	packet[i]=i%256;
	}
	/* 发送包*/
	pcap_sendpacket(fp, packet, 100); //发送原始的数据包
	return;
}

发送数据包有关的几个函数
10.pcap_send_queue* pcap_sendqueue_alloc(u_int memsize)
说明:给数据包队列分配空间
参数:
memsize队列缓冲区的大小
返回值:
pcap_send_queue指针

struct pcap_send_queue
{
	u_int maxlen;
	u_int len; 		//< Current size of the queue, in bytes.
	char *buffer; 		//< Buffer containing the packets to be sent.
};

举例:

squeue = pcap_sendqueue_alloc(caplen);

11.int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
说明:填充队列
参数:
queue 是由pcap_sendqueue_alloc()返回的指针
pkt_header是数据包头
pkt_data是数据包内容缓冲区指针
返回值:错误是返回-1
举例:

if(pcap_sendqueue_queue(squeue, pktheader, pktdata) == -1)
{
	printf("Warning: packet buffer too small, not all the packets will be sent.\n");
	break;
}

12.u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync)
说明:发送队列数据
参数:
pcap_t是pcap_open_live()函数打开的网卡指针
queue 是由pcap_sendqueue_alloc()返回的指针
sync是同步设置。如果非零,那么发送将是同步的,这将站用很大的CPU资源,因为发生在内核驱动的同步发送是通过"brute force" loops的,但是一般情况下能够精确到微秒。
返回值:错误是返回-1
举例:

if((res = pcap_sendqueue_transmit(outdesc, squeue, sync)) < squeue->len)
{
	printf("An error occurred sending the packets: %s. Only %d bytes were sent\n", error,res);
}

13.void pcap_sendqueue_destroy(pcap_send_queue* queue);
说明:释放队列
参数:
queue 是由pcap_sendqueue_alloc()返回的指针

 

 

 

struct pcap_pkthdr

{

      struct timeval ts;   ts是一个结构struct timeval,它有两个部分,第一部分是1900开始以来的秒数,第二部分是当前秒之后的毫秒数

      bpf_u_int32 caplen;  表示抓到的数据长度

      bpf_u_int32 len;    表示数据包的实际长度

}

 

函数名称:packet_handler(u_char *param , const struct pcap_pkthdr *header , const u_char *pkt_data)   
函数功能:可进行包处理,与pcap_loop等回调函数联合使用进行抓包   
参数说明:
u_char *param:数据包存储的文件指针   
struct pcap_pkthdr * header: 并非是数据包的指针,只是与数据包捕获驱动有关的一个header ,是堆文件包的结构体首部指针。可以得到时间值,数据包长度。   
const_char * pkt_data:指向数据包内容的指针,包括了协议头,可以经过计算获得IP数据包头部的位置, UDP首部的位置。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

winpcap编程函数介绍 的相关文章

随机推荐