显示过滤器基础:语法、运算符与协议字段过滤

说实话,很多新手学 Wireshark 都卡在显示过滤器这一关。抓包容易,但怎么从几千个包里快速找到你要的东西?这就是显示过滤器的本事了。我当年第一次用 Wireshark 排查一个 HTTP 慢速问题,抓了 5 分钟包,结果出来两万多条记录……当时就懵了。后来学会用 http.request 一过滤,瞬间只剩几十条,那种感觉,嗯,就像在垃圾堆里找到了宝藏。

这一章咱们就把显示过滤器的底牌翻个底朝天。你学完之后,至少能写出 80% 的日常过滤表达式。

显示过滤器 vs 捕获过滤器

先搞清楚一件事:显示过滤器和捕获过滤器是两码事。我见过不少同事把这两个搞混,结果要么抓不到想要的包,要么抓了一堆没用的。

对比项 显示过滤器 捕获过滤器
作用时机 抓包之后,对已捕获的数据包进行筛选显示 抓包之前,决定哪些包进入内存
语法基础 Wireshark 专有语法(基于协议字段) BPF(Berkeley Packet Filter)语法
能否恢复数据 可以,只是隐藏了不符合条件的包 不能,没抓到的包永远丢失
典型场景 分析已抓取的数据,逐步缩小范围 高流量环境下只保留关键流量

说白了,捕获过滤器是「进门前先筛一遍」,显示过滤器是「进门后再挑一挑」。我个人习惯是:能用显示过滤器解决的,绝不用捕获过滤器。为什么?因为一旦用捕获过滤器筛掉了某些包,后面想查就查不到了。你想想看,排查问题的时候,最怕的就是「数据不够」。

显示过滤器的基本语法结构

显示过滤器的核心语法其实很简单,就一句话:「协议.字段 运算符 值」。

举个例子:

ip.src == 192.168.1.1

这条过滤器的意思是:显示所有源 IP 地址为 192.168.1.1 的数据包。其中 ip.src 是协议字段,== 是比较运算符,192.168.1.1 是值。

再比如:

tcp.port == 80

这条会显示所有 TCP 端口为 80 的包。注意,这里 tcp.port 同时匹配源端口和目标端口。

小技巧: 在 Wireshark 的过滤栏输入时,按 Tab 键可以自动补全协议字段名。我刚开始学的时候,全靠这个功能记字段名,用多了自然就记住了。

比较运算符:不只是等于

很多人以为显示过滤器只有 ==,其实 Wireshark 支持一整套比较运算符。我在项目中遇到过一个问题:排查一个 TCP 重传率高的场景,需要找出所有重传次数超过 3 次的包。这时候 == 就不够用了,得用 >。

运算符 含义 示例
== 等于 ip.src == 10.0.0.1
!= 不等于 ip.src != 10.0.0.1
> 大于 tcp.analysis.retransmission_count > 3
< 小于 frame.len < 64
>= 大于等于 ip.ttl >= 128
<= 小于等于 tcp.window_size <= 65535
contains 包含子串 http.user_agent contains "Chrome"
matches 正则匹配 http.host matches "\.(com|cn)$"

这里我要特别提一下 contains 和 matches。这两个运算符处理的是字符串,不是数字。contains 判断某个字段是否包含指定的子串,而 matches 支持正则表达式。我曾经用 matches 在一个金融项目的日志中找出所有包含「交易失败」或「超时」的 HTTP 响应包,一条正则搞定,比一条条翻日志快多了。

注意: matches 运算符比较消耗性能。如果你只是简单的子串匹配,优先用 contains。在大包文件(几百 MB 以上)里用 matches,Wireshark 可能会卡住几秒钟。

逻辑运算符:组合你的过滤条件

单个条件往往不够用。比如你想看「来自 192.168.1.1 且发往 80 端口」的包,或者「来自 192.168.1.1 或来自 192.168.1.2」的包。这时候就需要逻辑运算符了。

运算符 含义 示例
and 或 && 逻辑与(两个条件都满足) ip.src == 192.168.1.1 and tcp.port == 80
or 或 || 逻辑或(满足任一条件) ip.src == 192.168.1.1 or ip.src == 192.168.1.2
not 或 ! 逻辑非(取反) not arp 或 !arp

举个例子,排查 DNS 问题时,我经常这么写:

dns.flags.response == 0 and ip.src == 10.0.0.1

