当前位置: 首页 > news >正文

Libpcap格式pcap包分析 - tomato

Featured image of post Libpcap格式pcap包分析

Libpcap格式pcap包分析

从字节分析pcap文件的构造

前言: 因为工作中经常要接触到 Pcap 这种文件格式,无论是利用其进行网络数据分析,还是测试时使用其进行流量回放。但是 Pcap 文件的底层数据结构是什么样子呢?里面装的又是什么东西呢?这是本文将要解答的两个核心问题

Pcap 是什么

Pcap 是 Packet Capture 的缩写,即网络数据包捕获。将捕获的数据包按照一定格式存入文件即 Pcap 文件。在 Linux 平台上使用 Tcpdump,在 Windows 上使用 Wireshark 抓包,可以保存为 .pcap 后缀的文件。

💡实际上 Pcap 文件存在多种格式,最常用的两种格式为 Libpcap 和 PcapNG(PCAP Next Generation) 格式,本文主要讲解 Libpcap 格式

Libpcap 格式最新草案:

  • https://datatracker.ietf.org/doc/html/draft-ietf-opsawg-pcap
  • https://github.com/IETF-OPSAWG-WG/draft-ietf-opsawg-pcap

Wireshark 说明:

  • https://wiki.wireshark.org/FileFormatReference
  • https://wiki.wireshark.org/Development/PcapNg
  • https://wiki.wireshark.org/Development/LibpcapFileFormat

Pcap 文件的数据结构

文件整体格式

Pcap 文件由一个全局的头部(Global Header)和零至多个数据包(Packet,与网络层的 Packet 不是一个概念)组成,全局头部主要用于存储字节顺序、文件版本、单个数据包最大长度等信息。每一个数据包又由 Header 和 Data 组成,Header 中主要用于存储时间戳和 Data 的长度信息。

全局头部格式(Global Header)

现在来分析 Pcap 文件的全局头部,构造信息如下,每行 4 个字节(Byte)即 32 比特(bit),全局头部共计 24 个字节:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
                     0               1               2               3
       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    0 |                          Magic Number                         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |          Major Version        |         Minor Version         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |                           Thiszone                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   12 |                            Sigfigs                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   16 |                            SnapLen                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   20 |                            LinkType                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Magic Number031Major Version3247Minor Version4863Thiszone6495Sigfigs96127SnapLen128159LinkType160191PCAP Global Header

C 语言实现(来自Wireshark 文档),Github 上 Libpcap 的最新实现略有不同,不过无实质差别,具体见链接

1
2
3
4
5
6
7
8
9
typedef struct pcap_hdr_s {
    guint32 magic_number;   /* magic number */
    guint16 version_major;  /* major version number */
    guint16 version_minor;  /* minor version number */
    gint32  thiszone;       /* GMT to local correction */
    guint32 sigfigs;        /* accuracy of timestamps */
    guint32 snaplen;        /* max length of captured packets, in octets */
    guint32 network;        /* data link type */
} pcap_hdr_t;

相关字段解释如下表:

字段字节数含义
Magic Number
magic_number
4十六进制数字0xA1 B2 C3 D4,标记文件开始,并用来识别文件的字节顺序。
Major Version
version_major
2一个无符号值,给出 pcap 格式的当前主要版本的编号,一般为2。
Minor Version
version_minor
2一个无符号值,给出 pcap 格式的当前次要版本的编号,一般为4。(与上面的 Major Version 一起组成版本号 2.4 )
ThisZone
thiszone
4当地的标准时间,如果用的是UTC则全零,一般全零(在最新的标准中已弃用,因为现在都使用UTC时间,为向下兼容设置为全0)
SigFigs
sigfigs
4时间戳的精度,一般全零(在最新的标准中已弃用,为向下兼容设置为全0)
SnapLen
snaplen
4设置所抓获的单个数据包的最大长度,一般设置为65535(单个数据链路层帧大小一般为1518字节包括14字节首部和4字节尾部,但是巨帧可达9000字节甚至更多,设置为65535大于任何标准或巨型以太网帧的大小,它提供了足够的空间来捕获几乎所有类型的网络数据包,包括非标准或实验性协议)
LinkType
network
4数据链路层协议类型。解析数据包首先要判断它的LinkType,所以这个值很重要。常见的值如 1,代表着以太网。相关定义和枚举值见参考链接

