显示过滤器精讲:协议字段过滤、逻辑运算符、比较表达式、函数使用

抓包这活儿,说白了就是跟海量数据打交道。你打开 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 就不认。

小技巧: 不知道字段名怎么写?在 Wireshark 里点开一个包,展开协议树,右键点击你想过滤的字段,选「作为过滤器应用」→「选中」。Wireshark 会自动帮你填好。这招我天天用。

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
核心思路: 显示过滤器的本质,就是「把包当成数据库里的记录,用 SQL 一样的方式去查」。字段名是列名,比较操作符是条件,逻辑运算符是连接词。想清楚你要查什么,然后翻译成 Wireshark 的语法就行。

3.5 知识体系总览

下面这张图,把显示过滤器的核心知识点串起来了。你一看就明白,各个部分是怎么配合的。

显示过滤器知识体系 显示过滤器核心知识体系 显示过滤器 协议字段过滤 逻辑运算符 比较表达式 函数使用 http.request.method ip.src / ip.dst tcp.port / udp.port and / &&(与) or / ||(或) not / !(非) == / !=(等于/不等) > / < / >= / <= contains / matches upper() / lower() string() len() / abs() 💡 核心思路:把包当数据库记录,用类似 SQL 的方式查询 字段名 = 列名,比较操作符 = 条件,逻辑运算符 = 连接词

3.6 实战组合:一个完整的排查场景

光说不练假把式。咱们来一个真实场景。假设你接到一个工单:用户反馈网站访问慢。你抓了包,怎么用显示过滤器快速定位?

  1. 先看有没有重传: tcp.analysis.retransmission —— 只要这个字段存在,就说明有包重传了。网络大概率有问题。
  2. 再看延迟: tcp.analysis.ack_rtt > 0.2 —— ACK 延迟超过 200ms,说明链路或服务器响应慢。
  3. 排除掉正常流量: tcp.analysis.retransmission and not ip.src == 10.0.0.1 —— 如果 10.0.0.1 是监控服务器,它的重传可以忽略。
  4. 缩小到具体服务: 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。

显示过滤器这东西,用熟了就是肌肉记忆。多练几次,你也能像我一样,闭着眼睛写出复杂的过滤表达式。