显示过滤器基础:语法、运算符与协议字段过滤
说实话,很多新手学 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 支持一整套比较运算符。我在项目中遇到过一个问题:排查一个 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 会自动帮你生成正确的过滤表达式。我刚开始学的时候,这个方法帮我省了不少查文档的时间。
知识体系总览
下面这张图把显示过滤器的核心知识点串起来了。你可以把它当作一个快速索引,以后写过滤器的时候,对照着看就行。
实战:从零开始写一条过滤器
光说不练假把式。咱们来走一遍完整的流程:
- 明确需求: 我想看「来自 192.168.1.100 的 HTTP 请求,且请求方法为 POST」。
- 拆解条件:
- 源 IP:
ip.src == 192.168.1.100 - HTTP 请求:
http.request(这是一个布尔字段,存在即为 true) - 请求方法为 POST:
http.request.method == "POST"
- 源 IP:
- 组合:
ip.src == 192.168.1.100 and http.request and http.request.method == "POST" - 验证: 在 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 分钟把语法和运算符记熟,后面分析任何协议都会顺手很多。记住那个核心公式:协议.字段 运算符 值,剩下的就是多练。