💡注意:对于 Magic Number,在其他地方或许会看到有人说除了0x A1 B2 C3 D4还有0x A1 B2 3C 4D,在较新的版本中确实存在A1 B2 3C 4D(大端模式)或者4D 3C B2 A1(小端模式)的情况,用于标记 Packet Header 中时间戳的精度为纳秒。

故从 Pcap 文件的低位到高位度读取,存在以下四种情况:

  • A1 B2 C3 D4:大端模式,Packet Header 中时间戳为毫秒精度
  • D4 C3 B2 A1:小端模式,Packet Header 中时间戳为毫秒精度
  • A1 B2 3C 4D:大端模式,Packet Header 中时间戳为纳秒精度
  • 4D 3C B2 A1:小端模式,Packet Header 中时间戳为纳秒精度

❗️ 网上的很多文档混淆了大小端和时间戳精度,注意甄别

在最新草案中,全局头部的最后四字节数据也有所变化,感兴趣的朋友可以自行查看

数据包头部格式(Packet Header)

数据包构造信息如下,每行 4 个字节(Byte)即 32 比特(bit),每个数据包头部共计 16 个字节:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
                     0               1               2               3
       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    0 |                      Timestamp (Seconds)                      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |                   Timestamp (Microseconds)                    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |                    Captured Packet Length                     |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   12 |                    Original Packet Length                     |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   16 /                                                               /
      /                          Packet Data                          /
      /                        variable length                        /
      /                                                               /
      +---------------------------------------------------------------+
Timestamp (Seconds)031Timestamp (Microseconds)3263Captured Packet Length6495Original Packet Length96127Packet Data (variable length)128159Packet Data (variable length)160191PCAP Packet Record

C 语言实现(来自Wireshark 文档),Github 上 Libpcap 的最新实现略有不同,不过无实质差别,具体见链接

1
2
3
4
5
6
typedef struct pcaprec_hdr_s {
    guint32 ts_sec;         /* timestamp seconds */
    guint32 ts_usec;        /* timestamp microseconds */
    guint32 incl_len;       /* number of octets of packet saved in file */
    guint32 orig_len;       /* actual length of packet */
} pcaprec_hdr_t;

相关字段解释如下表:

字段字节数含义
Timestamp(Seconds)
ts_sec
4秒级时间戳,秒值是一个 32 位无符号整数,表示自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的秒数
Timestamp(Microseconds)
ts_usec
4微秒值表示自秒值之后经过的微秒(可以理解为秒数小数点后的数)
Captured Packet Length
incl_len
4一个无符号值,表示从网络中实际中捕获的字节数(即 Packet Data 的长度)。它的值为原始数据包长度(Original Packet Length)和快照长度(上表中的 SnapLen)中的较小值。
Original Packet Length
orig_len
4一个无符号值,表示数据包在网络上传输时的实际长度。如果数据包在捕获过程中被截断,则捕获的数据包长度与实际网络中传输的数据包长度不同。
Packet Data
data
由上述 Captured Packet Length 字段决定实际被捕获的数据链路层帧,包括链路层标头。该字段的实际长度为 Captured Packet Length。链路层头部的格式取决于文件全局头部中指定的 LinkType 字段。可以理解为:每一个 Packet 中都装着一个数据链层的帧。

使用 ImHex 查看 Pcap 包

经过上文的介绍我们知道了 Pcap 文件的全局头部格式和数据包头部格式,下面我们使用十六进制编辑器开始查看和分析一个实际的 Pcap 文件:

图 3.1:使用 ImHex 打开 Pcap 文件

全局头部

根据上述章节的分析可知全局头部占24个字节,于是让我们分析上述 Pcap 文件的前24个字节,如下图:

图 3.1.1:Pcap 文件的全局头部

具体内容如下:

1
2
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
D4 C3 B2 A1 02 00 04 00 00 00 00 00 00 00 00 00 FF FF 00 00 01 00 00 00

