1:pcap文件格式
pcap文件主要包含了三个部分,pcap文件头,数据包头,数据包内容。在磁盘上的存储格式为
文件头 + 数据包头[0] + 数据包内容[0] + 数据包头[1] + 数据包内容[1] + … +数据包头[N] + 数据包内容[N]
每一部分包含的内容如下
Pcap文件头:保存着文件的读取方式,版本号等信息,解析数据包只需要关注magic字段。
- Magic:4Bytes, pcap文件标识,用于识别文件并确定字节顺序。0xA1B2C3D4用来表示按照原来的顺序读取,0xD4C3B2A1表 示下面的字节都要交换顺序读取。一般会采用0xD4C3B2A1,即所有字节都需要交换顺序读取。
- version_major: 2Bytes, 主版本号,一般为 0x0200【实际上因为交换读取顺序,所以计算机看到的应该是 0x0002】
- version_minor: 2Bytes, 次版本号,一般为 0x0400【计算机看到的应该是 0x0004】
- timezoon: 4Bytes, 当地的标准时间,如果用的是GMT则全零,一般都直接写 0000 0000
- sigfigs: 4Bytes, 时间戳的精度,设置为全零即可
- snaplen: 4Bytes, 最大的存储长度,如果想把整个包抓下来,设置为 ffff 0000,但一般来说 ff7f 0000就足够了【计算机看到的应该是 0000 ff7f 】
- linktype: 4Bytes, 链路类型,一般为Ethernet 即0x1。
数据包头:pkt_hdr保存了数据包被捕获时候的时间戳和数据包长度。
- tv_sec:时间戳秒字段
- tv_usec:时间戳微妙字段
- caplen:捕获的数据包的长度(保存在pcap文件中的长度)单位是 Byte。
- len:离线数据长度:实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
数据内容: pkt_content里面保存着网络上捕获的原始数据帧。长度为数据包头里面的caplen长度。
[以太网帧格式] (https://www.cnblogs.com/lifan3a/articles/6649970.html)
2:从pcap文件中读取以太网数据包
1:读取pcap文件头(24 Bytes),从文件头magic字段中确定文件读取方式。
- 如果为0xA1B2C3D4则按照正常顺序读取,如果为0xD4C3B2A1则按照交换顺序读取以读取文件头中的linktype字段为例,如果文件头第20-23字节为0x01 0x00 0x00 0x00,则0xA1B2C3D4正常顺序读取的结果为0x01000000(32 bit),0xD4C3B2A1交换顺序读取的结果为0x00000001
- 交换顺序读取
D4 C3 B2 A1交换顺序读取linktype:把[0x01 0x00 0x00 0x00]解释为0x00 00 00 01(十进制:1)
- 正常顺序读取
A1 B2 C3 D4正常顺序读取linktype: 把[0x00 0x00 0x00 0x01]解释为0x00 00 00 01(十进制:1)
2:读取一个数据包头(16 Bytes) header_0 ,确定紧接着这个数据包头后面的数据包内容content_0内容的长度caplen_0。
- 数据包头里面第8-11字节为caplen字段,即数据包的长度。例如 ”0x42 0x05 0x00 0x00” 按照0xD4C3B2A1交换顺序读取的结果为caplen_0 = 1346,如果按照0xA1B2C3D4正常顺序存储那么 ”0x00 0x00 0x05 0x42” 才会被解析为caplen_0 = 1346。
- 交换顺序读取
D4 C3 B2 A1交换顺序读取caplen: 将[0x42 0x05 0x00 0x00]为0x00 00 05 42(十进制:1346)
D4 C3 B2 A1交换顺序读取len: 将[0x42 0x05 0x00 0x00]为0x00 00 05 42(十进制:1346)
- 正常顺序读取
A1 B2 C3 D4正常顺序读取caplen: 将[0x00 0x00 0x05 0x42]为0x00 00 05 42(十进制:1346)
A1 B2 C3 D4正常顺序读取len: 将[0x00 0x00 0x05 0x42]为0x00 00 05 42(十进制:1346)
3:读取数据包内容
第2步中已经解析出了header_0中的caplen,那么我们读取caplen个字节长度content_0, header_0描述的数据包内容即为content_0。
- 例如header_0的caplen为1346,那么我们读取1346字节数据,这1346字节数据即为content_0的内容。
4:反复执行第2步和第3步,直到文件结束。
3:c语言代码实现
void test_pcap_file_read()
{
unsigned char pcap_file_header[24] = { 0 };/*file header 24 bytes*/
unsigned char pkt_hdr[16] = { 0 };/*pkt_hdr 16 bytes*/
unsigned char udp_pkt_data[4096] = { 0 };/*content*/
int len = 0;
int pkt_cnt = 0;
bool isLittleEndian = false;/*big endian*/
FILE *pInFile = fopen("Test.pcap", "rb");/*open pcap file*/
if (NULL == pInFile)
{
return;
}
if (1 != fread(pcap_file_header, 24, 1, pInFile))/*read pcap file header*/
{
fclose(pInFile);
}
/*交换顺序读取*/
if ((0xD4 == pcap_file_header[0]) && (0xC3 == pcap_file_header[1]) && (0xB2 == pcap_file_header[2]) && (0xA1 == pcap_file_header[3]))
{
isLittleEndian = true;
}
while (1)
{
if (1 != fread(pkt_hdr, 16, 1, pInFile))/*read a pkt_hdr 16 bytes*/
{
break;
}
if (isLittleEndian)/*get the pktcontent's length in the file following the pkt_hdr*/
{
len = (int(pkt_hdr[11]) << 24) + int((pkt_hdr[10]) << 16) + int((pkt_hdr[9]) << 8) + int(pkt_hdr[8]);
}
else
{
len = (int(pkt_hdr[8]) << 24) + int((pkt_hdr[9]) << 16) + int((pkt_hdr[10]) << 8) + int(pkt_hdr[11]);
}
/*read the length bytes from file, this is the pktcontent*/
if (1 != fread(udp_pkt_data, len, 1, pInFile))
{
break;
}
pkt_cnt++;
}
printf("Total %d packets found\n", pkt_cnt);
fclose(pInFile);
}
4 参考链接:
pcap文件格式: https://blog.csdn.net/u013793399/article/details/51474831
pcap包文件头: https://blog.csdn.net/yhangleo/article/details/8484597