手写 PCAP 文件

本文最后更新于:2022年5月1日 晚上

〇、PCAP 文件整体结构

PCAP 文件是由文件头、包头、包内容三部分组成
其中 PCAP 文件头部仅在文件开头位置出现一次,用来标识 PCAP 文件
包头用来标识数据包,包含时间和长度,后面紧跟数据包内容,一起写入文件

一、PCAP 文件头部格式

1
2
3
4
5
6
7
8
9
typedef struct {
uint32_t magic; // 标识文件的开始,固定 0x1a2b3c4d
uint16_t version_major; // 当前文件的主要版本号,固定 0x0002
uint16_t version_minor; // 当前文件的次要版本号,固定 0x0004
uint32_t thiszone; // 当地标准时间,固定 0x00000000
uint32_t sigfigs; // 时间戳的精度,固定 0x00000000
uint32_t snaplen; // 最大存储长度(文件长度)
uint32_t linktype; // 链路类型
} pcap_file_hdr_t;

二、包头格式

1
2
3
4
5
6
typedef struct {
uint32_t tv_sec; // 时间戳高位,精确到秒
uint32_t tv_usec; // 时间戳低位,精确到微秒
uint32_t caplen; // 抓取到的数据帧长度
uint32_t len; // 数据帧的实际长度(可能部分没抓到):len - caplen ≥ 0
} pcap_pkt_hdr_t;

三、实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* pcap.h */
#ifndef PCAP_H
#define PCAP_H

#include <sys/time.h>

#define TCPDUMP_MAGIC 0xa1b2c3d4
#define PCAP_VERSION_MAJOR 0x0002
#define PCAP_VERSION_MINOR 0x0004
#define PCAP_THISZONE 0x00000000
#define PCAP_SNAPLEN 0x00040000
#define PCAP_SIGFIGS 0x00000000
#define PCAP_LINKTYPE_RAW_IP 0x00000065

typedef struct {
uint32_t magic; // 标识文件的开始,固定 0x1a2b3c4d
uint16_t version_major; // 当前文件的主要版本号,固定 0x0002
uint16_t version_minor; // 当前文件的次要版本号,固定 0x0004
uint32_t thiszone; // 当地标准时间,固定 0x00000000
uint32_t sigfigs; // 时间戳的精度,固定 0x00000000
uint32_t snaplen; // 最大存储长度(文件长度)
uint32_t linktype; // 链路类型
} pcap_file_hdr_t;

typedef struct {
uint32_t tv_sec; // 时间戳高位,精确到秒
uint32_t tv_usec; // 时间戳低位,精确到微秒
uint32_t caplen; // 抓取到的数据帧长度
uint32_t len; // 数据帧的实际长度(可能部分没抓到):len - caplen ≥ 0
} pcap_pkt_hdr_t;

void init_pcap_file(const char *pcap_file);
void recode_pkt(const char *pcap_file, const char *data, int data_len);

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* pcap.c */
#include "pcap.h"

/* 创建 pcap 文件并写入文件头 */
void init_pcap_file(const char *pcap_file)
{
FILE *fp = fopen(pcap_file, "wb+");

pcap_file_hdr_t pcap_file_hdr;
pcap_file_hdr.magic = TCPDUMP_MAGIC;
pcap_file_hdr.version_major = PCAP_VERSION_MAJOR;
pcap_file_hdr.version_minor = PCAP_VERSION_MINOR;
pcap_file_hdr.thiszone = PCAP_THISZONE;
pcap_file_hdr.snaplen = PCAP_SNAPLEN;
pcap_file_hdr.sigfigs = PCAP_SIGFIGS;
pcap_file_hdr.linktype = PCAP_LINKTYPE_RAW_IP;

fwrite(&pcap_file_hdr, sizeof(pcap_file_hdr_t), 1, fp);
fclose(fp);
}

/* 记录一个数据帧 */
void recode_pkt(const char *pcap_file, const char *data, int data_len)
{
FILE *fp = fopen(pcap_file, "ab+");

pcap_pkt_hdr_t pcap_pkt_hdr;
struct timeval ts;
gettimeofday(&ts, NULL);
pcap_pkt_hdr.tv_sec = (uint32_t)ts.tv_sec;
pcap_pkt_hdr.tv_usec = (uint32_t)ts.tv_usec;
pcap_pkt_hdr.caplen = data_len;
pcap_pkt_hdr.len = data_len;

fwrite(&pcap_pkt_hdr, sizeof(pcap_pkt_hdr_t), 1, fp);
fwrite(data, sizeof(char), data_len, fp);
fclose(fp);
}