第4章:显示过滤器——语法与使用技巧

显示过滤器,说白了就是你在Wireshark里输入的那串表达式。它能帮你从海量数据包里,精准揪出你想要的那几个。跟捕获过滤器不一样——那个是在抓包之前就决定要什么,而这个是抓完了以后,在结果里再筛选。

我个人习惯,抓包的时候尽量多抓,抓全了,等分析阶段再用显示过滤器慢慢“筛”。为什么呢?因为有时候原始数据里藏着意想不到的线索,你一开始就把它过滤掉,后面可能就找不回来了。这个教训,我是吃过亏的。

4.1 基本语法

显示过滤器的核心语法,其实就两样东西:协议 和 字段。

  • 直接输入协议名:比如 http、tcp、dns。Wireshark就会把包含该协议的数据包显示出来。
  • 字段引用:用点号访问协议内部的字段。比如 ip.src 表示源IP地址,tcp.port 表示TCP端口。
  • 比较操作:==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。

简单吧?你试试在过滤器栏里输入 http,看看是不是只剩HTTP包了。

小技巧: 输入协议名后按一下Tab键,Wireshark会自动补全,还会显示出这个协议下有哪些字段可用。这个功能我天天用。

4.2 构建表达式——从入门到实战

光知道协议名不够,你得学会“精准定位”。比如我只想看某个IP的HTTP流量:

http and ip.addr == 192.168.1.100

这里 and 就是逻辑与。意思就是:满足是HTTP协议,同时IP地址是这个的包,都给我出来。

你可能会问:为什么用 ip.addr 而不是 ip.src 或 ip.dst?ip.addr 的意思是“源或目的任意一个匹配”,这个在排查双向通信时特别方便。我曾经遇到过一个场景:客户端向服务器发请求正常,服务器回包客户端收不到。用 ip.addr == 192.168.1.100 一把抓出来,再按响应时间排序,问题立马就定位到了——服务器回包被中间防火墙截了。

4.3 字段类型与匹配

Wireshark里的字段类型很多,用的最多的就这几种:

类型 示例 说明
整数 tcp.port == 443 直接比较数值
IP地址 ip.addr == 10.0.0.1 自动解析为IPv4或IPv6
字符串 http.host contains "example" 模糊匹配,contains 是关键
布尔值 tcp.flags.syn == 1 只显示SYN标志位为1的包
正则 http.user_agent matches "Mozilla.*" 用 matches 写正则

这里我特别想强调一下 contains 和 matches 的区别。contains 是“包含子串”,效率高;matches 是“正则匹配”,灵活但慢。如果你只是想看某个字段里有没有特定关键词,用 contains 就够了。我曾经遇到一个同事,查个简单的字符串非要用 matches,结果过滤一下等了好几分钟。那批数据包有十几个G,正则跑起来确实吃力。

4.4 逻辑运算符——组合你的条件

一个条件不够用?那就用逻辑运算符把它们串起来:

  • and(&&):两个条件同时满足
  • or(||):满足其中一个就行
  • not(!):取反
  • xor:异或,少用,但有时候很巧妙

举个例子:我想看DNS请求,但是排除掉本地回环地址的查询:

dns and not ip.addr == 127.0.0.1

再比如,我想看局域网内两台机器之间的HTTP通信,不论谁是客户端:

http and (ip.addr == 192.168.1.10 or ip.addr == 192.168.1.20)

注意,括号很重要,它决定了运算的优先级。我见过不少新手把 or 和 and 搞混,结果过滤出来的包完全不是自己想要的。你想想看,没有括号的话,http and ip.addr == 192.168.1.10 or ip.addr == 192.168.1.20 会被理解成:(http and ip.addr == 192.168.1.10) or ip.addr == 192.168.1.20。这结果可不是你期望的吧?

4.5 函数与计算字段

Wireshark的显示过滤器其实支持一些简单的函数,这些在高级排查场景下非常实用:

函数 说明 示例
upper() 将字符串转为大写后再比较 upper(http.request.method) == "GET"
lower() 转为小写 lower(http.host) contains "example"
len() 计算字段长度(字节数) len(udp) > 100
count() 统计满足条件的包数量 常用于统计视图,不过筛选也能用

举个实际场景:排查DNS响应过大的情况。正常情况下DNS响应很小,但如果有人用DNS隧道传输数据,响应包会很大。这时候:

dns and len(udp) > 500

就能帮你快速锁定异常的DNS流量。

注意: len() 计算的是字段在协议层中的实际长度,不是应用层数据大小。比如 len(udp) 是整个UDP报文的长度,包含头部和数据。如果你只想要数据部分,得用 udp.length 这个字段。

4.6 常用显示过滤器模式速查

这些是我在实际工作中总结出来的“高频筛法”,你直接拿去用:

  • 只看三次握手包:tcp.flags.syn == 1 or tcp.flags.fin == 1 or tcp.flags.reset == 1
  • 过滤特定MAC地址的广播:eth.addr == ff:ff:ff:ff:ff:ff
  • 排除所有ARP包:not arp(这个在分析网络问题时经常用)
  • 只看HTTP POST请求:http.request.method == "POST"
  • 寻找重传包:tcp.analysis.retransmission
  • 按端口过滤应用层:tcp.port == 3306 or udp.port == 3306(MySQL)

4.7 避坑指南

好了,前面说了这么多技巧,现在我得跟你聊聊那些“看似简单但一用就翻车”的地方。

坑一: ip.addr 是个集合字段,包里有多个IP地址时(比如隧道包、VLAN包),一个条件可能会匹配到多个值。比如 ip.addr != 192.168.1.1 的意思是“包里的某个IP地址不等于192.168.1.1”,而不是“包里的所有IP地址都不等于192.168.1.1”。这俩差别可大了。

我曾经花了一整个下午查一个网络故障,怎么过滤都不对劲。后来发现就是这个坑——我想删除掉某个IP的包,结果因为包里有多个IP地址,条件永远为真,等于没过滤。

坑二:字符串比较大小写。 显示过滤器默认是大小写敏感的。比如 http.request.method == "get" 是匹配不到 GET 的。用 upper() 或者 lower() 可以解决。

坑三:协议嵌套。 比如你在一个IP隧道里又封装了IP,这时候 ip.addr 会同时匹配外层和内层的IP地址。如果你只想匹配内层,得用 ip.inner.addr 或者根据具体协议结构来。不过这种情况比较少见,一般只有在分析VPN或隧道流量时才会遇到。

嗯,说了这么多,其实核心就一句话:知道你想要的字段叫什么,然后用正确的逻辑把它们组织起来。Wireshark的显示过滤器虽然功能强大,但语法本身并不复杂,多练几次就熟了。

你可以用Wireshark自带的“表达式”对话框(在过滤器栏旁边那个“…”按钮)来辅助构建条件。我刚开始用的时候也是全靠它,后来慢慢记住了常用字段,就很少再去点了。

显示过滤器知识体系 显示过滤器核心 语法基础 协议名: http, tcp, dns 字段引用: ip.src, tcp.port 比较操作: ==, !=, >, < 表达式构建 逻辑运算符: and, or, not 括号分组: 控制优先级 组合条件: 精准定位 高级函数与字段 contains / matches upper() / lower() / len() 集合字段处理: ip.addr 实战案例 三次握手 / 重传 / POST请求 常见陷阱 集合字段语义 / 大小写敏感