问题背景
源自于和朋友的一次技术讨论,关于 Wireshark 如何查找特定字符串所在的 TCP 流,原始问题如下:
仔细琢磨了下,基于我对 Wireshark 的使用经验,感觉一步到位实现比较困难,所以想着说用 Wireshark CLI 工具 Tshark 来实现试试。
当然实际上我对 CLI 工具用得也不是很多,并不太熟悉,这也是 Wireshark CLI 这个系列也很少单独更新的缘故,用得少,自然也想不到。😅
问题信息
分析原始图片上的问题,实际上原作者的需求如下:
- 第一步需求,查找包含特定 string 的 TCP 流,伪代码:
tcp.stream contains "string"
; - 第二步需求,实际上是要过滤掉上述包含特定 string 的 TCP 流,伪代码:
(tcp.stream contains "string I do not want")
。
首先说到 TCP 流显示过滤语法,也就是 tcp.stream
,日常使用的语法如下,但却并没有 contains
或者 matches
相关 。
tcp.stream
tcp.stream == 1
tcp.stream != 1
tcp.stream eq 1
not tcp.stream eq 1
tcp.stream in {0,1,2}
...
所以大概计划通过以下几步,进行处理:
- 首先查找包含特定 string 的数据包;
- 之后输出上述数据包所在的流;
- 最后过滤掉上述流,保留其他剩余的数据包。
问题分析
处理的第一步,额,得先造一个案例数据包文件 test.pcapng,信息如下:
string:
heartbeattcp.stream:
0 和 2过滤后需保留的数据包:
No.10 和 No.11
- 查找包含特定 string 的数据包
既然没有明确说明是在什么字段里,需要过滤什么 string,那么就简单通过 frame 来包含,-Y 参数来实现,得到 No.4 和 No.12 数据包。
frame contains "heartbeat"λ tshark -r test.pcapng -Y 'frame contains "heartbeat"'4 0.000002126 192.168.0.1 → 10.10.10.1 HTTP 1 222 1 275 POST /api/heartbeat.json?md5=144072c14a5282c76bd44bdd864fadccdmid =a67b54f37b7120c3983c4d6519b71e55&ver=1.0 HTTP/1.112 2.200002126 192.168.0.2 → 10.10.10.1 HTTP 1 222 1 275 POST /api/heartbeat.json?md5=144072c14a5282c76bd44bdd864fadccdmid =a67b54f37b7120c3983c4d6519b71e55&ver=1.0 HTTP/1.1
λ
当然这一步在 Wireshark 上也是可以做到的,和 tshark -Y 是同样的语法。
- 输出上述数据包所在的流
再次通过 tshark 输出所要的字段值,这里需要的是数据包所在 tcp.stream 的编号,-T 和 -e 参数组合来实现,得到 No.4 和 No.12 数据包所在的 TCP Stream 0 和 2。
λ tshark -r test.pcapng -Y 'frame contains "heartbeat"' -T fields -e tcp.stream
0
2
λ 考虑到关键字也可能出现在同一条流中的几个数据包中,会重复输出同一条流的编号,此处可以再加上 uniq 去重。
λ tshark -r test.pcapng -Y 'frame contains "heartbeat"' -T fields -e tcp.stream | uniq
0
2
λ
- 过滤掉上述流,保留其他剩余的数据包
如果只是过滤掉上述流,过滤表达式比较简单,如下可以得到最终的结果 No.10 和 No.11 数据包。
!(tcp.stream==0) and !(tcp.stream==2)
!tcp.stream in {0,2}λ tshark -r test.pcapng -Y '!(tcp.stream==0) and !(tcp.stream==2)'10 0.100016618 192.168.0.1 → 192.10.3.1 TCP 1 1 0 58 [TCP Window Update] 0 → 0 [<None>] Seq=1 Win=65532 Len=011 0.200016618 192.168.0.1 → 192.168.0.255 UDP 46 0 → 0 [BAD UDP LENGTH 26 > IP PAYLOAD LENGTH] Len=18[Malformed Packet]
λ
但也是考虑到不同的案例数据包过滤出来的 TCP Stream 会有很多,再一个个去手写过滤条件会很麻烦,这也是最开始说通过 Wireshark 一步到位无法实现的原因,做是可以做,很不方便,确实没有 CLI 脚本工具来得快。
以下仍然通过 tshark ,首先通过 awk 构建出过滤掉上述流的显示过滤表达式。
awk '{printf("%s!(tcp.stream==%d)",sep,$1);sep=" and "}'λ tshark -r test.pcapng -Y 'frame contains "heartbeat"' -T fields -e tcp.stream | uniq | awk '{printf("%s!(tcp.stream==%d)",sep, $1);sep=" and "}'
!(tcp.stream==0) and !(tcp.stream==2)
λ
再套用一次 tshark -Y + 上述得到的显示过滤表达式,结果如下:
λ tshark -r test.pcapng -Y '!(tcp.stream==0) and !(tcp.stream==2)' 10 0.100016618 192.168.0.1 → 192.10.3.1 TCP 1 1 0 58 [TCP Window Update] 0 → 0 [<None>] Seq=1 Win=65532 Len=0 11 0.200016618 192.168.0.1 → 192.168.0.255 UDP 46 0 → 0 [BAD UDP LENGTH 26 > IP PAYLOAD LENGTH] Len=18[Malformed Packet]
λ
最终命令如下:
tshark -r test.pcapng -Y "$(tshark -r test.pcapng -Y 'frame contains "heartbeat"' -T fields -e tcp.stream | uniq | awk '{printf("%s!(tcp.stream==%d)",sep,$1);sep=" and "}')"