WinPcap是一个重要的抓包工具,它是libpcap的Windows版本。
关于WinPcap的介绍、安装与配置,之后我会单独写一篇文章,这里先介绍下使用Winpcap函数能完成的基本功能:捕获数据包并转发。
首先来看几个函数:
int pcap_findalldevs_ex(char * source, struct pcap_rmtauth * auth,pcap_if_t ** alldevs, char * errbuf)
这个函数可以列出本地机器上的设备,还可以列出远程机器上的设备
网卡信息
struct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr *addresses;
u_int flags; }可以用pcap_if_t代替pcap_if
每一个包的包头和数据
struct pcap_pkthdr {
structtimeval ts;
bpf_u_int32caplen;
bpf_u_int32len;
}
ts:时间戳 ; cpalen:当前分组的长度 ; len:数据包的长度。
pcap_t*pcap_open (constchar * source, int snaplen, int flags,int read_timeout, struct pcap_rmtauth * auth, char * errbuf)
此函数用于为捕获/发送数据打开一个普通的源。
参数:
source:包含要打开的源名称的字符串。
read_timeout:以毫秒单位。readtimeout用来设置在遇到一个数据包的时候读操作不必立即返回,而是等待一段时间,让更多的数据包到来后从OS内核一次读多个数据包。并非所有的平台都支持readtimeout;在不支持readtimeout的平台上将被忽略。
snaplen:需要保留的数据包的长度。对每一个过滤器接收到的数据包,第一个‘snaplen’字节的内容将被保存到缓冲区,并且传递给用户程序。例如,snaplen等于100,那么仅仅每一个数据包的第一个100字节的内容被保存。简言之就是从每一个包的开头到snaplen的那段内容将被保存。
flags:保存一些由于抓包需要的标志。PCAP_OPENFLAG_PROMISCUOUS定义了适配器(网卡)是否进入混杂模式(promiscuous mode),当要捕获数据包时,就使用这个标志。
auth:一个指向’struct pcap_rmtauth’的指针,保存当一个用户登录到某个远程机器上时的必要信息。假如不是远程抓包,该指针被设置为NULL
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
此函数用于捕获数据包,其中,cnt指定了捕获数据包的最大数目,pcap_handler是个回调函数。
int pcap_sendpacket (pcap_t* p, u_char* buf, int size)
此函数用于发送一个原始数据包(raw packet)到网络上。
其中,p是用来发送数据包的那个接口,buf包含着要发送的数据包的数据(包括各种各样的协议头),
size是buf所指的缓冲区的尺寸,也就是要发送的数据包的大小。MAC循环冗余码校验不必被包含,因为它很容易被计算出来并被网络接口驱动添加。如果数据包被成功发送,返回0;否则,返回-1。
下图为捕获数据包并转发的流程图,其中转换时间戳格式和发送数据包使用了回调函数的方式完成。(我承认我没怎么画过流程图,一定很坑,不过领会精神就好了=。=)
#include "pcap.h" pcap_t *adhandle; //将时间戳转变为标准模式 void packet_handler(u_char *param, const struct pcap_pkthdr *header,const u_char *pkt_data) { struct tm *ltime; char timestr[16]; time_t t=(time_t)header->ts.tv_sec; u_char packet[1000]={0}; ltime=localtime(&t); strftime(timestr,sizeof(timestr),"%H:%M:%S",ltime); printf(" %s.%.6d Len: %d \n", timestr, header->ts.tv_usec, header->len); memcpy(packet,pkt_data,header->len); packet[0]=1; packet[1]=1; packet[2]=1; packet[3]=1; packet[4]=1; packet[5]=1; packet[6]=2; packet[7]=2; packet[8]=2; packet[9]=2; packet[10]=2; packet[11]=2; if (pcap_sendpacket(adhandle, packet,header->len) != 0) { fprintf(stderr,"\nError sending the packet: \n", pcap_geterr(adhandle)); } else { printf("Sending Success!\n"); } } int main() { pcap_if_t * alldevs; pcap_if_t *d; int inum=0,i=0; char errbuf[PCAP_ERRBUF_SIZE]; //获得网卡列表 if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL,&alldevs,errbuf)==-1) { fprintf(stderr,"Error in finding all devs:%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 available description!\n"); } } if(i==0) { printf("No interface found!Make sure the Winpcap is installed \n"); return -1; } printf("Input the interface number:1-%d\n",i); scanf("%d",&inum); if(inum<1 inum="">i) { printf("The number is out of range!\n"); pcap_freealldevs(alldevs); return -1; } //找到要选择的网卡结构 for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); if((adhandle=pcap_open(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf))==NULL) { fprintf(stderr,"Can't open the adapter.%s is not supported by Winpcap!\n",errbuf); pcap_freealldevs(alldevs); return -1; } /* 检查数据链路层,为了简单,只考虑以太网 */ if(pcap_datalink(adhandle) != DLT_EN10MB) { fprintf(stderr,"\nThis program works only on Ethernet networks.\n"); /* 释放设备列表 */ pcap_freealldevs(alldevs); return -1; } printf("Started listening on %s... \n",d->description); pcap_freealldevs(alldevs); pcap_loop(adhandle,0,packet_handler,NULL); return 0; }