使用 tshark 监视和检查网络流量

你们中的大多数人可能听说过 Wireshark,这是一个非常流行且功能强大的网络协议分析器。您可能不知道的是,存在一个名为 tshark 的 Wireshark 控制台版本。tshark 的两个主要优点是它可以在脚本中使用,也可以通过 SSH 连接在远程计算机上使用。它的主要缺点是它没有 GUI,当您必须搜索大量网络数据时,GUI 会非常方便。

您可以从其网站获取 tshark 并自行编译,也可以从您的 Linux 发行版获取预编译的软件包。第二种方法更快更简单。要在 Debian 7 系统上安装 tshark,您只需以 root 身份运行以下命令


# apt-get install tshark
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  libc-ares2 libcap2-bin libpam-cap libsmi2ldbl 
  libwireshark-data libwireshark2
  libwiretap2 libwsutil2 wireshark-common
Suggested packages:
  libcap-dev snmp-mibs-downloader wireshark-doc
The following NEW packages will be installed:
  libc-ares2 libcap2-bin libpam-cap libsmi2ldbl 
  libwireshark-data libwireshark2
  libwiretap2 libwsutil2 tshark wireshark-common
0 upgraded, 10 newly installed, 0 to remove and 0 not upgraded.
Need to get 15.6 MB of archives.
After this operation, 65.7 MB of additional disk space will be used.
Do you want to continue [Y/n]? Y
...

要了解 tshark 是否正确安装以及其版本,请执行以下命令


$ tshark -v
TShark 1.8.2
...

注意:本文假设您已经熟悉网络数据、TCP/IP、数据包捕获以及可能 Wireshark,并且您想了解更多关于 tshark 的信息。

关于 tshark

tshark 可以完成 Wireshark 可以做的任何事情,前提是它不需要 GUI。它也可以用作 tcpdump 的替代品,tcpdump 曾经是网络数据捕获的行业标准。除了捕获部分,这两个工具是等效的,tshark 比 tcpdump 更强大;因此,如果您只想学习一个工具,tshark 应该是您的选择。

您可以想象,tshark 有许多命令行选项。请参阅其手册页以获取完整列表。

使用 tshark 捕获网络流量

您应该运行的第一个命令是 sudo tshark -D 以获取可用网络接口的列表


$ sudo tshark -D
1. eth0
2. nflog (Linux netfilter log (NFLOG) interface)
3. any (Pseudo-device that captures on all interfaces)
4. lo

如果您以普通用户身份运行 tshark,您很可能会得到以下输出,因为普通用户没有直接访问网络接口设备的权限


$ tshark -D
tshark: There are no interfaces on which a capture can be done

捕获数据的最简单方法是运行不带任何参数的 tshark,这将在屏幕上显示所有数据。您可以通过按 Ctrl-C 停止数据捕获。

在繁忙的网络上,输出将滚动得非常快,因此根本没有帮助。较旧的计算机无法跟上繁忙的网络,因此像 tshark 和 tcpdump 这样的程序过去常常丢弃网络数据包。由于现代计算机非常强大,这已不再是问题。

使用文件保存和读取网络数据

最有用的命令行参数是 -w,后跟文件名。此参数允许您将网络数据保存到文件中,以便稍后处理。以下 tshark 命令捕获 500 个网络数据包 (-c 500) 并将它们保存到名为 LJ.pcap 的文件中 (-w LJ.pcap)


$ tshark -c 500 -w LJ.pcap

第二个最有用的参数是 -r。当后跟有效文件名时,它允许您读取和处理先前捕获的网络数据文件。

捕获过滤器

捕获过滤器是在数据捕获期间应用的过滤器;因此,它们使 tshark 丢弃不符合过滤器标准的网络流量,并避免创建巨大的捕获文件。这可以使用 -f 命令行参数完成,后跟双引号中的过滤器。