前四个字节为 Magic Number,从低地址到高地址依次为 D4 C3 B2 A1,由上述章节可知该 Pcap 文件的字节顺序为小端序,那么对于多字节数据需要倒排字节才能得到实际的数据,具体数据转换如下表:

字段字节序号小端序数据大端序数据实际数据(由大端序计算)
Magic Number(4B)0-30xD4 C3 B2 A10xA1 B2 C3 D4LE 小端模式标记
Major Version(2B)4-50x02 000x00 022
Minor Version(2B)6-70x04 000x00 044
ThisZone(4B)8-110x00 00 00 000x00 00 00 000
SigFigs(4B)12-150x00 00 00 000x00 00 00 000
SnapLen(4B)16-190xFF FF 00 000x00 00 FF FF65535
LinkType(4B)20-230x01 00 00 000x00 00 00 01LINKTYPE_ETHERNET 以太网

数据包头部

根据上述章节可知,数据包头部为 16 字节,那么全局头后面的16个字节即为 Pcap 文件中第一个数据包的头部,如下图:

图 3.2.1:Pcpa 文件中第一个数据包的头部信息

具体信息如下:

1
2
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
1F 90 D1 66 69 B3 07 00 36 00 00 00 36 00 00 00

依次分析,数据包头由四个字段组成,每个字段 4 个字节,具体数据转换如下表:

字段字节序号小端序数据大端序数据实际数据(由大端序计算)
Timestamp(Seconds)(4B)24-270x1F 90 D1 660x66 D1 90 1F1725009951
Timestamp(Microseconds)(4B)28-310x69 B3 07 000x00 07 B3 69504681
Captured Packet Length(4B)32-350x36 00 00 000x00 00 00 3654
Original Packet Length(4B)36-390x36 00 00 000x00 00 00 3654

秒级时间戳为 1725009951,微妙精度值为 504681,因此完整的秒级时间戳为1725009951.504691,转换为UTC +8时间为:2024-08-30 17:25:51

数据包捕获长度(Captured Packet Length)和网络上该包实际长度(Original Packet Length)是相同的说明网络上的数据包被完整捕获。意味着此 Packet Header 对应的 Packet Data 的长度为 54 字节。

数据包负载

由上述信息可知第一个 Packet 数据包的数据体长度为 54 字节,因此从该数据包包头的最后一个字节后向后读取 54 字节即为第一个 Packet 数据包的数据体。

循环上述流式读取过程(先读取全局头部 24 字节,读取Packet 数据包头部 16 字节,读取 Packet 包头(Packet Header)中字段 Captured Packet Length 记录长度的数据作为数据体(Packet Data),读取第二个包头,读取第二个数据体……)

这里的一个个 Packet 数据包,便对应着 Wireshark 工具中的一条条记录,现在让我们使用 ImHex 和 Wireshark 查看该 Pcap 文件中的第一个数据包的数据体:

图 3.3.1 使用 ImHex 查看第一个数据包的数据体

图 3.3.2 使用 Wireshark 查看第一个数据包

可以看出图 3.3.1 中选中的 54 字节数据,与图 3.3.2 中最下面显示的 54 字节数据一致。

那么这 54 字节数据是什么呢?以及为什么是 54 字节而不是其他数字呢?对网络比较熟悉的朋友可能已经知道答案了:54 = 14 + 20 + 20;下面让我们按照网络协议栈从上往下,来看看这里的 14 和 20 都是什么:

图 3.3.3 网络模型和协议

由上述图 3.3.2 Wireshark 的截图我们可知,第一个数据包捕获的内容即为 HTTP 协议在建立 TCP 连接前(HTTP 1.* 协议是基于 TCP 协议的)由客户端发起的第一个 ACK 请求。于是我们从 TCP 协议所在的传输层往下分析:

