实验八协议分析器程序的设计和实现1.实验目的:(1)掌握对网络上传输数据包的捕获方法。
(2)解析Ethernet网数据帧头部的全部信息。
(3)解析IP、ICMP数据包(4) 解析传输层和应用层相关协议的头部信息(5)设置过滤规则,能过滤相应协议的数据包。
(6)要求有良好的编程规范与注释信息,要求有详细的说明文档,包括程序的设计思想、活动图、关键问题以及解决方法。
2实验环境:(1)VC6.0(2)局域网能连接Internet。
3.程序设计的关键问题以及解决方法有哪些?当应用程序通过IP网络传送数据时,数据被送入TCP/IP协议栈中,然后从上至下逐一通过每一层,直到最后被当作一串比特流送入网络。
其中每一层对收到的数据都要增加一些首部信息,这个过程被称作封装。
通过以太网传输的比特流称作帧。
在传输的另一端,当目的主机收到一个以太网数据帧时,数据就开始从协议栈由底向上逐层解析,去掉各层协议所加上的报文头部。
每层协议均要检查报文头部中的协议标识字段,以确定要接收数据的上层协议,最终从报文中解析出应用层数据后交给应用程序处理。
本次要编写的协议分析器,就是从网络中捕获数据包并对其进行解析的过程。
因此,我们需要了解每层协议所规定的报文格式,然后由底向上逐层对数据包进行解码,最后将分析的结果显示出来。
4.描述程序设计过程,并画出程序活动图。
协议分析器总体结构:协议分析器的整体结构按功能应分为三个部分,自底向上分别是数据捕获模块、协议解析模块和用户显示模块。
数据包捕获流程:捕获数据包的算法一般分为以下几步:(1)获取并列出当前网络设备列表。
(2)由用户选择并打开指定网卡。
(3)根据过滤规则设置过滤器。
捕获数据包并进行解析处理:协议解析模块:对捕获的数据包按照数据链路层(MAC)、网络层(IP、ARP/RARP)、传输层(TCP、UDP、ICMP)和应用层(HTTP等)的层次结构自底向上进行解析,最后将解析结果显示输出。
1)解析Ethernet帧2)解析ARP数据包3)解析IP数据包4)解析ICMP,TCP和UDP数据包5.给出关键代码,并附注释。
1)解析Ethernet帧typedef struct{BYTE DesMacAddr[6]; //目的地址BYTE SrcMacAddr[6]; //源地址WORD LengthOrType; //数据长度或类型} MAC_HEADER;//MAC帧类型定义const u_short MAC_TYPE_IP = 0x0800; const u_short MAC_TYPE_ARP = 0x0806; const u_short MAC_TYPE_RARP = 0x8035;MAC_HEADER* pMacHdr = (MAC_HEADER*) pPkt;// Mac目的地址strItem.Format("Destination address: %02X:%02X:%02X:%02X:%02X:%02X",pMacHdr->DesMacAddr[0],pMacHdr->DesMacAddr[1],pMacHdr->DesMacAddr[2],pMacHdr->DesMacAddr[3],pMacHdr->DesMacAddr[4],pMacHdr->DesMacAddr[5]);// Mac源地址strItem.Format("Source address: %02X:%02X:%02X:%02X:%02X:%02X",pMacHdr->SrcMacAddr[0],pMacHdr->SrcMacAddr[1],pMacHdr->SrcMacAddr[2],pMacHdr->SrcMacAddr[3],pMacHdr->SrcMacAddr[4],pMacHdr->SrcMacAddr[5]);//类型/长度字段if (ntohs(pMacHdr->LengthOrType) > 1500)//类型字段(Ethernet V2.0){//根据类型字段调用相应的上层协议处理函数if (ntohs(pMacHdr->LengthOrType) == MAC_TYPE_IP) //IP协议{strItem = "IP";ParseIPPacket((BYTE*)pMacHdr+sizeof(MAC_HEADER),iLen-sizeof(MAC_HEADER));}else if (ntohs(pMacHdr->LengthOrType) == MAC_TYPE_ARP) //ARP协议{strItem = "ARP";ParseARPPacket((BYTE*)pMacHdr+sizeof(MAC_HEADER),iLen-sizeof(MAC_HEADER));}else if (ntohs(pMacHdr->LengthOrType) == MAC_TYPE_RARP) //RARP协议{strItem = "RARP";ParseRARPPacket((BYTE*)pMacHdr+sizeof(MAC_HEADER),iLen-sizeof(MAC_HEADER));}else //其他strItem = "UNKNOWN";}else //长度字段(IEEE802格式){strItem.Format("Length: %d bytes", ntohs(pMacHdr->LengthOrType));……/*解析IEEE802数据帧,省略*/}2)解析ARP数据包//ARP头部结构typedef struct{u_short hardware_type; //16位硬件类型u_short proto_type; //16位协议类型u_char hardware_addr_len; //8位硬件地址长度u_char proto_addr_len; //8位协议地址长度u_short operation_code; //16位操作码u_char src_mac_addr[6]; //源Ethernet网地址u_char scr_ip_addr[4]; //源IP地址u_char dest_mac_addr[6]; //目的Ethernet网地址u_char dest_ip_addr[4]; //目的IP地址} ARP_HEADER;//ARP报文操作码类型,1为请求,2位应答const u_short ARP_OP_REQUEST = 1; //ARP请求const u_short ARP_OP_REPL Y = 2; //ARP应答ARP_HEADER* pARPHdr = (ARP_HEADER*)pPkt;ntohs(pARPHdr->hardware_type); //硬件类型ntohs(pARPHdr->proto_type); //上层协议类型pARPHdr->hardware_addr_len; //硬件地址长度pARPHdr->proto_addr_len; //协议地址长度//操作类型if (ntohs(pARPHdr->operation_code) == ARP_OP_REQUEST)strItem = " (Request)";else if (ntohs(pARPHdr->operation_code) == ARP_OP_REPL Y)strItem = " (Reply)";//源Mac地址strItem.Format("Sender's hardware address: %02X:%02X:%02X:%02X:%02X:%02X",pARPHdr->src_mac_addr[0],pARPHdr->src_mac_addr[1],pARPHdr->src_mac_addr[2],pARPHdr->src_mac_addr[3],pARPHdr->src_mac_addr[4],pARPHdr->src_mac_addr[5]);//源IP地址in_addr ipAddr;memcpy(&ipAddr, pARPHdr->scr_ip_addr, sizeof(in_addr));inet_ntoa(ipAddr); //转化为点分十进制字符串//目的Mac地址strItem.Format("Target's hardware address: %02X:%02X:%02X:%02X:%02X:%02X",pARPHdr->dest_mac_addr[0],pARPHdr->dest_mac_addr[1],pARPHdr->dest_mac_addr[2],pARPHdr->dest_mac_addr[3],pARPHdr->dest_mac_addr[4],pARPHdr->dest_mac_addr[5]);//目的IP地址memcpy(&ipAddr, pARPHdr->dest_ip_addr, sizeof(in_addr));inet_ntoa(ipAddr); //转化为点分十进制字符串//填充字段长度strItem.Format("Frame padding: %d bytes", iLen-sizeof(ARP_HEADER));3)解析IP数据包//IP头部结构typedef struct{unsigned char hdr_len :4; //4位头部长度unsigned char version :4; //4位版本号unsigned char tos; //8位服务类型unsigned short total_len; //16位总长度unsigned short identifier; //16位标识符unsigned short frag_and_flags; //3位标志+13位片偏移unsigned char ttl; //8位生存时间unsigned char protocol; //8位上层协议号unsigned short checksum; //16位校验和unsigned long source_ip; //32位源IP地址unsigned long dest_ip; //32位目的IP地址} IP_HEADER;IP_HEADER* pIPHdr = (IP_HEADER*)pPkt;……/*解析IP头部各字段并显示输出*///根据不同上层协议调用相应解析函数switch (pIPHdr->protocol){case IPPROTO_TCP:strItem = "TCP";ParseTCPPacket((BYTE*)pIPHdr+iIPHdrLen, iLen-iIPHdrLen);break;case IPPROTO_UDP:strItem = "UDP";ParseUDPPacket((BYTE*)pIPHdr+iIPHdrLen, iLen-iIPHdrLen);break;case IPPROTO_ICMP:strItem = "ICMP";ParseICMPPacket((BYTE*)pIPHdr+iIPHdrLen, iLen-iIPHdrLen);break;default:strItem = "OTHERS";}4)解析ICMP,TCP和UDP数据包//ICMP基本头部typedef struct{BYTE type; //8位类型BYTE code; //8位代码USHORT c ksum; //16位校验和} ICMP_BASE_HEADER;//----------------------------------------------------//ICMP回显头部(回显请求、回显应答)typedef struct{ICMP_BASE_HEADER base_hdr; //基本头部USHORT i d; //16位标识符USHORT s eq; //16位序列号} ICMP_ECHO_HEADER;//----------------------------------------------------//ICMP差错报文头部(超时及目的不可达)typedef struct{ICMP_BASE_HEADER base_hdr; //基本头部ULONG unused; //32位未用,必须为0} ICMP_ERROR_HEADER;pICMPHdr->type; //类型pICMPHdr->code; //代码ntohs(pICMPHdr->cksum); //校验和//根据不同类型解析后续字段switch (pICMPHdr->type){case ICMP_ECHO_REQUEST: //回显请求报文case ICMP_ECHO_REPL Y: //回显应答报文{ICMP_ECHO_HEADER* pICMPEchoHdr = (ICMP_ECHO_HEADER*)pPkt;ntohs(pICMPEchoHdr->id); //标识符ntohs(pICMPEchoHdr->seq); //序列号if (iLen > sizeof(ICMP_ECHO_HEADER)) //选项数据{for (int i=0; i<iLen-sizeof(ICMP_ECHO_HEADER); i++)……/*打印选项数据*/}break;}case ICMP_TIMEOUT: //超时差错报文case ICMP_HOST_UNREACHABLE: //目的不可达差错报文{ICMP_ERROR_HEADER* pICMPErrHdr = (ICMP_ERROR_HEADER*)pPkt;ntohl(pICMPErrHdr->unused); //保留字段//解析产生差错的数据报IP首部(含选项字段)及其载荷的前8个字节ParseIPPacket((BYTE*)pICMPErrHdr+sizeof(ICMP_ERROR_HEADER),iLen-sizeof(ICMP_ERROR_HEADER));break;}5)解析HTTP数据包/*HTTP消息的拷贝存放在pHttpPkt指向的缓冲区中首先在HTTP消息中查找代表消息头结束的空行*/char* pStopPos = strstr(pHttpPkt, "\r\n\r\n"); //结束指针指向最后的空行if (pStopPos != NULL) //存在空行{char* pBegPos = pHttpPkt; //每行的首指针char* pCurPos = pHttpPkt; //每行的尾指针int iLine = 1;while (pCurPos < pStopPos){//查找消息头中每行的结束符pCurPos = strstr(pBegPos, "\r\n");if (pCurPos != NULL)*pCurPos = '\0'; //将该行变为以NULL结尾的字符串/*如果第一行含有版本字段前缀“HTTP/”则认为是一个正确的HTTP头*/if (iLine++ == 1){if (strstr(pBegPos, "HTTP/") == NULL)break;}strItem.Format(“%s”, pBegPos); //输出该行信息pCurPos += 2; //跳过回车换行符指向下一行开始pBegPos = pCurPos;}}。