0x01 前言
本来以为可以划水过去的网络安全实验课,突然就被老师布置了一个作业,写一个网络嗅探器,大概要求如下:
利用Winsock编程接口,设计实现一个能够在共享式局域网中完成网络抓包并分析所截获数据包的嗅探程序。
要求:
1、能够捕获并分析IP分组,ARP分组,ICMP报文,TCP报文,UDP报文等(如果能解析到应用层更好)。
2、数据包显示界面尽量美观
0x02 分析
一开始很苦恼呀,这丢给一个小白怎么做呀,后来听大神说虚拟机里有两个例子,改改就能用了(实验课用的是什么中软的一套程序,网络信息安全 综合实验系统,在虚拟机里运行,拷了也会显示未授权,比较尴尬),知识类实验→网络攻防→实验3 网络嗅探→练习二、练习三,,请教请教学霸,大概看了看,差不多了
就是先捕获一个数据包,捕获数据包后,首先判断是ip分组还是arp分组,如果是ip分组,则调用判断高层协议子函数,进一步分析后输出分析结果,如果是arp分组,则输出判断结果,别的分组则直接输出结果
0x03 代码
#define _WIN32_WINNT 0x502
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wpcap")
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
//Key offset
#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 5
void 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=0,i=0;
int iPacketsum=0;
struct tm *ltime;
struct pcap_pkthdr *header;
const u_char *pkt_data;
int nIpHeaderLen = 5;
//! 获取设备列表
if( pcap_findalldevs(&alldevs, errbuf ) == -1 )
{
printf( "调用pcap_findalldevs时发生错误: %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 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" );
// 释放设备列表
pcap_freealldevs(alldevs);
return -1;
}
//! 跳转到选中设备
for(d=alldevs, i=0; inext,i++);
//! 打开网络设备
if( (adhandle=pcap_open_live(d->name, // 设备名称
65535, // 捕获数据包最大长度(包括链路层数据)
1, // 网卡混杂模式工作
1000, // 读超时
errbuf // 错误缓冲
)) == NULL )
{
printf( "Unable to open the adapter. %s is not supported by Winpcap\n" );
// 释放设备列表
pcap_freealldevs(alldevs);
return -1;
}
printf( "\nlistening on %s...\n", d->description );
pcap_freealldevs(alldevs); // 此时已不再需要设备列表,释放设备列表
//! 开始捕获数据包,这里没有使用pcap_loop调用回调函数的方法,而是循环调用pcap_next_ex。
while( (res=pcap_next_ex( adhandle, &header, &pkt_data)) >=0 )
{
if( res == 0 )
{
// 超时
continue;
}
iPacketsum++;
printf("\n ------------------------------------------------\n");
// 判断数据包类型
printf("数据包类型编码或长度:%.2x%.2x\n",pkt_data[12],pkt_data[13]);
if(pkt_data[12] == 8 && pkt_data[13] == 0){
printf("This is an ip data packet!\n");
// 解析ip数据包
switch( ParseIPLayer( pkt_data +14 ) )
{
case HI_ICMP:
ParseICMPLayer( &pkt_data[nIpHeaderLen*4] );
break;
case HI_TCP:
ParseTCPLayer( &pkt_data[nIpHeaderLen*4] );
break;
case HI_UDP:
ParseUDPLayer( &pkt_data[nIpHeaderLen*4] );
break;
}
}else if(pkt_data[12] == 8 && pkt_data[13] == 6){
printf("This is an ARP data packet!\n");
}else{
printf("Other type data packet!\n");
}
// 捕获时间
ltime = localtime(&header->ts.tv_sec);
strftime( timestr, sizeof(timestr), "%H:%M:%S", ltime);
printf( " No %d. 时间%s. 长度%d\n", iPacketsum,timestr, header->len );
// 单行16字节显示数据包内容
for(unsigned int j=1; jlen+1; j++)
{
printf( " %.2x", pkt_data[j-1] );
if( (j % 16) == 0 )
printf("\n");
}
}
//! 关闭设备
pcap_close( adhandle );
return 0;
}
// 解析IP层,获得上层协议类型
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+1], sizeof(char) );
memcpy( &usIp3, &cBuff[IP_SADDR+2], sizeof(char) );
memcpy( &usIp4, &cBuff[IP_SADDR+3], sizeof(char) );
printf("****** IP Layer ******\n");
printf("源IP地址:%d.%d.%d.%d ", usIp1,usIp2,usIp3,usIp4);
memcpy( &usIp1, &cBuff[IP_DADDR], sizeof(char) );
memcpy( &usIp2, &cBuff[IP_DADDR+1], sizeof(char) );
memcpy( &usIp3, &cBuff[IP_DADDR+2], sizeof(char) );
memcpy( &usIp4, &cBuff[IP_DADDR+3], sizeof(char) );
printf("目的IP地址:%d.%d.%d.%d ", usIp1,usIp2,usIp3,usIp4);
memcpy( &nIpHiType, &cBuff[IP_HITYPE], sizeof(char) );
switch ( nIpHiType)
{
case 1:
printf("IP高层协议类型:%d (ICMP)", nIpHiType);
return HI_ICMP;
case 2:
printf("IP高层协议类型:%d (IGMP)", nIpHiType);
return HI_IGMP;
case 6:
printf("IP高层协议类型:%d (TCP)", nIpHiType);
return HI_TCP;
case 17:
printf("IP高层协议类型:%d (UDP)", nIpHiType);
return HI_UDP;
case 89:
printf("IP高层协议类型:%d (OSPF)", nIpHiType);
return HI_OSPF;
default:
printf("IP高层协议类型:%d (未知的类型)", nIpHiType);
return HI_UNKNOW;
}
}
// 解析ICMP数据包,获得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类型:%d (终点不可达)", nIcmpType);
break;
case 4:
printf("ICMP类型:%d (源点抑制)", nIcmpType);
break;
case 11:
printf("ICMP类型:%d (超时)", nIcmpType);
break;
case 12:
printf("ICMP类型:%d (参数问题)", nIcmpType);
break;
case 5:
printf("ICMP类型:%d (改变路由)", nIcmpType);
break;
case 8:
printf("ICMP类型:%d (回送请求)", nIcmpType);
break;
case 0:
printf("ICMP类型:%d(回送回答)", nIcmpType);
break;
case 13:
printf("ICMP类型:%d (时间戳请求)", nIcmpType);
break;
case 14:
printf("ICMP类型:%d (时间戳回答)", nIcmpType);
break;
case 17:
printf("ICMP类型:%d (地址掩码请求)", nIcmpType);
break;
case 18:
printf("ICMP类型:%d (地址掩码回答)", nIcmpType);
break;
case 10:
printf("ICMP类型:%d(路由器询问)", nIcmpType);
break;
case 9:
printf("ICMP类型:%d (路由器通告)", nIcmpType);
break;
default:
printf("ICMP类型:%d (未知的ICMP类型)", nIcmpType);
}
}
// 解析TCP数据包,获得源、目的端口对
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 = htons( usSPort );
switch ( usSPort )
{
case 20:
printf("TCP源端口:%d(FTP 数据) ",usSPort);
break;
case 21:
printf("TCP源端口:%d(FTP 控制) ",usSPort);
break;
case 23:
printf("TCP源端口:%d(TELNET) ",usSPort);
break;
case 25:
printf("TCP源端口:%d(SMTP) ",usSPort);
break;
case 80:
printf("TCP源端口:%d(HTTP) ",usSPort);
break;
case 110:
printf("TCP源端口:%d(POP3) ",usSPort);
break;
case 143:
printf("TCP源端口:%d(IMAP) ",usSPort);
break;
default:
printf("TCP源端口:%d ",usSPort);
break;
}
memcpy( &usDPort, &cBuff[TCP_DPORT], 2*sizeof(char) );
usDPort = htons( usDPort );
switch ( usDPort )
{
case 20:
printf("TCP目的端口:%d(FTP 数据)\n", usDPort);
break;
case 21:
printf("TCP目的端口:%d(FTP 控制)\n", usDPort);
break;
case 23:
printf("TCP目的端口:%d(TELNET)\n", usDPort);
break;
case 25:
printf("TCP目的端口:%d(SMTP)\n", usDPort);
break;
case 80:
printf("TCP目的端口:%d(HTTP)\n", usDPort);
break;
case 110:
printf("TCP目的端口:%d(POP3)\n", usDPort);
break;
case 143:
printf("TCP目的端口:%d(Imap)\n", usDPort);
break;
default:
printf("TCP目的端口:%d\n", usDPort);
break;
}
}
// 解析UDP数据包,获得源、目的端口对
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 = htons( usSPort );
switch ( usSPort )
{
case 161:
printf("UDP源端口:%d(SNMP) ", usSPort);
break;
case 67:
printf("UDP源端口:%d(DHCP) ", usSPort);
break;
case 68:
printf("UDP源端口:%d(DHCP) ", usSPort);
break;
case 53:
printf("UDP源端口:%d(DNS) ", usSPort);
break;
case 520:
printf("UDP源端口:%d(RIP) ", usSPort);
break;
case 138:
printf("UDP源端口:%d(NetBios) ", usSPort);
break;
case 139:
printf("UDP源端口:%d(SMB) ", usSPort);
break;
case 137:
printf("UDP源端口:%d(WINS) ", usSPort);
break;
default:
printf("UDP源端口:%d ", usSPort);
break;
}
memcpy( &usDPort, &cBuff[UDP_DPORT], 2*sizeof(char) );
usDPort = htons( usDPort );
switch ( usDPort )
{
case 161:
printf("UDP目的端口:%d(SNMP)\n", usDPort);
break;
case 67:
printf("UDP目的端口:%d(DHCP)\n", usDPort);
break;
case 68:
printf("UDP目的端口:%d(DHCP)\n", usDPort);
break;
case 53:
printf("UDP目的端口:%d(DNS)\n", usDPort);
break;
case 520:
printf("UDP目的端口:%d(RIP)\n", usDPort);
break;
case 138:
printf("UDP目的端口:%d(NetBios)\n", usDPort);
break;
case 139:
printf("UDP目的端口:%d(SMB)\n", usDPort);
break;
case 137:
printf("UDP目的端口:%d(WINS)\n", usDPort);
break;
default:
printf("UDP目的端口:%d\n", usDPort);
break;
}
}
0x04 问题
给老师演示的时候,解析的ip分组再向下分析时,都是未知类型,而且ip地址看着也很奇怪,最后经过学霸指点,需要去掉前面的14个 char,为什么呢?请看下图:
前14个字节没什么用了,但是那些函数还是从头开始分析,那就比较尴尬了(对了,第12,13字节,就是判断是ip分组还是arp分组的地方,ip分组是 0800,arp分组是0806)
所以调用ParseIPLayer函数时,捕获到的数据需要向后偏移14个字节,也就是调用 ParseIPLayer( pkt_data + 14 )
没有按老师要求用winsock编程接口,就这样吧,实现了就可以
0x05 废话
唉,看了一节课,也不如学霸的一句话,差距满满的
越微小的错误越难找到,,