图 3.3.4 网络模型 数据发送流程中数据的传递过程

  • 传输层:第一个数据包捕获的是 TCP 建立连接前的第一个由客户端发起的 ACK 请求,由于此次只使用到了 TCP 协议的头部,故为传输层的报文段(segment)的长度即为 TCP 协议头部的长度,即 20 字节。
  • 网络层:上述传输层的报文向下交由网络层处理,网络层会为其增加一个网络层头部(20字节),然后继续向下层数据链路层传递。故而网络层封装好后数据包(Packet,这里的Packet指网络层数据包,与 Pcap 文件中的数据包为不同概念)大小为: 网络层数据包头长度(20 字节)+ 网络层数据包的数据体长度(20 字节)= 40 字节
  • 数据链路层:上述网络层的数据包(40 字节)向下交由数据链路层处理,数据链路层会为其加上一个数据链路层头部(14 字节)和数据链路层尾部(FCS 即帧校验序列,4字节)进而组成一个数据链路层的帧。故而此帧的大小为: 帧头(14字节)+ 帧负载(40字节)+ 帧尾(4字节)= 54 + 4 = 58字节 此时有朋友可能已经发现异常了,为什么我们上述 Pcap 文件中第一个数据包的数据体只有 54 字节,相比我们的计算值少了 4 个字节的数据,原因很简单因为数据链层的帧尾是由网卡负责处理的,网卡负责帧尾的计算和校验,在发送数据时网卡计算并添加帧尾后将数据发出,在收到数据时网卡校验并去除帧尾后再向上层传递,故而我们拿到的数据链路层帧其实是通过了帧尾校验并去除了帧尾之后的数据,所以就是 58 - 4 = 54 字节。

由上述信息可知,Pcap 文件中第一个数据包的 54 字节数据体中,前 14 字节为数据链路层头部,往后中间的 20 字节为网络层头部,最后 20 字节即为传输层头部(为什么只有头部没有数据体?因为TCP建立连接前的第一次 ACK 请求用不上数据体)。

这里我们就不再按照字节去分析链路层、网络层或是传输层的头部信息了,因为 Wireshark 已经可以很好的帮我们分析。有一个细节是,虽然该 Pcap 文件的字节序是小端序,但是在计算链路层、网络层等层的头部信息时,对于多字节字段不需要倒排字节,因为网络传输使用的是大端序,又因为 Pcap 文件中每一个数据包的数据体都是一个”数据链路层帧”,并未做其他转换,故而数据体中的数据也是大端序,在计算跨字节数据时不需要逆转字节顺序。

总结

至此我们完成了 Libpcap 格式的 Pcap 文件数据构造分析。并以一个实际的 Pcap 文件作为例子进行了对照学习。解答了前言中提出的问题:

  1. Pcap 文件的数据构造?

    Pcap 文件由一个 24 字节的全局头部(Global Header),和多个数据包(Packet)组成;每一个数据包又由 16 字节的头部(Packet Header)和数据体(Packet Data)组成,数据体的长度由头部字段 Captured Packet Length 记录;

  2. Pcap 文件的数据包中的数据体(Packet Data)保存的是什么内容?

    Pcap 文件中的每一个数据包对应 WireShark 中的一条记录;每一个数据包中的数据体对应一个捕获到的“数据链路层的帧”(无 FCS 帧尾);

本文并未提及 Pcap 包的捕获和回放方法,感兴趣的读者可以自行检索,具体实现方法包括但不限于:原始套接字、DPDK、XDP

参考链接

  1. Libpcap File Format
  2. Capture File Format Reference
  3. Pcap link types