这条过滤器的意思是:显示所有由 10.0.0.1 发出的 DNS 查询请求(响应标志位为 0)。

逻辑运算符可以嵌套使用,用括号控制优先级:

(ip.src == 192.168.1.1 or ip.src == 192.168.1.2) and tcp.port == 443

这条的意思是:来自 192.168.1.1 或 192.168.1.2 的 HTTPS 流量。注意括号的位置,如果不加括号,and 的优先级高于 or,结果就完全不一样了。

核心原则: 当你不确定优先级时,加括号。这跟写代码一个道理,显式地表达你的意图,既不会出错,别人也看得懂。

协议字段过滤:从物理层到应用层

Wireshark 的显示过滤器支持几乎所有协议字段。你可以从最底层的 frame(帧)一直过滤到应用层的 http、dns、tls 等。

我整理了一些最常用的协议字段,按层级分一下类:

层级 协议 常用字段 示例
物理/数据链路 frame frame.len, frame.time, frame.number frame.len > 1500
网络层 ip ip.src, ip.dst, ip.ttl, ip.proto ip.proto == 6(TCP)
传输层 tcp tcp.port, tcp.flags, tcp.window_size tcp.flags.syn == 1
传输层 udp udp.port, udp.length udp.port == 53
应用层 http http.request.method, http.response.code, http.host http.response.code == 404
应用层 dns dns.qry.name, dns.flags.response dns.qry.name contains "google"
应用层 tls tls.handshake.type, tls.record.version tls.handshake.type == 1(Client Hello)

这里有个小窍门:如果你不确定某个字段叫什么名字,可以在 Wireshark 里打开一个数据包,展开协议树,右键点击你感兴趣的字段,选择「作为过滤器应用」或「准备过滤器」。Wireshark 会自动帮你生成正确的过滤表达式。我刚开始学的时候,这个方法帮我省了不少查文档的时间。

知识体系总览

下面这张图把显示过滤器的核心知识点串起来了。你可以把它当作一个快速索引,以后写过滤器的时候,对照着看就行。

显示过滤器知识体系 显示过滤器 语法结构 比较运算符 逻辑运算符 协议.字段 运算符 值 == != > < >= <= contains matches and && or || not ! 核心公式:协议.字段 运算符 值 示例:ip.src == 192.168.1.1 and tcp.port == 80 支持从 frame 到 http/dns/tls 全协议栈

实战:从零开始写一条过滤器

光说不练假把式。咱们来走一遍完整的流程:

  1. 明确需求: 我想看「来自 192.168.1.100 的 HTTP 请求,且请求方法为 POST」。
  2. 拆解条件:
    • 源 IP:ip.src == 192.168.1.100
    • HTTP 请求:http.request(这是一个布尔字段,存在即为 true)
    • 请求方法为 POST:http.request.method == "POST"
  3. 组合:
    ip.src == 192.168.1.100 and http.request and http.request.method == "POST"
  4. 验证: 在 Wireshark 过滤栏输入,观察左下角。如果背景是绿色,说明语法正确;如果是红色,说明有语法错误。
我的习惯: 写复杂过滤器时,我会先在记事本里把条件拆开,一行一个,然后用 and 或 or 连起来。这样不容易漏括号,也方便后期修改。

常见错误与避坑指南

最后分享几个我踩过的坑:

  • 字符串忘加引号: http.request.method == POST 是错的,必须写成 http.request.method == "POST"。字符串值一定要用双引号或单引号括起来。
  • IP 地址比较用错了运算符: IP 地址只能用 == 或 !=,不能用 contains。比如 ip.src contains "192.168" 是无效的。
  • 混淆了字段名和显示名: Wireshark 协议树里显示的是「Source Address」,但过滤字段名是 ip.src。右键复制字段名是最稳妥的办法。
  • 忘记括号导致逻辑错误: 我曾经写过 ip.src == 10.0.0.1 or ip.src == 10.0.0.2 and tcp.port == 80,结果发现只过滤了 10.0.0.2 的 80 端口,10.0.0.1 的所有端口都出来了。加上括号 (ip.src == 10.0.0.1 or ip.src == 10.0.0.2) and tcp.port == 80 才正确。

嗯,这一章的内容就到这儿。显示过滤器是 Wireshark 最核心的技能之一,你花 20 分钟把语法和运算符记熟,后面分析任何协议都会顺手很多。记住那个核心公式:协议.字段 运算符 值,剩下的就是多练。