捕获过滤器中使用的最重要的 TCP 相关字段名称是 tcp.port(用于过滤源或目标 TCP 端口)、tcp.srcport(用于检查 TCP 源端口)和 tcp.dstport(用于检查目标端口)。

一般来说,在数据捕获后应用过滤器被认为比在捕获阶段过滤更实用和通用,因为大多数时候,您事先不知道您想检查什么。然而,如果您真的知道自己在做什么,使用捕获过滤器可以节省您的时间和磁盘空间,这也是使用它们的主要原因。

请记住,过滤器字符串应始终以小写形式书写。

显示过滤器

显示过滤器是在数据包捕获后应用的过滤器;因此,它们只是“隐藏”网络流量而不删除它。您始终可以删除显示过滤器的效果并取回所有数据。

显示过滤器支持比较和逻辑运算符。http.response.code == 404 && ip.addr == 192.168.10.1 显示过滤器显示来自 192.168.10.1 IP 地址或去往 192.168.10.1 IP 地址的流量,并且该流量还包含 404(未找到)HTTP 响应代码。!bootp && !ip 过滤器从输出中排除 BOOTP 和 IP 流量。eth.addr == 01:23:45:67:89:ab && tcp.port == 25 过滤器显示与 MAC 地址为 01:23:45:67:89:ab 的网络设备之间使用 TCP 端口 25 进行传入或传出连接的流量。

在定义规则时,请记住 ip.addr != 192.168.1.5 表达式并不意味着任何 ip.addr 字段都不能包含 192.168.1.5 IP 地址。它意味着其中一个 ip.addr 字段不应包含 192.168.1.5 IP 地址!因此,另一个 ip.addr 字段值可以等于 192.168.1.5!您可以将其理解为“存在一个不为 192.168.1.5 的 ip.addr 字段”。表达它的正确方法是键入 !(ip.addr == 192.168.1.5)。这是显示过滤器中常见的误解。

另请记住,当您想在 LAN 上跟踪给定的机器时,MAC 地址非常有用,因为机器的 IP 地址可能会在使用 DHCP 时更改,但其 MAC 地址更难更改。

显示过滤器在正确使用时是非常有用的工具,但您仍然必须解释结果,找到问题并自己考虑可能的解决方案。建议您访问 TCP 相关流量的显示过滤器参考站点:http://www.wireshark.org/docs/dfref/t/tcp.html。有关与 UDP 流量相关的所有可用字段名称的列表,请参阅 http://www.wireshark.org/docs/dfref/u/udp.html

导出数据

假设您想从先前捕获的网络流量中提取帧号、帧的相对时间、源 IP 地址、目标 IP 地址、数据包的协议和网络数据包的长度。以下 tshark 命令将为您完成这项工作


$ tshark -r login.tcpdump -T fields -e frame.number -e 
 ↪frame.time_relative -e ip.src -e ip.dst -e 
 ↪frame.protocols -e frame.len -E header=y -E 
 ↪quote=n -E occurrence=f

-E header=y 选项告诉 tshark 首先打印标题行。-E quote=n 指示 tshark 不要在引号中包含数据,-E occurrence=f 告诉 tshark 仅对具有多次出现的字段使用第一次出现。

将纯文本作为输出意味着您可以轻松地以 UNIX 方式处理它。以下命令显示使用来自 ip.src 字段的输入的最流行的十个 IP


$ tshark -r ~/netData.pcap -T fields -e ip.src | sort 
 ↪| sed '/^\s*$/d' | uniq -c | sort -rn
 ↪| awk {'print $2 " " $1'} | head
使用 tshark 的两个 Python 脚本

现在,让我们看看两个读取 tshark 文本输出并处理它的 Python 脚本。我无法想象使用 GUI 应用程序(例如 Wireshark)做同样的事情!

清单 1 显示了第一个脚本的完整 Python 代码,该脚本检查 IP 地址的有效性。

清单 1. checkIP.py

# Programmer: Mihalis Tsoukalos
# Date: Tuesday 28 October 2014

