一.简介
pcap文件是常用的数据报存储格式,可以理解为就是一种文件格式,只不过里面的数据是按照特定格式存储的,所以我们想要解析里面的数据,也必须按照一定的格式。普通的记事本打开pcap文件显示的是乱码,用安装了HEX-Editor插件的Notepad++打开,能够以16进制数据的格式显示,用wireshark这种抓包工具就可以正常打开这种文件,愉快地查看里面的网络数据报了,同时wireshark也可以生成这种格式的文件。当然这些工具只是我经常使用的,还有很多其它能够查看pcap文件的工具。
初识Pcap文件
在开始读取pcap文件之前,先让我们来看看Pcap文件的大概结构。
pcap文件格式
如上图所示,pcap文件的总体结构就是文件头-数据包头1-数据包1-数据包头2-数据包2等形式,为什么要这么设计,看完下面的你就懂了。
1.Pcap Header
文件头,每一个pcap文件只有一个文件头,总共占24(B)字节,以下是总共7个字段的含义。
Magic(4B):标记文件开始,并用来识别文件和字节顺序。值可以为0xa1b2c3d4或者0xd4c3b2a1,如果是0xa1b2c3d4表示是大端模式,按照原来的顺序一个字节一个字节的读,如果是0xd4c3b2a1表示小端模式,下面的字节都要交换顺序。现在的电脑大部分是小端模式。
Major(2B):当前文件的主要版本号,一般为0x0200
Minor(2B):当前文件的次要版本号,一般为0x0400
ThisZone(4B):当地的标准事件,如果用的是GMT则全零,一般全零
SigFigs(4B):时间戳的精度,一般为全零
SnapLen(4B):最大的存储长度,设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将值设置为65535
LinkType(4B):链路类型。解析数据包首先要判断它的LinkType,所以这个值很重要。一般的值为1,即以太网
常用的LinkType(链路类型):
0 BSD loopback devices, except for later OpenBSD
1 Ethernet, and Linux loopback devices
6 802.5 Token Ring
7 ARCnet
8 SLIP
9 PPP
10 FDDI
100 LLC/SNAP-encapsulated ATM
101 "raw IP", with no link
102 BSD/OS SLIP
103 BSD/OS PPP
104 Cisco HDLC
105 802.11
108 later OpenBSD loopback devices (with the AF_value in network byte order)
113 special Linux "cooked" capture
114 LocalTalk
2.Packet Header
数据包头可以有多个,每个数据包头后面都跟着真正的数据包。数据包头则依次为:时间戳(秒)、时间戳(微妙)、抓包长度和实际长度,依次各占4个字节。以下是Packet Header的4个字段含义
Timestamp(4B):时间戳高位,精确到seconds,这是Unix时间戳。捕获数据包的时间一般是根据这个值
Timestamp(4B):时间戳低位,能够精确到microseconds
Caplen(4B):当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
Len(4B):离线数据长度,网路中实际数据帧的长度,一般不大于Caplen,多数情况下和Caplen值一样
3.Packet Data
Packet是链路层的数据帧,长度就是Packet Header中定义的Caplen值,所以每个Packet Header后面都跟着Caplen长度的Packet Data。也就是说pcap文件并没有规定捕获的数据帧之间有什么间隔字符串。Packet数据帧部分的格式就是标准的网络协议格式了。
最后来个实例,pcap文件的16进制格式
红色部分是Pcap Header,蓝色部分是Packet Header,当然这是从pcap文件的起始部分开始的。希望别嫌弃这扭曲的线条
Pcap Header的Magic:d4 c3 b2 a1,表示是小端模式,后面的字节从后往前读。
Pcap Header的Major:02 00,计算机读的应该是00 02。最大存储长度SnapLen:ff ff 00 00 ,同理计算机读的应该是00 00 ff ff,所以是2的16次方减一,是65535个字节。LinkType:01 00 00 00 ,实际是00 00 00 01,是以太网类型。
蓝色部分的Packet Header我就不一一说了,重点关注Caplen:c5 01 00 00,计算机读的是00 00 01 c5,转换成十进制就是453,所以后面的453个字节都是一个数据帧。之后就又是一个Pcap Header,如此循环。
同样的文件用wireshark打开看一下:
可以看到第一个数据帧,就是453个字节。还可以看底下的数据帧的16进制原始数据和上面蓝色部分之后的数据是不是一样。
读取各个数据包到单个pcap文件
c代码:
pcap_header.h
-
#pragma pack( push, 1)
-
// 为了保证在windows和linux下都能正常编译,放弃使用INT64或者_int_64
-
typedef short _Int16;
-
typedef long _Int32;
-
typedef char Byte;
-
// Pcap文件头
-
struct __file_header
-
{
-
_Int32 iMagic;
-
_Int16 iMaVersion;
-
_Int16 iMiVersion;
-
_Int32 iTimezone;
-
_Int32 iSigFlags;
-
_Int32 iSnapLen;
-
_Int32 iLinkType;
-
};
-
// 数据包头
-
struct __pkthdr
-
{
-
_Int32 iTimeSecond;
-
_Int32 iTimeSS;
-
_Int32 iPLength;
-
_Int32 iLength;
-
};
-
#pragma pack( pop)
main.c
-
#include<stdio.h>
-
#include"pcap_header.h"
-
#include<memory.h>
-
#include<stdlib.h>
-
#include<math.h>
-
int main()
-
{
-
struct __pkthdr data;
-
struct __file_header header;
-
FILE* pFile = fopen( "iupsc.pcap", "rb");
-
if( pFile == 0)
-
{
-
printf( "打开pcap文件失败");
-
return 0;
-
}
-
fseek( pFile, 0, SEEK_END);
-
long iFileLen = ftell( pFile);
-
fseek( pFile, 0, SEEK_SET);
-
Byte* pBuffer = (Byte*)malloc( iFileLen);
-
fread( (void*)pBuffer, 1, iFileLen, pFile);
-
fclose( pFile);
-
memcpy( (void*)&header, (void*)(pBuffer)
-
, sizeof(struct __file_header));
-
int iIndex = sizeof(struct __file_header);
-
int iNo = 1;
-
while(iIndex <= iFileLen)
-
{
-
memcpy( (void*)&data, (void*)(pBuffer + iIndex)
-
, sizeof(struct __pkthdr));
-
char strPath[51];
-
sprintf( strPath, "export/%d-%d.pcap", iNo++, (int)data.iTimeSecond);
-
strPath[50] = '\0';
-
FILE* pwFile = fopen( strPath, "wb");
-
fwrite((void*)&header, 1, sizeof(struct __file_header), pwFile);
-
fwrite( (void*)(pBuffer + iIndex), 1,
-
sizeof(struct __pkthdr) + data.iPLength, pwFile);
-
fclose( pwFile);
-
iIndex += sizeof(struct __pkthdr) + data.iPLength;
-
}
-
free( pBuffer);
-
printf( "成功导出%d个文件", iNo - 1);
-
return 1;
-
}