0x01 前言
本来以为可以划水过去的网络安全实验课,突然就被老师布置了一个作业,写一个网络嗅探器,大概要求如下:
利用Winsock编程接口,设计实现一个能够在共享式局域网中完成网络抓包并分析所截获数据包的嗅探程序。
要求:
1、能够捕获并分析IP分组,ARP分组,ICMP报文,TCP报文,UDP报文等(如果能解析到应用层更好)。
2、数据包显示界面尽量美观
0x02 分析
一开始很苦恼呀,这丢给一个小白怎么做呀,后来听大神说虚拟机里有两个例子,改改就能用了(实验课用的是什么中软的一套程序,网络信息安全 综合实验系统,在虚拟机里运行,拷了也会显示未授权,比较尴尬),知识类实验→网络攻防→实验3 网络嗅探→练习二、练习三,,请教请教学霸,大概看了看,差不多了
就是先捕获一个数据包,捕获数据包后,首先判断是ip分组还是arp分组,如果是ip分组,则调用判断高层协议子函数,进一步分析后输出分析结果,如果是arp分组,则输出判断结果,别的分组则直接输出结果
0x03 代码
#define _WIN32_WINNT 0x502
#include
#include
#include
#define IP_HITYPE 9
#define IP_SADDR 12
#define IP_DADDR 16
#define IP_HEADLEN 32#define ICMP_TYPE 0#define TCP_SPORT 0
#define TCP_DPORT 2#define UDP_SPORT 0
#define UDP_DPORT 2//Higher layer Protocol Type#define HI_UNKNOW -1
//IP layer
#define HI_ICMP 1
#define HI_IGMP 2
#define HI_TCP 3
#define HI_UDP 4
#define HI_OSPF 5void ParseTCPLayer( const u_char* cBuff );
void ParseUDPLayer( const u_char* cBuff );
void ParseICMPLayer( const u_char* cBuff );
int ParseIPLayer( const u_char* cBuff );int main()
{pcap_if_t *alldevs;pcap_if_t *d;pcap_t *adhandle;char errbuf[PCAP_ERRBUF_SIZE];char timestr[16];int res,inum&#61;0,i&#61;0;int iPacketsum&#61;0;struct tm *ltime;struct pcap_pkthdr *header;const u_char *pkt_data;int nIpHeaderLen &#61; 5;//! 获取设备列表if( pcap_findalldevs(&alldevs, errbuf ) &#61;&#61; -1 ){printf( "调用pcap_findalldevs时发生错误: %s\n", errbuf );exit(1);} //! 显示设备列表for( d&#61;alldevs; d; d&#61;d->next ){printf( "%d.%s", &#43;&#43;i, d->name );if(d->description)printf( " %s\n", d->description );elseprintf( " no description available\n" );}if( i&#61;&#61;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" );// 释放设备列表pcap_freealldevs(alldevs);return -1;}//! 跳转到选中设备for(d&#61;alldevs, i&#61;0; i
}// 解析IP层&#xff0c;获得上层协议类型
int ParseIPLayer( const u_char* cBuff )
{unsigned int usIp1(0), usIp2(0), usIp3(0), usIp4(0);unsigned int nIpHiType(0);memcpy( &usIp1, &cBuff[IP_SADDR], sizeof(char) );memcpy( &usIp2, &cBuff[IP_SADDR&#43;1], sizeof(char) );memcpy( &usIp3, &cBuff[IP_SADDR&#43;2], sizeof(char) );memcpy( &usIp4, &cBuff[IP_SADDR&#43;3], sizeof(char) );printf("****** IP Layer ******\n");printf("源IP地址&#xff1a;%d.%d.%d.%d ", usIp1,usIp2,usIp3,usIp4);memcpy( &usIp1, &cBuff[IP_DADDR], sizeof(char) );memcpy( &usIp2, &cBuff[IP_DADDR&#43;1], sizeof(char) );memcpy( &usIp3, &cBuff[IP_DADDR&#43;2], sizeof(char) );memcpy( &usIp4, &cBuff[IP_DADDR&#43;3], sizeof(char) );printf("目的IP地址&#xff1a;%d.%d.%d.%d ", usIp1,usIp2,usIp3,usIp4);memcpy( &nIpHiType, &cBuff[IP_HITYPE], sizeof(char) );switch ( nIpHiType){case 1:printf("IP高层协议类型&#xff1a;%d &#xff08;ICMP&#xff09;", nIpHiType);return HI_ICMP;case 2:printf("IP高层协议类型&#xff1a;%d &#xff08;IGMP&#xff09;", nIpHiType);return HI_IGMP;case 6:printf("IP高层协议类型&#xff1a;%d &#xff08;TCP&#xff09;", nIpHiType);return HI_TCP;case 17:printf("IP高层协议类型&#xff1a;%d &#xff08;UDP&#xff09;", nIpHiType);return HI_UDP;case 89:printf("IP高层协议类型&#xff1a;%d &#xff08;OSPF&#xff09;", nIpHiType);return HI_OSPF;default:printf("IP高层协议类型&#xff1a;%d &#xff08;未知的类型&#xff09;", nIpHiType);return HI_UNKNOW;}
}// 解析ICMP数据包&#xff0c;获得ICMP类型
void ParseICMPLayer( const u_char* cBuff )
{unsigned int nIcmpType(0);memcpy( &nIcmpType, &cBuff[ICMP_TYPE], sizeof(char) );printf("****** ICMP Layer ******\n");switch ( nIcmpType ){case 3:printf("ICMP类型&#xff1a;%d &#xff08;终点不可达&#xff09;", nIcmpType);break;case 4:printf("ICMP类型&#xff1a;%d &#xff08;源点抑制&#xff09;", nIcmpType);break;case 11:printf("ICMP类型&#xff1a;%d &#xff08;超时&#xff09;", nIcmpType);break;case 12:printf("ICMP类型&#xff1a;%d &#xff08;参数问题&#xff09;", nIcmpType);break;case 5:printf("ICMP类型&#xff1a;%d &#xff08;改变路由&#xff09;", nIcmpType);break;case 8:printf("ICMP类型&#xff1a;%d &#xff08;回送请求&#xff09;", nIcmpType);break;case 0:printf("ICMP类型&#xff1a;%d&#xff08;回送回答&#xff09;", nIcmpType);break;case 13:printf("ICMP类型&#xff1a;%d &#xff08;时间戳请求&#xff09;", nIcmpType);break;case 14:printf("ICMP类型&#xff1a;%d &#xff08;时间戳回答&#xff09;", nIcmpType);break;case 17:printf("ICMP类型&#xff1a;%d &#xff08;地址掩码请求&#xff09;", nIcmpType);break;case 18:printf("ICMP类型&#xff1a;%d &#xff08;地址掩码回答&#xff09;", nIcmpType);break;case 10:printf("ICMP类型&#xff1a;%d&#xff08;路由器询问&#xff09;", nIcmpType);break;case 9:printf("ICMP类型&#xff1a;%d &#xff08;路由器通告&#xff09;", nIcmpType);break;default:printf("ICMP类型&#xff1a;%d &#xff08;未知的ICMP类型&#xff09;", nIcmpType);}
}// 解析TCP数据包&#xff0c;获得源、目的端口对
void ParseTCPLayer( const u_char* cBuff )
{unsigned short usSPort(0);unsigned short usDPort(0);printf("****** TCP Layer ******\n");memcpy( &usSPort, &cBuff[TCP_SPORT], 2*sizeof(char) );usSPort &#61; htons( usSPort );switch ( usSPort ){case 20:printf("TCP源端口&#xff1a;%d&#xff08;FTP 数据&#xff09; ",usSPort);break;case 21:printf("TCP源端口&#xff1a;%d&#xff08;FTP 控制&#xff09; ",usSPort);break;case 23:printf("TCP源端口&#xff1a;%d&#xff08;TELNET&#xff09; ",usSPort);break;case 25:printf("TCP源端口&#xff1a;%d&#xff08;SMTP&#xff09; ",usSPort);break;case 80:printf("TCP源端口&#xff1a;%d&#xff08;HTTP&#xff09; ",usSPort);break;case 110:printf("TCP源端口&#xff1a;%d&#xff08;POP3&#xff09; ",usSPort);break;case 143:printf("TCP源端口&#xff1a;%d&#xff08;IMAP&#xff09; ",usSPort);break;default:printf("TCP源端口&#xff1a;%d ",usSPort);break;}memcpy( &usDPort, &cBuff[TCP_DPORT], 2*sizeof(char) );usDPort &#61; htons( usDPort );switch ( usDPort ){case 20:printf("TCP目的端口&#xff1a;%d&#xff08;FTP 数据&#xff09;\n", usDPort);break;case 21:printf("TCP目的端口&#xff1a;%d&#xff08;FTP 控制&#xff09;\n", usDPort);break;case 23:printf("TCP目的端口&#xff1a;%d&#xff08;TELNET&#xff09;\n", usDPort);break;case 25:printf("TCP目的端口&#xff1a;%d&#xff08;SMTP&#xff09;\n", usDPort);break;case 80:printf("TCP目的端口&#xff1a;%d&#xff08;HTTP&#xff09;\n", usDPort);break;case 110:printf("TCP目的端口&#xff1a;%d&#xff08;POP3&#xff09;\n", usDPort);break;case 143:printf("TCP目的端口&#xff1a;%d&#xff08;Imap&#xff09;\n", usDPort);break;default:printf("TCP目的端口&#xff1a;%d\n", usDPort);break;}
}// 解析UDP数据包&#xff0c;获得源、目的端口对
void ParseUDPLayer( const u_char* cBuff )
{unsigned short usSPort(0);unsigned short usDPort(0);printf("****** UDP Layer ******\n");memcpy( &usSPort, &cBuff[UDP_SPORT], 2*sizeof(char) );usSPort &#61; htons( usSPort );switch ( usSPort ){case 161:printf("UDP源端口&#xff1a;%d&#xff08;SNMP&#xff09; ", usSPort);break;case 67:printf("UDP源端口&#xff1a;%d&#xff08;DHCP&#xff09; ", usSPort);break;case 68:printf("UDP源端口&#xff1a;%d&#xff08;DHCP&#xff09; ", usSPort);break;case 53:printf("UDP源端口&#xff1a;%d&#xff08;DNS&#xff09; ", usSPort);break;case 520:printf("UDP源端口&#xff1a;%d&#xff08;RIP&#xff09; ", usSPort);break;case 138:printf("UDP源端口&#xff1a;%d&#xff08;NetBios&#xff09; ", usSPort);break;case 139:printf("UDP源端口&#xff1a;%d&#xff08;SMB&#xff09; ", usSPort);break;case 137:printf("UDP源端口&#xff1a;%d&#xff08;WINS&#xff09; ", usSPort);break;default:printf("UDP源端口&#xff1a;%d ", usSPort);break;}memcpy( &usDPort, &cBuff[UDP_DPORT], 2*sizeof(char) );usDPort &#61; htons( usDPort );switch ( usDPort ){case 161:printf("UDP目的端口&#xff1a;%d&#xff08;SNMP&#xff09;\n", usDPort);break;case 67:printf("UDP目的端口&#xff1a;%d&#xff08;DHCP&#xff09;\n", usDPort);break;case 68:printf("UDP目的端口&#xff1a;%d&#xff08;DHCP&#xff09;\n", usDPort);break;case 53:printf("UDP目的端口&#xff1a;%d&#xff08;DNS&#xff09;\n", usDPort);break;case 520:printf("UDP目的端口&#xff1a;%d&#xff08;RIP&#xff09;\n", usDPort);break;case 138:printf("UDP目的端口&#xff1a;%d&#xff08;NetBios&#xff09;\n", usDPort);break;case 139:printf("UDP目的端口&#xff1a;%d&#xff08;SMB&#xff09;\n", usDPort);break;case 137:printf("UDP目的端口&#xff1a;%d&#xff08;WINS&#xff09;\n", usDPort);break;default:printf("UDP目的端口&#xff1a;%d\n", usDPort);break;}
}
0x04 问题
给老师演示的时候&#xff0c;解析的ip分组再向下分析时&#xff0c;都是未知类型&#xff0c;而且ip地址看着也很奇怪&#xff0c;最后经过学霸指点&#xff0c;需要去掉前面的14个 char&#xff0c;为什么呢&#xff1f;请看下图&#xff1a;
前14个字节没什么用了&#xff0c;但是那些函数还是从头开始分析&#xff0c;那就比较尴尬了&#xff08;对了&#xff0c;第12,13字节&#xff0c;就是判断是ip分组还是arp分组的地方&#xff0c;ip分组是 0800&#xff0c;arp分组是0806&#xff09;
所以调用ParseIPLayer函数时&#xff0c;捕获到的数据需要向后偏移14个字节&#xff0c;也就是调用 ParseIPLayer( pkt_data &#43; 14 )
没有按老师要求用winsock编程接口&#xff0c;就这样吧&#xff0c;实现了就可以
0x05 废话
唉&#xff0c;看了一节课&#xff0c;也不如学霸的一句话&#xff0c;差距满满的
越微小的错误越难找到&#xff0c;&#xff0c;