import socket
import sys
import re

def valid_ip(address):
    try:
        socket.inet_aton(address)
        return True
    except:
        return False

# Counters for the IPs
total = 0
valid = 0
invalid = 0

# Read the file from stdin, line by line
for line in sys.stdin:
        line = line.rstrip('\n')
        if valid_ip(line):
            valid = valid + 1
            # print "The IP is valid!"
        else:
            # print "The IP is not valid!"
            invalid = invalid + 1
        total = total + 1

# Present the total number of IPs checked
print "Total number of IPs checked:", total
print "Valid IPs found:", valid
print "Invalid IPs found:", invalid

checkIP.py Python 脚本的目的是找到无效的 IP 地址,并且它意味着网络数据已经使用 tshark 捕获。您可以按如下方式使用它


$ tshark -r ~/networkData.pcap -T fields -e ip.src 
 ↪| python checkIP.py
Total number of IPs checked: 1000
Valid IPs found: 896
Invalid IPs found: 104

清单 2 显示了第二个 Python 脚本(storeMongo.py)的完整代码。

清单 2. store Mongo.py

# Programmer: Mihalis Tsoukalos
# Date: Tuesday 28 October 2014
#
# Description: This Python script reads input from 
# tshark, parses it and stores it in a MongoDB database

import sys
import pymongo
import re

# The number of BSON documents written
total = 0

# Open the MongoDB connection
connMongo = pymongo.Connection('mongodb://127.0.0.1:27017')
# Connect to database named LJ (Linux Journal)
db = connMongo.LJ
# Select the collection to save the network packet
traffic = db.netdata

# Read the file from stdin, line by line
for line in sys.stdin:
        line = line.rstrip('\n')
        parsed = line.split("\t")
        total = total + 1

        # Construct the "record to be inserted
        netpacket = {
                'framenumber': parsed[0],
                'sourceIP': parsed[1],
                'destIP': parsed[2],
                'framelength': parsed[3],
                'IPlength': parsed[4]
                }

        # Store it!
        net_id = traffic.insert(netpacket)

connMongo.close()

# Present the total number of BSON documents written
print "Total number of documents stored: ", total

清单 2 中显示的 Python 脚本将网络数据插入到 MongoDB 数据库中,以进行进一步的处理和查询。您可以使用任何您想要的数据库。我使用 MongoDB 的主要原因是因为我喜欢它在存储可能有一些不规则记录(缺少字段的记录)的结构化数据时提供的灵活性。

Python 脚本的名称是 storeMongo.py,它假设网络数据已经使用 tshark 或 tcpdump 捕获。下一个 shell 命令使用来自 tshark 的输入运行 Python 脚本


$ tshark -r ~/var/test.pcap -T fields -e frame.number 
 ↪-e ip.src -e ip.dst -e frame.len -e
 ↪ip.len -E header=n -E quote=n -E occurrence=f 
 ↪| python storeMongo.py
Total number of documents stored:  500

tshark 命令的文本输出类似于以下内容


5    yy.xx.zz.189    yyy.74.xxx.253  66      52
6    197.224.xxx.145 yyy.74.xxx.253  86      72
7    109.xxx.yyy.253 zzz.224.xxx.145 114     100
8    197.xxx.zzz.145 zzz.xxx.xxx.253 86      72
9    109.zzz.193.yyy 197.224.zzz.145 114     100

目前,所有数值都存储为字符串,但如果您愿意,可以轻松地将它们转换为数字。以下命令将 IPlength 列中的所有字符串值转换为它们各自的整数值


> db.netdata.find({IPlength : {$exists : true}}).forEach( 
 ↪function(obj) { obj.IPlength = new NumberInt( 
 ↪obj.IPlength ); db.netdata.save(obj); } );

现在您可以开始查询 MongoDB 数据库。以下命令查找包含给定目标 IP 地址的所有“记录”(在 NoSQL 术语中称为文档)


