实验名称:基于WINPCAP的网络通信
实验目的:
熟练掌握基于WinpCap的网络数据包捕获及数据发送方法
实验要求:第一题,至少显示数据包的源IP,目的IP,源端口,目的端口及协议及TCP或UDP数据部分的10个字节的数据。
实验内容:
- 1、修改老师给的WinpCap程序,可以捕获TCP和UDP通信,显示数据包相关数据。(必须完成)
- 2、修改老师给的winpcapsend程序,完成TCP三次握手( winpcapsend程序只完成了两次握手),用wireshark查看,是否正常完成三次,同时查看wireshark上显示的端口号、IP地址等是否和你的程序的端口号、IP地址一致。
实验结果:
窗体顶端
- // winpcap.cpp : Defines the entry point for the console application.
- //
- #include "stdafx.h"
- #include "include/pcap.h"
- #include "winpcap.h"
- #pragma comment(lib "ws2_32.lib");
- #pragma comment (lib "packet.lib")
- #pragma comment (lib "wpcap.lib")
- void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
- int main(int argc, char* argv[])
- {
- 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;
- /* 获取设备列表 */
- if(pcap_findalldevs(&alldevs, errbuf) == -1)
- {
- fprintf(stderr,"pcap_findalldevs函数调用错误: %s\n", errbuf);
- return 1;;
- }
- /* 打印设备列表 */
- for(d=alldevs; d; d=d->next)
- {
- printf("%d. %s", ++i, d->name);
- if (d->description)
- printf(" (%s)\n", d->description);
- else
- printf(" (没有可用的描述符)\n");
- }
- if(i==0)
- {
- printf("\n无法找到网络接口!请确认WinPcap已正确安装.\n");
- return -1;
- }
- printf("请待捕获数据的输入网卡编号 (1-%d):",i);
- scanf("%d", &inum);
- /* 检查用户输入的网卡编号是否合法 */
- if(inum < 1 || inum > i)
- {
- printf("\nAdapter number out of range.\n");
- /* 释放设备列表 */
- pcap_freealldevs(alldevs);
- return -1;
- }
- /* 跳转到用户选择的网卡 */
- for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
- /* 打开网卡 */
- adhandle= pcap_open_live(d->name, // 设备名
- 65536, // 保证能捕获到不同数据链路层上的每个数据包的全部内容
- 1, // 混杂模式
- 1000, // 读取超时时间
- errbuf // 错误缓冲池
- );
- if ( adhandle == NULL)
- {
- fprintf(stderr,"\n无法打开网络适配器. WinPcap不支持%s \n", d->name);
- // 释放设备列表
- pcap_freealldevs(alldevs);
- return -1;
- }
- /* 检查链接层.本程序只支持以太网. */
- if(pcap_datalink(adhandle) != DLT_EN10MB)
- {
- fprintf(stderr,"\n本程序仅在以太网环境下可用.\n");
- /* 释放设备列表 */
- pcap_freealldevs(alldevs);
- return -1;
- }
- if(d->addresses != NULL)
- /* 获得第一个接口地址的网络掩码 */
- netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
- else
- /* 如果接口没有地址,那么我们假设一个C类的掩码 */
- netmask=0xffffff;
- //编译过滤器
- if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
- {
- fprintf(stderr,"\n无法编译过滤规则,请检查语法的正确性.\n");
- // 释放设备列表
- pcap_freealldevs(alldevs);
- return -1;
- }
- //设置过滤器
- if (pcap_setfilter(adhandle, &fcode)<0)
- {
- fprintf(stderr,"\n设置过滤器错误.\n");
- // 释放设备列表
- pcap_freealldevs(alldevs);
- return -1;
- }
- printf("\nlistening on %s...\n", d->description);
- /* 不需要其他的设备列表信息,释放资源 */
- pcap_freealldevs(alldevs);
- /* 开始流量捕获 */
- pcap_loop(adhandle, 0, packet_handler, NULL);
- return 0;
- }
- struct Packet {
- ether_header eth;
- ip_header ip;
- udp_header udp;
- };
- /* 每当有数据包进入,则以下回调函数被WinPcap调用 */
- 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;
- time_t local_tv_sec;
- /* 将时间戳转换成可识别的格式 */
- local_tv_sec = header->ts.tv_sec;
- ltime=localtime(&local_tv_sec);
- strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
- struct Packet *pack=(struct Packet *)pkt_data;
- // ether_header *eth=&(pack->eth)://(ether_header *)pkt_data;
- /* 打印数据包的时间戳和长度 */
- printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
- /* 获得IP数据包头部的位置 */
- ih = (ip_header *) (pkt_data + sizeof(ether_header)); //以太网头部长度
- /* 打印IP地址和UDP/TCP端口 */
- if (ih->ip_protocol == IPPROTO_TCP) {
- printf("TCP ");
- } else if (ih->ip_protocol == IPPROTO_UDP) {
- printf("UDP ");
- }
- /* 获得UDP首部的位置 */
- ip_len = ih->ip_header_length * 4;
- uh = (udp_header *) ((u_char*)ih + ip_len);
- /* 将网络字节序列转换成主机字节序列 */
- sport = ntohs( uh->udp_source_port );
- dport = ntohs( uh->udp_destination_port );
- /* 打印IP地址和UDP端口 */
- printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
- ih->ip_souce_address.S_un.S_un_b.s_b1,
- ih->ip_souce_address.S_un.S_un_b.s_b2,
- ih->ip_souce_address.S_un.S_un_b.s_b3,
- ih->ip_souce_address.S_un.S_un_b.s_b4,
- sport,
- ih->ip_destination_address.S_un.S_un_b.s_b1,
- ih->ip_destination_address.S_un.S_un_b.s_b2,
- ih->ip_destination_address.S_un.S_un_b.s_b3,
- ih->ip_destination_address.S_un.S_un_b.s_b4,
- dport);
- if (ih->ip_protocol == IPPROTO_TCP) {
- printf("协议为TCP,数据部分的10个字节的数据为: ");
- for (int i = 0; i < 10; i++) {
- printf("%02X \n", pkt_data[ip_len + sizeof(udp_header) + i]);
- }
- } else if (ih->ip_protocol == IPPROTO_UDP) {
- printf("协议为UDP,数据部分的10个字节的数据为: ");
- for (int i = 0; i < 10; i++) {
- printf("%02X \n", pkt_data[ip_len + sizeof(tcp_header) + i]);
- }
- }
- }
窗体底端
实验心得:
通过这段代码的实验,我对网络数据包的捕获和分析有了更深入的了解。在实验中,我学会了如何使用WinPcap库来捕获网络数据包,并对捕获到的数据包进行解析和分析。具体来说,我了解到了网络数据包由数据链路层、网络层、传输层和应用层组成,每个层次都有自己的协议和头部。在代码中,我学会了如何解析以太网头部、IP头部和UDP/TCP头部,并从中获取相关信息。WinPcap是一个开源的网络数据包捕获库,可以在Windows平台上捕获网络数据包。在代码中,我学会了如何使用WinPcap库来打开网络接口、设置过滤条件、捕获数据包和关闭网络接口。在实验中,我学会了如何根据数据包的协议类型、源IP地址、目的IP地址、源端口号和目的端口号等信息,来分析数据包的内容和用途。通过对数据包的分析,我可以了解网络流量的特征和趋势,以及排查网络问题的原因。总的来说,这次实验让我更深入地了解了网络数据包的捕获和分析,也让我掌握了一些基本的网络分析技巧。这对于我今后从事网络安全工作都非常有帮助。