4. 显示过滤器:从入门到实战
抓包抓了一大堆,怎么找到你关心的那几条?这就是显示过滤器要解决的问题。捕获过滤器是「抓之前过滤」,显示过滤器是「抓之后筛选」——两者完全不同,别搞混了。
我个人习惯是:先全量抓个几秒,再用显示过滤器慢慢筛。为什么?因为你永远不知道漏掉什么。有一次我排查一个间歇性断连的问题,如果一开始就用捕获过滤器只抓特定端口,那个关键的重传包就永远看不到了。
4.1 显示过滤器语法与操作符
显示过滤器的核心语法很简单:字段 操作符 值。比如:
ip.addr == 192.168.1.1
tcp.port == 443
dns.flags.response == 1
常用的比较操作符:
| 操作符 | 含义 | 示例 |
|---|---|---|
| == | 等于 | ip.src == 10.0.0.1 |
| != | 不等于 | ip.src != 10.0.0.1 |
| > | 大于 | frame.len > 1000 |
| < | 小于 | tcp.window_size < 65535 |
| contains | 包含子串 | http.host contains "baidu" |
| matches | 正则匹配 | http.request.uri matches "\.php$" |
| in | 属于集合 | tcp.port in {80,443,8080} |
ip. 就会弹出所有 ip 相关的字段。我建议你多利用这个功能,比死记硬背字段名快得多。
4.2 基于 IP 的过滤
IP 过滤是最常用的。记住这几个就够了:
ip.addr == 192.168.1.1— 源或目标 IP 等于该地址ip.src == 192.168.1.1— 只看源 IPip.dst == 192.168.1.1— 只看目标 IPip.addr == 10.0.0.0/24— 过滤整个网段
这里有个坑:ip.addr 匹配的是「源或目标任意一个」。如果你只想看「从 A 到 B」的流量,得写成:
ip.src == 192.168.1.1 and ip.dst == 10.0.0.2
我曾经见过有人用 ip.addr == 192.168.1.1 and ip.addr == 10.0.0.2,结果发现根本筛不出东西——因为同一个包不可能同时源和目标都是同一个 IP。嗯,逻辑上就错了。
4.3 基于端口的过滤
端口过滤也很直接:
tcp.port == 443— 源或目标端口为 443tcp.srcport == 443— 只看源端口udp.port == 53— DNS 查询通常用 UDP 53tcp.port in {80,443,8080}— 多个端口
tcp.port == 80 or tcp.port == 443。你想想看,如果服务器返回了 302 重定向,从 443 跳到 80,你只筛一个端口就会漏掉关键跳转。
4.4 基于协议的过滤
Wireshark 能识别几百种协议。直接输入协议名就行:
dns
http
arp
icmp
tcp
udp
tls
你还可以组合协议和字段:
dns.flags.response == 0 # 只看 DNS 查询请求
dns.flags.response == 1 # 只看 DNS 响应
http.response.code == 404 # 只看 404 响应
icmp.type == 8 # 只看 ping 请求
4.5 逻辑组合过滤
单个条件不够用?用逻辑操作符组合:
| 操作符 | 含义 | 示例 |
|---|---|---|
| and / && | 与 | ip.src == 192.168.1.1 and tcp.port == 80 |
| or / || | 或 | tcp.port == 80 or tcp.port == 443 |
| not / ! | 非 | not arp |
| () | 分组 | (ip.src == 10.0.0.1 or ip.src == 10.0.0.2) and dns |
举个例子。你想看「来自 192.168.1.100 或 192.168.1.101 的 DNS 查询,但排除 .local 域名的请求」:
(ip.src == 192.168.1.100 or ip.src == 192.168.1.101) and dns and not dns.qry.name contains ".local"
ip.src == A or ip.src == B and dns,结果发现只筛出了 B 的 DNS 流量——因为 and 优先级高于 or。记住:拿不准就加括号。
4.6 实战案例:快速定位 DNS 查询失败
好了,理论说完了。咱们来点实际的。
假设用户反馈「网页打不开」。你抓了包,怎么快速定位是不是 DNS 的问题?
第一步,先筛出所有 DNS 流量:
dns
第二步,只看 DNS 响应,找到那些「有问题」的:
dns.flags.response == 1 and dns.flags.rcode != 0
rcode 是 DNS 响应码。0 表示成功,非 0 表示有错误。常见的:
| rcode | 含义 | 说明 |
|---|---|---|
| 0 | NoError | 查询成功 |
| 1 | FormatError | 查询格式错误 |
| 2 | ServerFailure | 服务器内部故障 |
| 3 | NXDOMAIN | 域名不存在(最常见) |
| 4 | NotImplemented | 服务器不支持该查询类型 |
| 5 | Refused | 服务器拒绝查询 |
第三步,如果响应码是 3(NXDOMAIN),说明域名根本不存在。这时候你要检查:用户是不是输错了域名?或者 DNS 服务器是不是返回了错误的 NXDOMAIN(比如某些运营商劫持)?
第四步,还有一种情况:DNS 请求发出去了,但一直没有响应。怎么筛?
dns.flags.response == 0 and !dns.flags.truncated
然后看这些请求有没有对应的响应。Wireshark 有个好用的功能:右键点击一个 DNS 请求包 → Follow → UDP Stream,就能看到完整的请求-响应对话。
我曾经遇到过一个案例:用户访问某个网站时快时慢。抓包发现 DNS 查询有时要 3 秒才返回,有时直接超时。后来排查发现是 DNS 服务器配置了多个上游,其中一个上游响应极慢。用 dns.time > 1 筛出所有响应时间超过 1 秒的查询,问题一目了然。
最后,给你一个我常用的「DNS 故障快速诊断」过滤组合:
dns and (dns.flags.rcode != 0 or dns.time > 1 or dns.flags.truncated == 1)
这个表达式能一次性筛出:查询失败的、响应慢的、被截断的。你想想看,是不是比一条条翻包快多了?
dns.flags.rcode == 0,但响应里却没有答案记录。后来才注意到 dns.count.answers == 0。所以别只看 rcode,还要看答案数量。完整的过滤应该是:dns.flags.response == 1 and (dns.flags.rcode != 0 or dns.count.answers == 0)。
4.7 本章知识体系
下面这张图帮你梳理了显示过滤器的核心逻辑:
这张图把显示过滤器的三大核心——语法结构、过滤维度、逻辑组合——串在了一起。你写过滤表达式的时候,脑子里过一遍这个结构,基本不会出错。