欢迎访问binary的Blog   虚心使人进步,骄傲使人落后。

          W3CHINA Blog首页    管理页面    写新日志    退出



«December 2025»
123456
78910111213
14151617181920
21222324252627
28293031


登录

用户名称:
登陆密码:
密码保存:


联系我
email: binaryluo(at)gmail.com

我的分类

日志更新

最新评论

留言板

Blog信息

 
blog名称:二进制-虚心使人进步,骄傲使人落后。
日志总数:42
评论数量:370
留言数量:88
访问次数:646150
建立时间:2005年2月19日




[网络编程技术]【原创】Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

binaryluo 发表于 2006/2/3 21:18:58

     Winpcap提供(libpcap也提供)的一个强大特性是过滤引擎(filtering engine)。它提供了一个非常有效的接收网络流量的方法,并且它通常与Winpcap提供的抓包机制集成在一起。用于过滤数据包的函数是pcap_complie()和pcap_setfilter()。      pcap_complie()使用一个包含高级布尔表达式的字符串并且产生一个能被过滤引擎集成到数据包驱动中的低级字节码。      pcap_setfilter()把一个过滤器与核心驱动抓包会话关联起来。一旦pcap_setfilter()被调用,相关的过滤器将被应用到所有的来自网络的数据包上,并且所有的一致的数据包将被复制给应用程序。   抓包和过滤     经过前面几天的知识准备,现在我们将把前面的知识综合后应用于一个简单的实际应用程序。下面试验的目的是如何解析和解释被捕获的数据包的协议头。应用程序运行的结果是打印出一组我们的网络上的UDP通信数据。我们之所以选择解析和显示UDP协议是因为它比起其他协议(例如:TCP)更易于理解,对于初学者更适合。     试验代码: #include <pcap.h>#include <remote-ext.h> /* 4 bytes IP address */typedef struct ip_address{ u_char byte1; u_char byte2; u_char byte3; u_char byte4;}ip_address; /* IPv4 header */typedef struct ip_header{ u_char ver_ihl;  /* Version (4 bits) + Internet header length (4 bits)*/ u_char tos;      /* Type of service */ u_short tlen;    /* Total length */ u_short identification; /* Identification */  u_short flags_fo;       /* Flags (3 bits) + Fragment offset (13 bits)*/ u_char ttl;      /* Time to live */ u_char proto;    /* Protocol */ u_short crc;     /* Header checksum */ ip_address saddr;/* Source address */ ip_address daddr;/* Destination address */ u_int op_pad;    /* Option + Padding */}ip_header; /* UDP header */typedef struct udp_header{ u_short sport;   /* Source port */ u_short dport;   /* Destination port */ u_short len;     /* Datagram length */ u_short crc;     /* Checksum */}udp_header; /* Prototype of the pachet 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]; u_int netmask; char packet_filter[] = "ip and udp"; struct bpf_program fcode;  /* Retrieve the device list */ 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("%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; d; d = d->next);  /* Open the adapter */ if ((adhandle = pcap_open(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 */       PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */       1000,       /* read timeout */       NULL,       /* remote authentication */       errbuf      /* error buffer */       )) == NULL) {  fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Winpcap\n");  /* Free the devices list */  pcap_freealldevs(alldevs);  return -1; }  /* Check the link layer. We support only Ethernet for simplicity */ if (pcap_datalink(adhandle) != DLT_EN10MB) {  fprintf(stderr, "\nThis program works only on Ethernet networks.\n");  /* Free the devices list */  pcap_freealldevs(alldevs);  return -1; }  if (d->addresses != NULL) {  /* Retrieve the mask of the first address of the interface */  netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; } else {  /* If the interface is without addresses we suppose to be in a C class network */  netmask = 0xffffffff; }  /* complie the filter */ if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0) {  fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");  /* Free the devices list */  pcap_freealldevs(alldevs);  return -1; }  /* set the filter */ if (pcap_setfilter(adhandle, &fcode) < 0) {  fprintf(stderr, "\nError setting the filter.\n");  /* Free the devices 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 1;} /* 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]; ip_header* ih; udp_header* uh; u_int ip_len; u_short sport, dport;  /* convert the timestamp to readable format */ ltime = localtime(&header->ts.tv_sec); strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);  /* print timestamp and length of the packet */ printf("%s.%.6d len: %d ", timestr, header->ts.tv_usec, header->len);  /* retrieve the position of the ip header */ ih = (ip_header*)(pkt_data + 14);  /* length of ethernet header */  /* retrieve the position of the udp header */ ip_len = (ih->ver_ihl & 0xf) * 4; uh = (udp_header*)((u_char*)ih + ip_len);  /* convert from network byte order to host byte order */ /*sport = ntohs(uh->sport); dport = ntohs(uh->dport);*/  /* print ip addresses and udp ports */ printf("%d.%d.%d.%d -> %d.%d.%d.%d\n",  ih->saddr.byte1,  ih->saddr.byte2,  ih->saddr.byte3,  ih->saddr.byte4,  /*sport,*/  ih->daddr.byte1,  ih->daddr.byte2,  ih->daddr.byte3,  ih->daddr.byte4  /*dport*/);} 函数1: int pcap_datalink(pcap_t* p) 返回链路层上的一个适配器。返回链路层的类型,链路层的类型包括: l         DLT_NULL: BSD回路封装;链路层协议头是一个4字节的域,以主机字节顺序(host byte order),包含一个从socket.h来的PF_value。主机字节顺序(host byte order)是捕获数据包的机器的字节顺序,而PF_value是捕获数据包的机器的OS。如果一个读取一个文件,字节顺序和PF_value不一定是抓取文件的那些机器。 l         DLT_EN10MB: 以太网(10Mb, 100Mb, 1000Mb, 或者更高)。 l         DLT_IEEE802: IEEE802.5令牌环网。 l         DLT_ARCNET:ARCNET。 l         DLT_SLIP:SLIP。 l         DLT_PPP:PPP;如果第一个字节是0xff或0x03,它是类HDLC帧上的PPP。 l         DLT_FDDI:FDDI l         DLT_ATM_RFC1483:RFC1483LLC/SNAP ATM;数据包以IEEE802.2 LLC头开始。 l         DLT_RAW:原始IP(raw IP);数据包以IP头开始。 l         DLT_PPP_SERIAL:按照RFC1662,基于类HDLC帧的PPP,或者按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP;前者的第一个字节是0xFF,后者的第一个字节是0x0F或0x8F。 l         DLT_PPP_ETHER:按照RFC2516,PPPoE;数据包以PPPoE头开始。 l         DLT_C_HDLC:按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP。 l         DLT_IEEE802_11:IEEE 802.11无线局域网。 l         DLT_FRELAY:帧中继(Frame Relay)。 l         DLT_LOOP:OpenBSD回路封装。 l         DLT_LINUX_SLL:Linux抓包封装。 l         DLT_LTALK:苹果的LocalTalk,数据包以AppleTalk LLAP头开始。 l         DLT_PFLOG:OpenBSD pflog。 l         DLT_PRISM_HEADER:后接802.11头的棱镜监视器模式(Prism monitor mode)信息。 l         DLT_IP_OVER_FC:RFC2625 IP-over-Fiber 频道,以RFC2625中定义的Network_Header开始。 l         DLT_SUNATM:SunATM设备。 l         DLT_IEEE802_11_RADIO:后接802.11头的链路层信息。 l         DLT_ARCNET_LINUX:没有异常帧的ARCNET。 l         DLT_LINUX_IRDA:Linux-IrDA数据包,DLT_LINUX_SLL头后接IrLAP头。 


