抓包存成的文件,格式简单到可以手搓:一个 24 字节的文件头,加每包 16 字节的记录头。 而「抓包」这件事真正的难点不在格式,在「抓得全、存得下、找得到」。
上一节讲「记账」,这一节讲「抄全文」。两者是分析的两个极端。 这一节的 Demo 是真写文件——手搓一个 pcap,再读回来验算。
一、pcap 文件的结构
文件头 24 字节,里面最关键的两个字段是「魔数」与「链路类型」。
魔数 0xA1B2C3D4: 它落盘后如果读成 d4 c3 b2 a1,说明是小端;如果读成 a1 b2 c3 d4,说明是大端。 所以这四字节同时表达了「这是 pcap」与「字节序是哪一种」。
链路类型: 告诉解析器「每个包的第一个字节是什么」——以太网是 1、原始 IP 是 101、Linux cooked 是 113。 这个字段填错,整个文件就解不出东西。
每包 16 字节的记录头:时间戳(秒 + 微秒)、捕获长度、原始长度。 「捕获长度」与「原始长度」分开是有原因的——抓包时可以只存前 96 字节(snaplen),两个字段就记录了「实际存了多少」与「线上是多少」。
二、抓包为什么会丢
三个地方会丢,一层比一层难查:
一、网卡与驱动:包到达太快,驱动来不及交给上层,环形缓冲区满了就丢。 这一层丢包在系统计数器里能看到(dropped)。
二、抓包程序:写到磁盘太慢,缓冲区满了也丢。 这一层要调大缓冲区或者用内存盘。
三、镜像口本身:交换机的镜像口带宽有限(常见是「一个口的带宽」), 如果镜像的是多个口的总流量,超出的部分在交换机上就丢了。 这一层最阴,因为抓包机上一点异常都看不到。
所以「抓包结果不全」的第一件事不是怀疑工具,是检查这三层。 顺序应该是「交换机镜像口 → 抓包机 dropped 计数 → 磁盘写入速度」。
三、抓包与隐私
抓包会把载荷原封不动存下来,这意味着「密码、Cookie、内网地址、聊天内容」都进了文件。
所以工程上有三条常规做法: 一、限制 snaplen:只存前 96 到 128 字节,够看包头与部分载荷,不存全文。 二、限定范围与时长:按需抓、抓到就停,不做长期全量。 三、加密传输与权限控制:抓包文件本身就是敏感数据。
第一条最有效,也最容易被忽略:因为默认 snaplen 是「全存」, 很多人抓完才发现文件里全是明文表单提交。 而 snaplen 一旦设成 96,绝大多数排障需求仍然满足——因为排障要看的是「谁跟谁、什么端口、什么标志」,这些都在前 96 字节里。
四、动手 Demo:亲手写一个 pcap 文件
这段用 struct 拼一个 24 字节的文件头与两个 16 字节的记录头,真的写进一个 .pcap, 再读回来逐字段验算。
ghdr = struct.pack("<IHHiIII", 0xA1B2C3D4, 2, 4, 0, 0, 65535, 1)
for i, pl in enumerate(payloads):
phdr = struct.pack("<IIII", 1700000000 + i, i * 1000, len(pl), len(pl))
pkts += phdr + pl
with open(path, "wb") as f:
f.write(ghdr + pkts)
实跑输出:
--------------------------------------------------------------------
亲手写一个 pcap 文件
--------------------------------------------------------------------
**抓到包之后存成的文件,格式其实简单到可以手搓。**
**一个 24 字节的文件头,加上每包 16 字节的记录头。**
文件头 24 字节:d4c3b2a1020004000000000000000000ffff000001000000
写入 2 个包,文件 112 字节。
账算得对:24 + 2 × (16 + 28) = 112。
读回文件头:magic=0xA1B2C3D4 版本=2.4 最大长度=65535 链路类型=1
包 1:时间 1700000000.000000,捕获 28 字节,原始 28 字节。
包 2:时间 1700000001.001000,捕获 28 字节,原始 28 字节。
读完正好到文件末尾:是。
**magic 0xA1B2C3D4 落盘后是 d4 c3 b2 a1**,**这个「反着的魔数」就是判断字节序的标志**——
**Wireshark 一读头四个字节,就知道该怎么解后面的数。**
这段能跑通,说明 pcap 真的只是一个「带头部的裸包序列」——没有压缩、没有索引、没有校验。 所以它简单到任何语言都能写,也简单到任何工具都能读。
注意「24 + 2 × (16 + 28) = 96」这个账: 文件大小完全可预测,这正是「估算抓包存储」的算法基础——你只要知道平均包长与包数,就能算出文件多大。
最后那句「魔数反着写就是字节序标志」是本节的落点: 它是个精巧的设计——一个字段同时表达两件事。 而且它让「读文件的一方」不需要任何外部约定就能自适应,这在跨平台场景里非常实用。
30 秒实验
- 用 Wireshark 打开一个 pcap,看它的「捕获长度」和「原始长度」是否相等。
- 抓 100 个包,算一下文件大小和 24 + Σ(16 + 长度) 对不对得上。
- 想一想:为什么抓包时默认 snaplen 是 65535 而不是 96?
常见误区
- 以为 pcap 是某种压缩格式。它是裸包的顺序拼接,没有压缩。
- 以为抓不到包就是没流量。镜像口带宽、驱动缓冲、磁盘速度都会导致丢包。
- 以为抓包没有隐私风险。载荷原封不动存下来,密码也在里面。
自测三问(点开对答案)
1. pcap 文件头里最关键的两个字段是什么? —— 魔数(同时表达格式与字节序)与链路类型(决定第一个字节怎么解)。 2. 「捕获长度」与「原始长度」为什么要分开存? —— 因为可以只存前若干字节(snaplen),两者记录实际存了多少与线上是多少。 3. 抓包丢包要按什么顺序排查? —— 交换机镜像口带宽、抓包机的 dropped 计数、磁盘写入速度。下一节:把一台设备切成多台「虚拟路由器」。
- ☐ (本节完,下一节 05.07.12 虚拟路由器)