> use LJ
switched to db LJ
> db.netdata.find({ "destIP": "192.168.1.12" })
...
>

下一个命令查找所有 frame.len 值小于 70 的条目


> use LJ
switched to db LJ
> db.netdata.find({ "framelength": {"$lt" : "70" }})
...
>

下一个命令查找所有 IPlength 值大于 100 且小于 200 的条目


> use LJ
switched to db LJ
> db.netdata.find({ "IPlength": {"$lt" : "200", "$gt": "100" }})
...
>

您应该记住的不是实际的命令,而是您可以查询您选择的数据库,使用您想要的查询语言,并找到有用的信息,而无需重新运行 tshark 并再次解析网络数据。

在您测试您的查询后,您可以将它们作为 cron 作业运行。生活真美好!

使用 tshark 检查 Nmap ping 扫描

接下来,让我们检查 Nmap 在执行 ping 扫描时生成的网络流量。ping 扫描的目的是简单地找出 IP 地址是否启动。对于 Nmap 在 ping 扫描中重要的是,不是接收数据包的实际数据,而是简而言之,回复数据包的实际存在。LAN 内的 Nmap ping 扫描使用 ARP 协议;而 LAN 外部的主机使用 ICMP 协议进行扫描。执行的扫描 ping 了 LAN 外部的 IP 地址。

以下 Nmap 命令扫描 64 个 IP 地址,从 2.x.yy.1 到 2.x.yy.64