阅读全文(18588) | 回复(24) | 编辑 | 精华
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

feiyu_lili发表评论于2006/3/5 11:23:46

呵呵,终于抓到binaryluo的一个小错误了,开心 


个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

binaryluo发表评论于2006/3/3 23:53:20

以下引用feiyu_lili在2006-3-3 14:28:08的评论: 以下引用binaryluo在2006-3-3 9:46:42的评论: 以下引用feiyu_lili在2006-3-2 15:39:13的评论: 终于发现运行出错的原因了!  /* Jump to the selected adapter */ for (d = alldevs; d; d = d->next); 的 for (d = alldevs; d; d = d->next);这句话的指针出错了,指向了下一个了。我把它改成 for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);就对了。我就不知道你怎么不会出错呢两句的效果是一样的。因为alldevs相当于是个链表,对链表的每一个节点进行访问用前面那句就可以了;你的做法也对,不过还要先遍历一遍链表以获得链表的长度(inum),效率较前者低。如果你的这种写法不会出错的话你用你的这种写法也行。 这里我不认为是你说的那样前面的程序里面有: printf("Enter the interface number (1 - %d):", i); scanf("%d", &inum); 这里的inum是输入的选择的是哪个 ,而不是链表的长度,链表的长度我认为是i. 我认为你的d也是链表的长度,才会出现错误,我曾经用for (d = alldevs; d; d = d->next);打印出来的地址是0。是的,是我错了。inum是表示网卡的编号。你是对的。 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

