pcap文件格式及文件解析.docx
《pcap文件格式及文件解析.docx》由会员分享,可在线阅读,更多相关《pcap文件格式及文件解析.docx(14页珍藏版)》请在冰豆网上搜索。
pcap文件格式及文件解析
第一部分:
PCAP包文件格式
一基本格式:
文件头数据包头数据报数据包头数据报......
二、文件头:
文件头结构体
sturctpcap_file_header
{
DWORD magic;
DWORD version_major;
DWORD version_minor;
DWORD thiszone;
DWORD sigfigs;
DWORD snaplen;
DWORD linktype;
}
说明:
1、标识位:
32位的,这个标识位的值是16进制的0xa1b2c3d4。
a32-bit magicnumber,Themagicnumberhasthevaluehexa1b2c3d4.
2、主版本号:
16位,默认值为0x2。
a16-bit majorversionnumber,Themajorversionnumbershouldhavethevalue2.
3、副版本号:
16位,默认值为0x04。
a16-bit minorversionnumber,Theminorversionnumbershouldhavethevalue4.
4、区域时间:
32位,实际上该值并未使用,因此可以将该位设置为0。
a32-bit timezoneoffsetfieldthatactuallynotused,soyoucan(andprobablyshould)justmakeit0;
5、精确时间戳:
32位,实际上该值并未使用,因此可以将该值设置为0。
a32-bit timestampaccuracyfieldthanotactuallyused,soyoucan(andprobablyshould)justmakeit0;
6、数据包最大长度:
32位,该值设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将该值设置为65535;例如:
想获取数据包的前64字节,可将该值设置为64。
a32-bit snapshotlength"field;Thesnapshotlengthfieldshouldbethemaximumnumberofbytesperpacketthatwillbecaptured.Iftheentirepacketiscaptured,makeit65535;ifyouonlycapture,forexample,thefirst64bytesofthepacket,makeit64.
7、链路层类型:
32位,数据包的链路层包头决定了链路层的类型。
a32-bitlinklayertypefield.Thelink-layertypedependsonthetypeoflink-layerheaderthatthe
packetsinthecapturefilehave:
以下是数据值与链路层类型的对应表
0 BSD loopbackdevices,exceptforlaterOpenBSD
1 Ethernet,andLinuxloopbackdevices 以太网类型,大多数的数据包为这种类型。
6 802.5TokenRing
7 ARCnet
8 SLIP
9 PPP
10 FDDI
100 LLC/SNAP-encapsulatedATM
101 rawIP,withnolink
102 BSD/OSSLIP
103 BSD/OSPPP
104 CiscoHDLC
105 802.11
108 laterOpenBSDloopbackdevices(withtheAF_valueinnetworkbyteorder)
113 specialLinuxcookedcapture
114 LocalTalk
三packet数据包头:
structpcap_pkthdr
{
structtim ts;
DWORD caplen;
DWORD len;
}
structtim
{
DWORD GMTtime;
DWORD microTime
}
说明:
1、时间戳,包括:
秒计时:
32位,一个UNIX格式的精确到秒时间值,用来记录数据包抓获的时间,记录方式是记录从格林尼治时间的1970年1月1日00:
00:
00到抓包时经过的秒数;
微秒计时:
32位,抓取数据包时的微秒值。
atimestamp,consistingof:
aUNIX-formattime-in-secondswhenthepacketwascaptured,i.e.thenumberofsecondssinceJanuary1,1970,00:
00:
00GMT(thatGMT,*NOT*localtime!
);
thenumberofmicrosecondssincethatsecondwhenthepacketwascaptured;
2、数据包长度:
32位,标识所抓获的数据包保存在pcap文件中的实际长度,以字节为单位。
a32-bitvaluegivingthenumberofbytesofpacketdatathatwerecaptured;
3、数据包实际长度:
所抓获的数据包的真实长度,如果文件中保存不是完整的数据包,那么这个值可能要比前面的数据包长度的值大。
a32-bitvaluegivingtheactuallengthofthepacket,inbytes(whichmaybegreaterthanthepreviousnumber,ifyouarenotsavingtheentirepacket).
四:
packet数据:
即Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就是说:
PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。
我们需要靠第一个Packet包确定。
最后,Packet数据部分的格式其实就是标准的网路协议格式了可以任何网络教材上找得到。
五:
举例分析
图中最开始的绿色部分就是24Bytes的PcapHeader,接下来红色的16Bytes是第一个消息的PcapHeader。
后面的红色的16Bytes是第二个消息的PcapHeader。
两块蓝色的部分分别是两个消息从链路层开始的完整内容。
在网络上实际传输的数据包在数据链路层上每一个Packet开始都会有7个用于同步的字节和一个用于标识该Packet开始的字节,最后还会有四个CRC校验字节;而PCAP文件中会把前8个字节和最后4个校验自己去掉,因为这些信息对于协议分析是没有用的。
用Wireshark打开一个PCAP数据包,每条消息的所有field会被解析出来并会按照协议层次折叠起来。
第一层显示的是FrameXXX,这一级别没有对应某层具体的协议,而是对本条消息的一个概括性总结,描述了一些有用的概括性信息,比如从里面我们可以看到本条消息各种协议的层次关系,展开其它协议层之后对应的是该协议的各个域,如下图所示:
第二部分:
PCAP文件解析
1、pcap解析工具Xplico
Xplico是一个从pcap文件中解析出IP流量数据的工具,可解析每个邮箱(POP,IMAP,和SMTP协议),所有HTTP内容,VoIPcalls(SIP)等等
2、C语言实现PCAP文件分析
实现步骤:
1)用Wireshark软件抓包得到test.pcap文件
2)程序:
分析pcap文件头->分析pcap_pkt头->分析帧头->分析ip头->分析tcp头->分析http信息
#include
#include
#include
#include
#include
#defineBUFSIZE10240
#defineSTRSIZE1024
typedeflongbpf_int32;
typedefunsignedlongbpf_u_int32;
typedefunsignedshort u_short;
typedefunsignedlongu_int32;
typedefunsignedshortu_int16;
typedefunsignedcharu_int8;
//pacp文件头结构体
structpcap_file_header
{
bpf_u_int32magic; /*0xa1b2c3d4*/
u_shortversion_major; /*magjorVersion2*/
u_shortversion_minor; /*magjorVersion4*/
bpf_int32thiszone; /*gmttolocalcorrection*/
bpf_u_int32sigfigs; /*accuracyoftimestamps*/
bpf_u_int32snaplen; /*maxlengthsavedportionofeachpkt*/
bpf_u_int32linktype; /*datalinktype(LINKTYPE_*)*/
};
//时间戳
structtime_val
{
longtv_sec; /*seconds含义同time_t对象的值*/
longtv_usec; /*andmicroseconds*/
};
//pcap数据包头结构体
structpcap_pkthdr
{
structtime_valts; /*timestamp*/
bpf_u_int32caplen;/*lengthofportionpresent*/
bpf_u_int32len; /*lengththispacket(offwire)*/
};
//数据帧头
typedefstructFramHeader_t
{//Pcap捕获的数据帧头
u_int8DstMAC[6];//目的MAC地址
u_int8SrcMAC[6];//源MAC地址
u_shortFrameType; //帧类型
}FramHeader_t;
//IP数据报头
typedefstructIPHeader_t
{//IP数据报头
u_int8Ver_HLen; //版本+报头长度
u_int8TOS; //服务类型
u_int16TotalLen; //总长度
u_int16ID;//标识
u_int16Flag_Segment; //标志+片偏移
u_int8TTL; //生存周期
u_int8Protocol; //协议类型
u_int16Checksum; //头部校验和
u_int32SrcIP;//源IP地址
u_int32DstIP;//目的IP地址
}IPHeader_t;
//TCP数据报头
typedefstructTCPHeader_t
{//TCP数据报头
u_int16SrcPort;//源端口
u_int16DstPort;//目的端口
u_int32SeqNO;//序号
u_int32AckNO;//确认号
u_int8HeaderLen;//数据报头的长度(4bit)+保留(4bit)
u_int8Flags;//标识TCP不同的控制消息
u_int16Window;//窗口大小
u_int16Checksum;//校验和
u_int16UrgentPointer; //紧急指针
}TCPHeader_t;
//
voidmatch_http(FILE*fp,char*head_str,char*tail_str,char*buf,inttotal_len);//查找http信息函数
//
intmain()
{
structpcap_file_header*file_header;
structpcap_pkthdr*ptk_header;
IPHeader_t*ip_header;
TCPHeader_t*tcp_header;
FILE*fp,*output;
int pkt_offset,i=0;
intip_len,http_len,ip_proto;
intsrc_port,dst_port,tcp_flags;
charbuf[BUFSIZE],my_time[STRSIZE];
charsrc_ip[STRSIZE],dst_ip[STRSIZE];
char host[STRSIZE],uri[BUFSIZE];
//初始化
file_header=(structpcap_file_header*)malloc(sizeof(structpcap_file_header));
ptk_header =(structpcap_pkthdr*)malloc(sizeof(structpcap_pkthdr));
ip_header=(IPHeader_t*)malloc(sizeof(IPHeader_t));
tcp_header=(TCPHeader_t*)malloc(sizeof(TCPHeader_t));
memset(buf,0,sizeof(buf));
//
if((fp=fopen(“test.pcap”,”r”))==NULL)
{
printf(“error:
cannotopenpcapfile\n”);
exit(0);
}
if((output=fopen(“output.txt”,”w+”))==NULL)
{
printf(“error:
cannotopenoutputfile\n”);
exit(0);
}
//开始读数据包
pkt_offset=24;//pcap文件头结构24个字节
while(fseek(fp,pkt_offset,SEEK_SET)==0)//遍历数据包
{
i++;
//pcap_pkt_header16byte
if(fread(ptk_header,16,1,fp)!
=1)//读pcap数据包头结构
{
printf(“\nreadendofpcapfile\n”);
break;
}
pkt_offset+=16+ptk_header->caplen; //下一个数据包的偏移值
strftime(my_time,sizeof(my_time),“%Y-%m-%d%T”,localtime(&(ptk_header->ts.tv_sec)));//获取时间
//printf(“%d:
%s\n”,i,my_time);
//数据帧头14字节
fseek(fp,14,SEEK_CUR);//忽略数据帧头
//IP数据报头20字节
if(fread(ip_header,sizeof(IPHeader_t),1,fp)!
=1)
{
printf(“%d:
cannotreadip_header\n”,i);
break;
}
inet_ntop(AF_INET,(void*)&(ip_header->SrcIP),src_ip,16);
inet_ntop(AF_INET,(void*)&(ip_header->DstIP),dst_ip,16);
ip_proto=ip_header->Protocol;
ip_len=ip_header->TotalLen;//IP数据报总长度
//printf(“%d:
src=%s\n”,i,src_ip);
if(ip_proto!
=0×06)//判断是否是TCP协议
{
continue;
}
//TCP头20字节
if(fread(tcp_header,sizeof(TCPHeader_t),1,fp)!
=1)
{
printf(“%d:
cannotreadip_header\n”,i);
break;
}
src_port=ntohs(tcp_header->SrcPort);
dst_port=ntohs(tcp_header->DstPort);
tcp_flags=tcp_header->Flags;
//printf(“%d:
src=%x\n”,i,tcp_flags);
if(tcp_flags==0×18)//(PSH,ACK)3路握手成功后
{
if(dst_port==80)//HTTPGET请求
{
http_len=ip_len–40;//http报文长度
match_http(fp,“Host:
“,“\r\n”,host,http_len);//查找host值
match_http(fp,“GET“,“HTTP”,uri,http_len);//查找uri值
sprintf(buf,“%d:
%s src=%s:
%d dst=%s:
%d %s%s\r\n”,i,my_time,src_ip,src_port,dst_ip,dst_port,host,uri);
//printf(“%s”,buf);
if(fwrite(buf,strlen(buf),1,output)!
=1)
{
printf(“outputfilecannotwrite”);
break;
}
}
}
}//endwhile
fclose(fp);
fclose(output);
return0;
}
//查找HTTP信息
voidmatch_http(FILE*fp,char*head_str,char*tail_str,char*buf,inttotal_len)
{
inti;
inthttp_offset;
inthead_len,tail_len,val_len;
charhead_tmp[STRSIZE],tail_tmp[STRSIZE];
//初始化
memset(head_tmp,0,sizeof(head_tmp));
memset(tail_tmp,0,sizeof(tail_tmp));
head_len=strlen(head_str);
tail_len=strlen(tail_str);
//查找head_str
http_offset=ftell(fp);//记录下HTTP报文初始文件偏移
while((head_tmp[0]=fgetc(fp))!
=EOF)//逐个字节遍历
{
if((ftell(fp)–http_offset)>total_len)//遍历完成
{
sprintf(buf,“cannotfind%s\r\n”,head_str);
exit(0);
}
if(head_tmp[0]==*head_str)//匹配到第一个字符
{
for(i=1;i{
head_tmp[i]=fgetc(fp);
if(head_tmp[i]!
=*(head_str+i))
break;
}
if(i==head_len)//匹配head_str成功,停止遍历
break;
}
}
//printf(“head_tmp=%s\n”,head_tmp);
//查找tail_str
val_len=0;
while((tail_tmp[0]=fgetc(fp))!
=EOF)//遍历
{
if((ftell(fp)–http_offset)>total_len)//遍历完成
{
sprintf(buf,“cannotfind%s\r\n”,tail_str);
exit(0);
}
buf[val_len++]=tail_tmp[0];//用buf存储value直到查找到tail_str
if(tail_tmp[0]==*tail_str)//匹配到第一个字符
{
for(i=1;i{
tail_tmp[i]=fgetc(fp);
if(tail_tmp[i]!
=*(tail_str+i))
break;
}
if(i==tail_len)//匹配head_str成功,停止遍历
{
buf[val_len-1]=0;//清除多余的一个字符
break;
}
}
}
//printf(“val=%s\n”,buf);
fseek(fp,http_offset,