# nmap -sP 2.x.yy.1-64
Starting Nmap 6.00 ( http://nmap.org ) at 2014-10-29 11:55 EET
Nmap scan report for ppp-4.home.SOMEisp.gr (2.x.yy.4)
Host is up (0.067s latency).
Nmap scan report for ppp-6.home.SOMEisp.gr (2.x.yy.6)
Host is up (0.084s latency).
...
Nmap scan report for ppp-64.home.SOMEisp.gr (2.x.yy.64)
Host is up (0.059s latency).
Nmap done: 64 IP addresses (35 hosts up) scanned in 3.10 seconds

结果显示,在执行时只有 35 台主机启动,或者更准确地说,只有 35 台主机响应了 Nmap 扫描。Nmap 还计算了往返时间延迟(或延迟)。这给出了初始数据包(由 Nmap 发送)到达目标设备所需的时间加上响应数据包返回 Nmap 所需的时间的相当准确的估计。

以下 tshark 命令用于捕获,并通过 Ctrl-C 终止


# tshark -w nmap.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on eth0
2587 ^C
18 packets dropped
# ls -l nmap.pcap
-rw------- 1 root root 349036 Oct 29 11:55 nmap.pcap

现在,让我们使用 tshark 分析生成的流量。以下命令搜索到或来自 2.x.yy.6 IP 地址的流量


$ tshark -r nmap.pcap -R "ip.src == 2.x.yy.6 || ip.dst == 2.x.yy.6"
712  3.237125000 109.zz.yyy.253 -> 2.x.yy.6     
 ↪ICMP 42 Echo (ping) request  id=0xa690, seq=0/0, ttl=54
1420 5.239804000 109.zz.yyy.253 -> 2.x.yy.6     
 ↪ICMP 42 Echo (ping) request  id=0x699a, seq=0/0, ttl=49
1432 5.240111000 109.zz.yyy.253 -> 2.x.yy.6     
 ↪TCP 58 41242 > https [SYN] Seq=0 Win=1024 Len=0 MSS=1460
1441 5.296861000     2.x.yy.6 -> 109.zz.yyy.253 ICMP 60 
 ↪Timestamp reply      id=0x0549, seq=0/0, ttl=57

正如您所看到的,来自 2.x.yy.6 的响应数据包 (1441) 的存在足以让主机被 Nmap 视为启动;因此,不会在此 IP 上尝试其他测试。

现在,让我们看看被认为已关闭的 IP 的流量


$ tshark -r nmap.pcap -R "ip.src == 2.x.yy.2 || ip.dst == 2.x.yy.2"
708  3.236922000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪ICMP 42 Echo (ping) request  id=0xb194, seq=0/0, ttl=59
1407 5.237255000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪ICMP 42 Echo (ping) request  id=0x24ed, seq=0/0, ttl=47
1410 5.237358000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪TCP 58 41242 > https [SYN] Seq=0 Win=1024 Len=0 MSS=1460
1413 5.237448000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪TCP 54 41242 > http [ACK] Seq=1 Ack=1 Win=1024 Len=0
1416 5.237533000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪ICMP 54 Timestamp request    id=0xf7af, seq=0/0, ttl=51
1463 5.348871000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪ICMP 54 Timestamp request    id=0x9d7e, seq=0/0, ttl=39
1465 5.349006000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪TCP 54 41243 > http [ACK] Seq=1 Ack=1 Win=1024 Len=0
1467 5.349106000 109.zz.yyy.253 -> 2.x.yy.2     
 ↪TCP 58 41243 > https [SYN] Seq=0 Win=1024 Len=0 MSS=1460

由于 ICMP 数据包没有得到响应,Nmap 通过发送 HTTP 和 HTTPS 数据包在 2.x.yy.2 IP 上进行了更多尝试,但仍然没有成功。发生这种情况是因为 Nmap 通过尝试一些常见的 TCP 端口来增加对标准 ping(ICMP 协议)的智能,以防 ICMP 请求由于某种原因被阻止。

可以使用以下命令找到发送的 ICMP 数据包总数


$ tshark -r nmap.pcap -R "icmp" | grep "2.x" | wc -l
233
显示特定协议的统计信息

tshark 允许您显示有关特定协议的有用统计信息。以下命令使用包含网络数据的现有文件显示有关 HTTP 协议的统计信息


$ tshark -q -r http.pcap -R http -z http,tree

=====================================================
 HTTP/Packet Counter      value    rate     percent
-----------------------------------------------------
 Total HTTP Packets        118   0.017749
  HTTP Request Packets      66   0.009928    55.93%
   GET                      66   0.009928   100.00%
  HTTP Response Packets     52   0.007822    44.07%
   ???: broken               0   0.000000     0.00%
   1xx: Informational        0   0.000000     0.00%
   2xx: Success             51   0.007671    98.08%
    200 OK                  51   0.007671   100.00%
   3xx: Redirection          0   0.000000     0.00%
   4xx: Client Error         1   0.000150     1.92%
    404 Not Found            1   0.000150   100.00%
   5xx: Server Error         0   0.000000     0.00%
  Other HTTP Packets         0   0.000000     0.00%

=====================================================

所有工作都由 -z 选项完成,该选项用于计算统计信息,以及 -q 选项,该选项用于禁用打印每个单独数据包的信息。-R 选项在进行任何其他处理之前丢弃所有与指定过滤器不匹配的数据包。

这是另一个有用的命令,显示协议层次结构统计信息


$ tshark -nr ~/var/http.pcap -qz "io,phs"

自己尝试一下以查看输出!

总结

如果您深入理解显示过滤器并很好地了解 TCP/IP 和网络,借助 tshark 或 Wireshark,与网络相关的问题将不再是问题。

掌握 tshark 需要时间,但我认为这将是值得花费的时间。

资源

tshark: http://www.wireshark.org/docs/man-pages/tshark.html

Wireshark: http://www.wireshark.org

显示过滤器参考: http://www.wireshark.org/docs/dfref

Internetworking with TCP/IP, Volume I, Douglas E. Comer, Prentice Hall

Mihalis Tsoukalos 是一位 UNIX 管理员和开发人员,一位 DBA 和数学家,他喜欢技术写作。他是 Go Systems ProgrammingMastering Go 的作者。您可以通过 http://www.mtsoukalos.eu 和 @mactsouk 联系他。

加载 Disqus 评论