📡 第五章 防火墙功能与防范威胁的对策
📄 一句话概括

抓包存成的文件,格式简单到可以手搓:一个 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 秒实验

  1. 用 Wireshark 打开一个 pcap,看它的「捕获长度」和「原始长度」是否相等。
  2. 抓 100 个包,算一下文件大小和 24 + Σ(16 + 长度) 对不对得上。
  3. 想一想:为什么抓包时默认 snaplen 是 65535 而不是 96?

常见误区

  1. 以为 pcap 是某种压缩格式。它是裸包的顺序拼接,没有压缩。
  2. 以为抓不到包就是没流量。镜像口带宽、驱动缓冲、磁盘速度都会导致丢包。
  3. 以为抓包没有隐私风险。载荷原封不动存下来,密码也在里面。
自测三问(点开对答案) 1. pcap 文件头里最关键的两个字段是什么? —— 魔数(同时表达格式与字节序)与链路类型(决定第一个字节怎么解)。 2. 「捕获长度」与「原始长度」为什么要分开存? —— 因为可以只存前若干字节(snaplen),两者记录实际存了多少与线上是多少。 3. 抓包丢包要按什么顺序排查? —— 交换机镜像口带宽、抓包机的 dropped 计数、磁盘写入速度。

下一节:把一台设备切成多台「虚拟路由器」。

相关笔记