feiyu_lili发表评论于2006/3/3 14:29:41

以下引用binaryluo在2006-3-3 9:41:35的评论: 以下引用feiyu_lili在2006-3-2 13:20:32的评论:stderr 是系统自动定义的标准出错输出(也就是从终端输出)。那就是说把Error in pcap_findalldevs加errbuf的内容输出到终端上,而fprintf是要放入文件的阿?那为什么这里不用printf而是要用 fprintf,难道说fprintf也能用来只输出到屏幕上吗?在UNIX操作系统结构中,它把各种外部设备也看成是文件。所以 fprintf不仅能把errbuf的内容输出到磁盘文件里,也可以输出到像终端这样的特殊文件里。printf是fprintf的一个特例,它固定的只能把相应内容输出到终端文件上。你可以看下C语言里的文件操作就明白了。我查过相关资料,不过说的都不够详细,但经过你的解释我已经明白了,谢谢啦~ 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

feiyu_lili发表评论于2006/3/3 14:28:08

以下引用binaryluo在2006-3-3 9:46:42的评论: 以下引用feiyu_lili在2006-3-2 15:39:13的评论: 终于发现运行出错的原因了!  /* Jump to the selected adapter */ for (d = alldevs; d; d = d->next); 的 for (d = alldevs; d; d = d->next);这句话的指针出错了,指向了下一个了。我把它改成 for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);就对了。我就不知道你怎么不会出错呢两句的效果是一样的。因为alldevs相当于是个链表,对链表的每一个节点进行访问用前面那句就可以了;你的做法也对,不过还要先遍历一遍链表以获得链表的长度(inum),效率较前者低。如果你的这种写法不会出错的话你用你的这种写法也行。 这里我不认为是你说的那样前面的程序里面有: printf("Enter the interface number (1 - %d):", i); scanf("%d", &inum); 这里的inum是输入的选择的是哪个 ,而不是链表的长度,链表的长度我认为是i. 我认为你的d也是链表的长度,才会出现错误,我曾经用for (d = alldevs; d; d = d->next);打印出来的地址是0。 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

binaryluo发表评论于2006/3/3 9:46:42

以下引用feiyu_lili在2006-3-2 15:39:13的评论:终于发现运行出错的原因了!  /* Jump to the selected adapter */ for (d = alldevs; d; d = d->next); 的 for (d = alldevs; d; d = d->next);这句话的指针出错了,指向了下一个了。我把它改成 for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);就对了。我就不知道你怎么不会出错呢两句的效果是一样的。因为alldevs相当于是个链表,对链表的每一个节点进行访问用前面那句就可以了;你的做法也对,不过还要先遍历一遍链表以获得链表的长度(inum),效率较前者低。如果你的这种写法不会出错的话你用你的这种写法也行。 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:Winpcap学习:第四天(20060203)【上】
原创空间,  随笔,  读书笔记,  心得体会,  软件技术,  电脑与网络

binaryluo发表评论于2006/3/3 9:41:35

以下引用feiyu_lili在2006-3-2 13:20:32的评论:stderr 是系统自动定义的标准出错输出(也就是从终端输出)。那就是说把Error in pcap_findalldevs加errbuf的内容输出到终端上,而fprintf是要放入文件的阿?那为什么这里不用printf而是要用 fprintf,难道说fprintf也能用来只输出到屏幕上吗?在UNIX操作系统结构中,它把各种外部设备也看成是文件。所以 fprintf不仅能把errbuf的内容输出到磁盘文件里,也可以输出到像终端这样的特殊文件里。printf是fprintf的一个特例,它固定的只 能把相应内容输出到终端文件上。你可以看下C语言里的文件操作就明白了。 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


» 1 2 3 4 »

发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.047 second(s), page refreshed 144807958 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号