C++ 解析IP数据包 (winpcap抓包)
//网络编程学习经验记录贴
#include <Winsock2.h>
#include<iostream>
#include "pcap.h"
#include "stdio.h"
#include<time.h>
#include <string>
#include <fstream> //文件的输入输出;
using namespace std;
/*下边是以太网的协议格式 */
struct ethernet_header
{
u_int8_t ether_dhost[6]; /*目的以太地址*/
u_int8_t ether_shost[6]; /*源以太网地址*/
u_int16_t ether_type; /*以太网类型*/
};
/*ip地址格式*/
typedef u_int32_t in_addr_t;
struct ip_header
{
#ifdef WORKS_BIGENDIAN
u_int8_t ip_version:4, /*version:4*/
ip_header_length:4; /*IP协议首部长度Header Length*/
#else
u_int8_t ip_header_length:4,
ip_version:4;
#endif
u_int8_t ip_tos; /*服务类型Differentiated Services Field*/
u_int16_t ip_length; /*总长度Total Length*/
u_int16_t ip_id; /*标识identification*/
u_int16_t ip_off; /*片偏移*/
u_int8_t ip_ttl; /*生存时间Time To Live*/
u_int8_t ip_protocol; /*协议类型(TCP或者UDP协议)*/
u_int16_t ip_checksum; /*首部检验和*/
struct in_addr ip_source_address; /*源IP*/
struct in_addr ip_destination_address; /*目的IP*/
};
/*关于tcp头部的定义*/
struct tcp_header
{
u_int16_t tcp_source_port; //源端口号
u_int16_t tcp_destination_port; //目的端口号
u_int32_t tcp_acknowledgement; //序号
u_int32_t tcp_ack; //确认号字段
#ifdef WORDS_BIGENDIAN
u_int8_t tcp_offset:4 ,
tcp_reserved:4;
#else
u_int8_t tcp_reserved:4,
tcp_offset:4;
#endif
u_int8_t tcp_flags;
u_int16_t tcp_windows; //窗口字段
u_int16_t tcp_checksum; //检验和
u_int16_t tcp_urgent_pointer; //紧急指针字段
};
/*下边实现tcp数据包分析的函数定义tcp_protocol_packet_callback*/
void tcp_protocol_packet_callback(u_char *argument,const struct pcap_pkthdr*
packet_header,const u_char* packet_content)
{
struct tcp_header *tcp_protocol ; /*tcp协议变量*/
u_char flags; /*标记*/
int header_length; /*头长度*/
u_short source_port; /*源端口*/
u_short destination_port; /*目的端口*/
u_short windows; /*窗口大小*/
u_short urgent_pointer; /*紧急指针*/
u_int sequence; /*****/
u_int acknowledgement; /*确认号*/
u_int16_t checksum; /*检验和*/
tcp_protocol=(struct tcp_header *) (packet_content+14+20); /*获得tcp首部内容*/
source_port =ntohs(tcp_protocol->tcp_source_port); /*获得源端口号*/
destination_port =ntohs(tcp_protocol->tcp_destination_port); /*获得目的端口号*/
header_length =tcp_protocol->tcp_offset *4; /*获得首部长度*/
sequence =ntohl(tcp_protocol->tcp_acknowledgement); /*获得****/
acknowledgement =ntohl(tcp_protocol->tcp_ack);
windows = ntohs(tcp_protocol->tcp_windows);
urgent_pointer = ntohs(tcp_protocol->tcp_urgent_pointer);
flags = tcp_protocol->tcp_flags;
checksum =ntohs (tcp_protocol->tcp_checksum);
printf("\n========== 运输层(TCP协议) ==========\n");
printf("源端口:\t %d\n",source_port);
printf("目的端口:\t %d\n",destination_port);
int min= ( destination_port <source_port )? destination_port : source_port ;
cout<<"应用层协议是:\t";
switch (min)
{
case 80:printf(" http 用于万维网(WWW)服务的超文本传输协议(HTTP)");
break;
case 21:printf(" ftp 文件传输协议(FTP)");
break;
case 23:printf(" telnet Telnet 服务 ");
break;
case 25:printf(" smtp 简单邮件传输协议(SMTP)");
break;
case 110:printf(" pop3 邮局协议版本3 ");
break;
case 443:printf(" https 安全超文本传输协议(HTTP) ");
break;
default :printf("【其他类型】 ");
break;
}
cout<<endl;
printf("***:\t %u \n",sequence);
printf("确认号:\t%u \n",acknowledgement);
printf("首部长度:\t%d \n",header_length);
printf("保留字段:\t%d \n",tcp_protocol->tcp_reserved);
printf("控制位:");
if (flags & 0x08) printf("\t【推送 PSH】");
if (flags & 0x10) printf("\t【确认 ACK】 ");
if (flags & 0x02) printf("\t【同步 SYN】");
if (flags & 0x20) printf("\t【紧急 URG】");
if (flags & 0x01) printf("\t【终止 FIN】");
if (flags & 0x04) printf("\t【复位 RST】");
printf("\n");
printf("窗口大小 :\t%d \n",windows);
printf("检验和 :\t%d\n",checksum);
printf("紧急指针字段 :\t%d\n",urgent_pointer);
}
/*下边实现IP数据包分析的函数定义ethernet_protocol_packet_callback*/
void ip_protocol_packet_callback(u_char *argument,const struct pcap_pkthdr*
packet_header,const u_char* packet_content)
{
struct ip_header *ip_protocol; /*ip协议变量*/
u_int header_length; /*长度*/
u_int offset; /*片偏移*/
u_char tos; /*服务类型*/
u_int16_t checksum; /*首部检验和*/
ip_protocol=(struct ip_header*) (packet_content+14); /*获得ip数据包的内容去掉以太头部*/
checksum=ntohs(ip_protocol->ip_checksum); /*获得校验和*/
header_length=ip_protocol->ip_header_length*4; /*获得长度*/
tos=ip_protocol->ip_tos; /*获得tos*/
offset=ntohs(ip_protocol->ip_off); /*获得偏移量*/
printf("\n########## 网络层(IP协议) ########### \n");
printf("IP版本:\t\tIPv%d\n",ip_protocol->ip_version);
printf("IP协议首部长度:\t%d\n",header_length);
printf("服务类型:\t%d\n",tos);
printf("总长度:\t\t%d\n",ntohs(ip_protocol->ip_length));/*获得总长度*/
printf("标识:\t\t%d\n",ntohs(ip_protocol->ip_id)); /*获得标识*/
printf("片偏移:\t\t%d\n",(offset&0x1fff)*8); /**/
printf("生存时间:\t%d\n",ip_protocol->ip_ttl); /*获得ttl*/
printf("首部检验和:\t%d\n",checksum);
printf("源IP:\t%s\n",inet_ntoa(ip_protocol->ip_source_address)); /*获得源ip地址*/
printf("目的IP:\t%s\n",inet_ntoa(ip_protocol->ip_destination_address));/*获得目的ip地址*/
printf("协议号:\t%d\n",ip_protocol->ip_protocol); /*获得协议类型*/
cout<<"\n传输层协议是:\t";
switch(ip_protocol->ip_protocol)
{
case 6 :
printf("TCP\n");
tcp_protocol_packet_callback(argument,packet_header,packet_content);
break; /*协议类型是6代表TCP*/
case 17:
printf("UDP\n");
break;/*17代表UDP*/
case 1:
printf("ICMP\n");
break;/*代表ICMP*/
case 2:
printf("IGMP\n");
break;/*代表IGMP*/
default :break;
}
}
void ethernet_protocol_packet_callback(u_char *argument,const struct pcap_pkthdr *packet_header,const u_char* packet_content)
{
u_short ethernet_type; /*以太网协议类型*/
struct ethernet_header *ethernet_protocol; /*以太网协议变量*/
u_char *mac_string;
static int packet_number=1;
printf("\n*** ****** ******* ********* ********* ******* ****** ***\n");
printf("\t!!!!!# 第【 %d 】个IP数据包被捕获 #!!!!!\n",packet_number);
printf("\n========== 链路层(以太网协议) ==========\n");
ethernet_protocol =(struct ethernet_header *) packet_content; /*获得一太网协议数据内容*/
printf("以太网类型为 :\t");
ethernet_type=ntohs(ethernet_protocol->ether_type); /*获得以太网类型*/
printf("%04x\n",ethernet_type);
switch(ethernet_type) /*判断以太网类型的值*/
{
case 0x0800 :
printf("网络层是:\tIPv4协议\n");break;
case 0x0806 :
printf("网络层是:\tARP协议\n");break;
case 0x8035 :
printf("网络层是:\tRARP 协议\n");break;
default: break;
}
/*获得Mac源地址*/
printf("Mac源地址:\t");
mac_string=ethernet_protocol->ether_shost;
printf("%02x:%02x:%02x:%02x:%02x:%02x:\n",*mac_string,*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
/*获得Mac目的地址*/
printf("Mac目的地址:\t");
mac_string=ethernet_protocol->ether_dhost;
printf("%02x:%02x:%02x:%02x:%02x:%02x:\n",*mac_string,*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
switch (ethernet_type)
{
case 0x0800:
/*如果上层是IPv4ip协议,就调用分析ip协议的函数对ip包进行贩治*/
ip_protocol_packet_callback(argument,packet_header,packet_content);
break;
default :break;
}
packet_number++;
}
main()
{
cout<<"========== 解析IP数据包 ==========\n";
pcap_if_t *alldevs;
pcap_if_t *d;
int inum=0;
int i=0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
/* 获得网卡的列表 */
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in 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("\n没有发现接口!确保安装了LibPcap.\n");
return -1;
}
printf("\n【输入要选择打开的网卡号 (1-%d)】:\t",i);
scanf("%d", &inum); //输入要选择打开的网卡号
if(inum < 1 || inum > i) //判断号的合法性
{
printf("\n网卡号超出范围.\n");
/*释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 找到要选择的网卡结构 */
for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
/* 打开选择的网卡 */
if ( (adhandle= pcap_open_live(d->name, /* 设备名称*/
65536, /* 最大值.*/
/*65536允许整个包在所有mac电脑上被捕获.*/
1, /* 混杂模式*/
/*
混杂模式是指一台主机能够接受所有经过它的数据流,不论这个数据流的目的地址是不是它,它都会接受这个数据包。也就是说,混杂模式下,网卡会把所有的发往它的包全部都接收。在这种情况下,可以接收同一集线器局域网的所有数据。
*/
1000, /* 读超时为1秒*/
errbuf /* error buffer*/
) ) == NULL)
{
fprintf(stderr,"\n无法打开适配器.\t %s 不被LibPcap支持\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
printf("\n监听 %s...\n", d->description);
/* 现在,我们不再需要设备列表, 释放它 */
pcap_freealldevs(alldevs);
int cnt = -1;
cout<<"\n【将要捕获数据包的个数】:\t\t";
cin>>cnt;
/* 开始以回调的方式捕获包
函数名称:int pcap_loop(pcap_t * p,int cnt, pcap_handler callback, uchar * user);
函数功能:捕获数据包,不会响应pcap_open_live()函数设置的超时时间
*/
pcap_loop(adhandle,cnt, ethernet_protocol_packet_callback, NULL);
cout<<"\n\t!!!!!# 解析IP数据包结束 #!!!!!\n";
return 0;
}
/*
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"wpcap.lib")
*/