显示过滤器精讲:协议字段过滤、逻辑运算符、比较表达式、函数使用
抓包这活儿,说白了就是跟海量数据打交道。你打开 Wireshark,网卡一开,几秒钟就能抓到成千上万个包。怎么从里面捞出你真正关心的那几条?这就是显示过滤器的本事了。
捕获过滤器是「拦路抢劫」,在数据进门之前就筛掉一批。而显示过滤器,是「事后诸葛亮」——所有包都抓进来了,你再慢慢挑。我个人习惯,99% 的场景只用显示过滤器。为什么?因为万一漏了关键线索,你还能回头再查。捕获过滤器一旦设错,数据根本没进来,那就真没辙了。
3.1 协议字段过滤:直接点,别绕弯
显示过滤器最基础的用法,就是按协议字段来查。Wireshark 把每个协议的每个字段都暴露出来了,你直接写字段名就行。
http.request.method == "GET"
ip.src == 192.168.1.1
tcp.port == 443
dns.qry.name contains "baidu"
你看,语法很直白:协议.字段名 操作符 值。我在项目中遇到过好几次,新手总喜欢写 tcp.srcport,其实标准写法是 tcp.srcport 或者 tcp.dstport。Wireshark 的字段名是区分大小写的,tcp.SrcPort 就不认。
3.2 逻辑运算符:组合拳才是王道
单个条件往往不够用。你想查「来自 192.168.1.1 的 HTTP 请求」,或者「DNS 查询中不含 'google' 的」,就得用逻辑运算符把它们串起来。
| 运算符 | 写法 | 示例 | 说明 |
|---|---|---|---|
| 与 | and 或 && |
ip.src == 10.0.0.1 and tcp.port == 80 |
两个条件都满足 |
| 或 | or 或 || |
tcp.port == 80 or tcp.port == 443 |
满足其中一个就行 |
| 非 | not 或 ! |
not arp |
排除掉某些包 |
举个例子。有一次线上故障,我怀疑是某个 IP 在疯狂发 TCP RST 包。我这么写:
tcp.flags.reset == 1 and ip.src == 10.0.0.88
结果一筛,那个 IP 果然在 1 秒内发了 200 多个 RST。问题定位,前后不到 5 分钟。
not 优先级最高,然后是 and,最后是 or。拿不准的时候,加括号。比如 (tcp.port == 80 or tcp.port == 443) and ip.src == 192.168.1.1,括号一加,逻辑清清楚楚。
3.3 比较表达式:不只是等于
你以为显示过滤器只能做等于判断?那太小看它了。Wireshark 支持一整套比较操作符,能帮你做范围、大小、存在性等各种判断。
| 操作符 | 写法 | 示例 | 含义 |
|---|---|---|---|
| 等于 | == |
ip.src == 10.0.0.1 |
精确匹配 |
| 不等于 | != |
ip.src != 10.0.0.1 |
排除某个值 |
| 大于 | > |
frame.len > 1500 |
超过 MTU 的包 |
| 小于 | < |
tcp.window_size < 1000 |
窗口太小的包 |
| 大于等于 | >= |
ip.ttl >= 64 |
TTL 不低于 64 |
| 小于等于 | <= |
tcp.analysis.ack_rtt <= 0.1 |
ACK 延迟不超过 100ms |
| 包含 | contains |
http.host contains "example" |
字符串子串匹配 |
| 匹配正则 | matches |
http.request.uri matches "^/api/v[0-9]" |
正则表达式匹配 |
| 存在 | 直接写字段名 | tcp.analysis.retransmission |
只要字段存在就匹配 |
这里面有个坑,我踩过好几次。contains 是大小写敏感的。你想查 http.host contains "baidu",如果实际域名是 Baidu.com,那就匹配不上。这时候用 matches 加正则 (?i) 忽略大小写:
http.host matches "(?i)baidu"
嗯,正则虽然强大,但别滥用。简单场景用 contains 就够了,正则写复杂了你自己都看不懂。
3.4 函数使用:让过滤更聪明
Wireshark 还内置了一些函数,能帮你做更高级的过滤。这些函数就像瑞士军刀,平时用不上,关键时刻真能救命。
3.4.1 upper() 和 lower():大小写不愁
刚才说了 contains 大小写敏感。如果你不想写正则,可以用 upper() 或 lower() 把字段转成统一大小写再比较:
upper(http.host) contains "BAIDU"
lower(http.host) contains "baidu"
这两种写法效果一样,都能匹配 Baidu.com、baidu.com、BAIDU.COM。
3.4.2 string():把数字当字符串用
有些字段是数字类型的,比如 ip.src 是 IPv4 地址。你想用 contains 来匹配 IP 段?直接写不行。得先用 string() 转成字符串:
string(ip.src) contains "192.168"
这样就能匹配所有 192.168.x.x 的源 IP 了。我在排查内网流量时经常用这招。
3.4.3 其他实用函数
| 函数 | 作用 | 示例 |
|---|---|---|
len() |
计算字段长度 | len(tcp.payload) > 1000 |
count() |
统计出现次数 | 用于过滤统计,不常用 |
abs() |
取绝对值 | abs(tcp.analysis.ack_rtt) < 0.01 |
3.5 知识体系总览
下面这张图,把显示过滤器的核心知识点串起来了。你一看就明白,各个部分是怎么配合的。
3.6 实战组合:一个完整的排查场景
光说不练假把式。咱们来一个真实场景。假设你接到一个工单:用户反馈网站访问慢。你抓了包,怎么用显示过滤器快速定位?
- 先看有没有重传:
tcp.analysis.retransmission—— 只要这个字段存在,就说明有包重传了。网络大概率有问题。 - 再看延迟:
tcp.analysis.ack_rtt > 0.2—— ACK 延迟超过 200ms,说明链路或服务器响应慢。 - 排除掉正常流量:
tcp.analysis.retransmission and not ip.src == 10.0.0.1—— 如果 10.0.0.1 是监控服务器,它的重传可以忽略。 - 缩小到具体服务:
tcp.analysis.retransmission and http.host contains "yourdomain.com"—— 只看目标域名的重传。
你看,几个条件一组合,问题范围就从「全网」缩小到了「某个域名下的重传」。这就是显示过滤器的威力。
not icmp and not arp and not dns,先把噪音包去掉。然后再逐步叠加条件。这样不会一开始就被海量数据淹没了。
3.7 避坑指南
最后,分享几个我踩过的坑,你遇到了能少走弯路。
- 字段名写错: Wireshark 不会报错,只会默默返回空结果。你查了半天,发现是
tcp.sreport写成了tcp.sreport(少了个 c)。嗯,这种低级错误我犯过不止一次。 - 字符串忘加引号:
http.host contains baidu会报语法错误。字符串必须用双引号包起来:http.host contains "baidu"。 - IP 地址比较用错了操作符:
ip.src > 192.168.1.1这种写法不合法。IP 地址只能用==、!=或者string()转成字符串后再用contains。 - 逻辑运算符优先级搞混: 我见过有人写
tcp.port == 80 or 443,以为能匹配 80 或 443。实际上 Wireshark 会把它解析成(tcp.port == 80) or (443),而 443 是非零值,永远为真。结果就是所有包都显示出来。正确写法是tcp.port == 80 or tcp.port == 443。
显示过滤器这东西,用熟了就是肌肉记忆。多练几次,你也能像我一样,闭着眼睛写出复杂的过滤表达式。