Licensed under CC BY-NC-SA 4.0
<script>const loadScript = (url, onloadFunction) => {const newScript = document.createElement("script");newScript.onerror = (oError) => {throw new URIError("The script " +oError.target.src +" didn't load correctly.");};if (onloadFunction) {newScript.onload = onloadFunction;}document.head.insertAdjacentElement("beforeend", newScript);newScript.src = url;};const getMermaidTheme = (scheme) => {return scheme === "dark" ? "dark" : "default";};let mermaidLoaded = false;const initMermaid = (theme) => {if (!mermaidLoaded) return;mermaid.initialize({startOnLoad: false,securityLevel: "strict",theme: theme,});const containers = document.querySelectorAll(".mermaid");containers.forEach((el) => {const rawCode = el.getAttribute("data-mermaid");if (rawCode) {el.innerHTML = rawCode; el.removeAttribute("data-processed");} else {el.setAttribute("data-mermaid", el.textContent); }});mermaid.run({nodes: Array.from(containers),});console.log("mermaid rendered with theme:", theme);};const loadMermaidIfNeeded = () => {if (document.querySelectorAll(".mermaid").length > 0) {loadScript("https://cdn.jsdelivr.net/npm/mermaid@11.8.1/dist/mermaid.min.js",() => {mermaidLoaded = true;const scheme =document.documentElement.dataset.scheme || "light";initMermaid(getMermaidTheme(scheme));});}};window.addEventListener("load", loadMermaidIfNeeded);window.addEventListener("onColorSchemeChange", (e) => {const scheme = e.detail;initMermaid(getMermaidTheme(scheme));});</script>
<script>window.disqus_config = function () {};(function() {if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';return;}var d = document, s = d.createElement('script'); s.async = true;s.src = '//' + "heaciy" + '.disqus.com/embed.js';s.setAttribute('data-timestamp', +new Date());(d.head || d.body).appendChild(s);})(); </script> comments powered by Disqus
<script>window.addEventListener('onColorSchemeChange', (e) => {if (typeof DISQUS == 'object') {DISQUS.reset({reload: true});}}) </script>
Built with Hugo
Theme Stack designed by Jimmy
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js" integrity="sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo=" crossorigin="anonymous" defer=""></script><script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js" integrity="sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU=" crossorigin="anonymous" defer=""></script>
http://www.jsqmd.com/news/779029/

相关文章:

  • 本地部署 AI 大模型保姆级教程:Ollama 安装、模型下载与终端实战全流程
  • 5G神经接收器技术:站点特定微调与性能优化
  • Nginx 入门教程(安装、反向代理、负载均衡、动静分离)
  • 口碑好的常州汽车开锁企业有哪些?百姓开锁18052537666本地优秀靠谱单位 - 品牌企业推荐师(官方)
  • 基于MCP协议构建AI智能体工具服务器:从原理到实战部署
  • 终极鸣潮自动化指南:开源工具OK-WW如何解放你的双手
  • 在嵌入式项目中观测大模型API用量与成本的实际体验
  • 6个月速成!从0基础到LLM开发工程师,抓住AI风口,高薪就业不是梦!
  • AGI的到来对普通人的影响
  • 2026年5月丨办公家具企业转型趋势:从功能到体验的跨越 - 品牌企业推荐师(官方)
  • FPGA上实现SM4加密:用Verilog写一个‘边算边用’的循环迭代核心
  • facefusion-3.6.1
  • 三河开锁哪家靠谱?三河市聚凯开锁15100720433河北直营店攻略 - 品牌企业推荐师(官方)
  • ThreadPoolexecutor源码分析、C++11线程池实现
  • 2026年尼龙牛津布实力厂家精选 - 品牌企业推荐师(官方)
  • G-Helper技术解析:华硕笔记本硬件控制框架的逆向工程实现与性能优化
  • 气凝胶+玄武岩复合毡 | 石化管道场景的经济账:投资回收期2-5年,减碳数百吨/年
  • 告别Burp Intruder!用Yakit的Web Fuzzer,一个标签搞定密码爆破、目录扫描和Host碰撞
  • 口碑好的乐清市管道疏通疏通下水道服务好的机构?乐清市鑫诚13868771395本地直营单位 - 品牌企业推荐师(官方)
  • 春季提高思维能力测试(B) A.符文大陆
  • 手把手教你用Keil C51插件搞定赛元SC92F73A3单片机IO口配置(附避坑指南)
  • 基础SQL:约束
  • 2026年美容仪器排名前十品牌,真实用户评价深度解析 - 品牌企业推荐师(官方)
  • 从SITS2026看AISMM评估拐点:为什么头部企业已在Q2完成差距分析与基线对标?
  • 告别数据丢失!ABAP ALV修改事件(Data Changed)的两种正确注册与刷新姿势
  • 开源大模型机械爪操作能力评测框架解析与实践
  • Arm Cortex-X2处理器编程陷阱与解决方案
  • 零代码RAG构建与向量数据库操作:从文档到知识的自动化之路
  • 风电系统光纤通信技术应用与优化指南
  • 2026壁挂炉十大品牌硬核横评:抛开营销看数据,选对品牌能省一半气费?