Linux抓包工具tcpdump使用小结

简介及安装

tcpdump 是一款用于截取网络分组,并过滤输出分组内容的工具。tcpdump 凭借强大的功能和灵活的截取策略,使其成为类 UNIX 系统下用于网络分析和问题排查的首选工具。 tcpdump 提供了源代码,公开了接口,因此具备很强的可扩展性,对于网络维护和入侵者都是非常有用的工具。tcpdump 存在于基本的 Linux 系统中,由于它需要将网络界面设置为混杂模式,普通用户不能正常执行,但具备 root 权限的用户可以直接执行它来获取网络上的信息。因此系统中存在网络分析工具主要不是对本机安全的威胁,而是对网络上的其他计算机的安全存在威胁。tcpdump 可以将网络中传送的数据包的 “头” 完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供 and、or、not 等逻辑语句来帮助我们去掉无用的信息。

tcpdump 默认在几乎所有的 Linux 发行版中都可用,但若你的 Linux 上没有的话,使用下面方法进行安装。
CentOS/RHEL使用下面命令在 CentOS 和 RHEL 上安装 tcpdump

sudo yum install tcpdump*

tcpdump命令详解

tcpdump [ 选项 ] [ -c 数量 ] [ -i 网络接口 ] [ -w 文件名 ] [ 表达式 ]
tcpdump
选项翻译如下: 

-l:使标准输出变为缓冲行形式;
-c:抓包次数;
-n: 禁用域名解析,让 tcpdump 直接输出 IP 地址;
-nn:直接以 IP 及 Port Number 显示,而非主机名与服务名称;
-s :< 数据包大小 & gt; 设置每个数据包的大小;
-i:指定监听的网络接口;
-r:从指定的文件中读取包;
-w:输出信息保存到指定文件;
-A: 以 ASCII 值显示抓到的包, 比如和 MySQL 的交互时,可以通过 - A 查看包的文本内容;
-a:将网络地址和广播地址转变成名字;
-d:将匹配信息包的代码以人们能够理解的汇编格式给出;
-e:在输出行打印出数据链路层的头部信息;
-f:将外部的 Internet 地址以数字的形式打印出来;
-t:在输出的每一行不打印时间戳;
-v :输出稍微详细的报文信息;
-vvv 会尝试解析应用层协议,输出详细信息。二者组合就能完整的详细信息;
-vvvv 该参数其实是 -v 与 -vvv 的组合;
-vv 则输出更详细信息。

tcpdump参数
tcpdump tcp -i bond0 -tttt -s 0 -c 100 and dst port ! 22 and src net 10.10.1.0/24 -w 20190131.tcpdump
  • tcp: 协议类型,用来过滤数据报的协议类型。
  • -i bond0 : 只抓取经过接口 bond0 的包
  • -tttt : 使用格式 2019-02-02 10:37:37.120297, 便于分析。
  • -s 0: 抓取数据包时默认抓取长度为 68 字节。加上 - s 0 后可以抓到完整的数据包
  • -c 100: 只抓取 100 个数据包
  • dst port ! 22: 不抓取目标端口是 22 的数据包
  • src net 10.10.1.0/24: 数据包的源网络地址为 10.10.1.0/24
  • -w 20190131.tcpdump: 保存成 tcpdump 文件中, 方便使用 wireshark 分析抓包结果。

还有其他常用的参数:

-D: 列出所有可用的网络接口
-X: 以 16 进制格式输出数据包的内容, 不加该参数, 会只输出 iptcp/udp 头部信息。
加上该选项会将整个数据包输出。

tcpdump表达式

在表达式中一般有如下几种类型的关键字:

  • 关于类型的关键字:
    host(缺省类型): 指明一台主机,如:host 10.215.20.13
    net: 指定网络地址, net 10.215.20.0
    port: 指明端口号, port 3306

  • 确定传输方向的关键字:

    dst or src(缺省值) 指定源或者目标地址是 10.215.20.13 的流量包
    src: src 10.9.51.13, 指定源地址是 10.9.51.13
    dst: dst net 172.0.0.0, 指定目标网络地址是 172.0.0.0
    dst and src 比如: src host 10.9.51.13 and dst host 10.215.20.13

  • 协议的关键字:
    协议的关键字:缺省值是监听所有协议的信息包
    ip
    arp
    tcp
    udp
    icmp

  • 三种逻辑运算:
    非 : ! , not
    与 : && , and
    或 : || , or

  • 其他重要的关键字:
    gateway, broadcast
    less(小于), greater(大于)

tcpdump 命令演示
  • 从所有网卡中捕获数据包:
tcpdump -i any
  • 从指定网卡中捕获数据包:
tcpdump -i eth0

查看某个协议的数据包:

tcpdump ssh

捕获某个端口或一个范围的数据包:

tcpdump port 22
tcpdump portrange 22-125
  • 使用 -w 选项将所有捕获的包写入文件:
tcpdump -i eth1 -w packets_file
  • 从之前创建的 tcpdump 文件中读取内容,-r:从指定的文件中读取包;
tcpdump -r packets_file
  • 获取更多的包信息,并且以可读的形式显示时间戳:
    -tttt : 使用格式 2019-02-02 10:37:37.120297便于分析;
    -nn:直接以 IP 及 Port Number 显示,而非主机名与服务名称;
    -vv 则输出更详细信息;
    -S 用绝对而非相对数值列出TCP关联数。
tcpdump -ttttnnvvS

查看整个目标网络的数据包:

tcpdump net 192.168.1.0/24

获取指定 IP 的数据包,不管是作为源地址还是目的地址:

tcpdump host 192.168.1.100

要指定 IP 地址是源地址或是目的地址则使用:

tcpdump src 192.168.1.100
tcpdump dst 192.168.1.100

我们还可以使用 “与” (and,&&)、“或” (or,|| ) 和 “非”(not,!) 来将两个条件组合起来。当我们需要基于某些条件来分析网络报文是非常有用。
使用 and 或者符号 && 来将两个或多个条件组合起来:

tcpdump src 192.168.1.100 && port 22 -w ssh_packets

使用 “或”,“或” 会检查是否匹配命令所列条件中的其中一条:

tcpdump src 192.168.1.100 or dst 192.168.1.50 && port 22 -w ssh_packets
tcpdump port 443 or 80 -w http_packets

想表达不匹配某项条件时可以使用 “非”:

tcpdump -i eth0 src port not 22
常用抓包命令组合
# 抓取包含 172.16.1.122 的数据包  
tcpdump -i eth0 -vnn host 172.16.1.122  
# 抓取包含 172.16.1.0/24 网段的数据包  
tcpdump -i eth0 -vnn net 172.16.1.0/24  
# 抓取包含端口 22 的数据包  
tcpdump -i eth0 -vnn port 22  
# 抓取 udp 协议的数据包  
tcpdump -i eth0 -vnn  udp  
# 抓取 icmp 协议的数据包  
tcpdump -i eth0 -vnn icmp  
# 抓取 arp 协议的数据包  
tcpdump -i eth0 -vnn arp  
# 抓取 ip 协议的数据包  
tcpdump -i eth0 -vnn ip  
# 抓取源 ip 是 172.16.1.122 数据包。  
tcpdump -i eth0 -vnn src host 172.16.1.122  
# 抓取目的 ip 是 172.16.1.122 数据包  
tcpdump -i eth0 -vnn dst host 172.16.1.122  
# 抓取源端口是 22 的数据包  
tcpdump -i eth0 -vnn src port 22  
# 抓取源 ip 是 172.16.1.253 且目的 ip 是 22 的数据包  
tcpdump -i eth0 -vnn src host 172.16.1.253 and dst port 22  
# 抓取源 ip 是 172.16.1.122 或者包含端口是 22 的数据包  
tcpdump -i eth0 -vnn src host 172.16.1.122 or port 22  
# 抓取源 ip 是 172.16.1.122 且端口不是 22 的数据包  
tcpdump -i eth0 -vnn src host 172.16.1.122 and not port 22  
# 抓取源 ip 是 172.16.1.2 且目的端口是 22,或源 ip 是 172.16.1.65 且目的端口是 80 的数据包。  
tcpdump -i eth0 -vnn \( src host 172.16.1.2 and dst port 22 \) or   \( src host 172.16.1.65 and dst port 80 \)  
# 抓取源 ip 是 172.16.1.59 且目的端口是 22,或源 ip 是 172.16.1.68 且目的端口是 80 的数据包。  
tcpdump -i  eth0 -vnn 'src host 172.16.1.59 and dst port 22' or  'src host 172.16.1.68 and dst port 80'  
# 把抓取的数据包记录存到 / tmp/fill 文件中,当抓取 100 个数据包后就退出程序。  
tcpdump –i eth0 -vnn -w  /tmp/fil1 -c 100  
# 从 / tmp/fill 记录中读取 tcp 协议的数据包  
tcpdump –i eth0 -vnn -r  /tmp/fil1 tcp
# 从 / tmp/fill 记录中读取包含 172.16.1.58 的数据包  
tcpdump –i eth0 -vnn -r  /tmp/fil1 host  172.16.1.58 
# 抓取目的地址范围是 10 网段
tcpdump -i any -nn 'ip[16] == 10'
# 抓取目的地址范围是 192.168.1.10 ~ 192.168.1.100
tcpdump -i any -nn 'ip[16] == 192 and ip[17] == 168 and ip[18] == 1 and ip[19] > 9 and ip[19] < 101'
# 保存 10000 个数据包过滤条件为 443 端口,并解析来源 IP
tcpdump -i any -nn -c 10000 port 443 > tcpdump.log
cat tcpdump.log | awk '{print $3}' | awk -F '.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -rn
# 查看 source IP
cat tcpdump.log | awk '{print $1}' | awk -F '.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -rn | grep -v ":"

tcpdump抓包实测

我们用如下命令先对 any 接口进行抓包:

$ sudo tcpdump -i any
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
09:56:18.293641 IP rhel75.localdomain.ssh > 192.168.64.1.56322: Flags [P.], seq 3770820720:3770820916, ack 3503648727, win 309, options [nop,nop,TS val 76577898 ecr 510770929], length 196
09:56:18.293794 IP 192.168.64.1.56322 > rhel75.localdomain.ssh: Flags [.], ack 196, win 391, options [nop,nop,TS val 510771017 ecr 76577898], length 0
09:56:18.295058 IP rhel75.59883 > gateway.domain: 2486+ PTR? 1.64.168.192.in-addr.arpa. (43)
09:56:18.310225 IP gateway.domain > rhel75.59883: 2486 NXDomain* 0/1/0 (102)
09:56:18.312482 IP rhel75.49685 > gateway.domain: 34242+ PTR? 28.64.168.192.in-addr.arpa. (44)
09:56:18.322425 IP gateway.domain > rhel75.49685: 34242 NXDomain* 0/1/0 (103)
09:56:18.323164 IP rhel75.56631 > gateway.domain: 29904+ PTR? 1.122.168.192.in-addr.arpa. (44)
09:56:18.323342 IP rhel75.localdomain.ssh > 192.168.64.1.56322: Flags [P.], seq 196:584, ack 1, win 309, options [nop,nop,TS val 76577928 ecr 510771017], length 388
09:56:18.323563 IP 192.168.64.1.56322 > rhel75.localdomain.ssh: Flags [.], ack 584, win 411, options [nop,nop,TS val 510771047 ecr 76577928], length 0
09:56:18.335569 IP gateway.domain > rhel75.56631: 29904 NXDomain* 0/1/0 (103)
09:56:18.336429 IP rhel75.44007 > gateway.domain: 61677+ PTR? 98.122.168.192.in-addr.arpa. (45)
09:56:18.336655 IP gateway.domain > rhel75.44007: 61677* 1/0/0 PTR rhel75. (65)
09:56:18.337177 IP rhel75.localdomain.ssh > 192.168.64.1.56322: Flags [P.], seq 584:1644, ack 1, win 309, options [nop,nop,TS val 76577942 ecr 510771047], length 1060
---- SKIPPING LONG OUTPUT -----
09:56:19.342939 IP 192.168.64.1.56322 > rhel75.localdomain.ssh: Flags [.], ack 1752016, win 1444, options [nop,nop,TS val 510772067 ecr 76578948], length 0
^C
9003 packets captured
9010 packets received by filter
7 packets dropped by kernel
$

tcpdump 会持续抓包直到收到中断信号。你可以按 Ctrl+C 来停止抓包。正如上面示例所示,tcpdump 抓取了超过 9000 个数据包。在这个示例中,由于我是通过 ssh 连接到服务器,所以 tcpdump 也捕获了所有这类数据包。-c 选项可以用于限制 tcpdump 抓包的数量:

$ sudo tcpdump -i any -c 5
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
11:21:30.242740 IP rhel75.localdomain.ssh > 192.168.64.1.56322: Flags [P.], seq 3772575680:3772575876, ack 3503651743, win 309, options [nop,nop,TS val 81689848 ecr 515883153], length 196
11:21:30.242906 IP 192.168.64.1.56322 > rhel75.localdomain.ssh: Flags [.], ack 196, win 1443, options [nop,nop,TS val 515883235 ecr 81689848], length 0
11:21:30.244442 IP rhel75.43634 > gateway.domain: 57680+ PTR? 1.64.168.192.in-addr.arpa. (43)
11:21:30.244829 IP gateway.domain > rhel75.43634: 57680 NXDomain 0/0/0 (43)
11:21:30.247048 IP rhel75.33696 > gateway.domain: 37429+ PTR? 28.64.168.192.in-addr.arpa. (44)
5 packets captured
12 packets received by filter
0 packets dropped by kernel
$

如上所示,tcpdump 在抓取 5 个数据包后自动停止了抓包。这在有些场景中十分有用 —— 比如你只需要抓取少量的数据包用于分析。当我们需要使用过滤规则抓取特定的数据包(如下所示)时,-c 的作用就十分突出了。

在上面示例中,tcpdump 默认是将 IP 地址和端口号解析为对应的接口名以及服务协议名称。而通常在网络故障排查中,使用 IP 地址和端口号更便于分析问题;用 -n 选项显示 IP 地址,-nn 选项显示端口号:

$ sudo tcpdump -i any -c5 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
23:56:24.292206 IP 192.168.64.28.22 > 192.168.64.1.35110: Flags [P.], seq 166198580:166198776, ack 2414541257, win 309, options [nop,nop,TS val 615664 ecr 540031155], length 196
23:56:24.292357 IP 192.168.64.1.35110 > 192.168.64.28.22: Flags [.], ack 196, win 1377, options [nop,nop,TS val 540031229 ecr 615664], length 0
23:56:24.292570 IP 192.168.64.28.22 > 192.168.64.1.35110: Flags [P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 615664 ecr 540031229], length 372
23:56:24.292655 IP 192.168.64.1.35110 > 192.168.64.28.22: Flags [.], ack 568, win 1400, options [nop,nop,TS val 540031229 ecr 615664], length 0
23:56:24.292752 IP 192.168.64.28.22 > 192.168.64.1.35110: Flags [P.], seq 568:908, ack 1, win 309, options [nop,nop,TS val 615664 ecr 540031229], length 340
5 packets captured
6 packets received by filter
0 packets dropped by kernel

如上所示,抓取的数据包中显示 IP 地址和端口号。这样还可以阻止 tcpdump 发出 DNS 查找,有助于在网络故障排查中减少数据流量。

理解抓取的报文

tcpdump 抓取的 TCP 报文如下:

08:41:13.729687 IP 192.168.64.28.22 > 192.168.64.1.41916: Flags [P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 117964079 ecr 816509256], length 372

具体的字段根据不同的报文类型会有不同,但上面这个例子是一般的格式形式。

  • 第一个字段 08:41:13.729687 是该数据报文被抓取的系统本地时间戳。

  • 第二个字段IP 是网络层协议类型,这里是 IPv4,如果是 IPv6 协议,该字段值是 IP6。

  • 192.168.64.28.22 是源 ip 地址和端口号,紧跟其后的是目的 ip 地址和其端口号,这里是 192.168.64.1.41916。

  • 在源 IP 和目的 IP 之后,可以看到是 TCP 报文标记段 Flags [P.]。该字段通常取值如下:
    在这里插入图片描述

    该字段也可以是这些值的组合,例如 [S.] 代表 SYN-ACK 数据包。

  • 该数据包中数据的序列号。对于抓取的第一个数据包,该字段值是一个绝对数字,后续包使用相对数值,以便更容易查询跟踪。例如此处 seq 196:568 代表该数据包包含该数据流的第 196 到 568 字节。

  • ack 值:ack 1。该数据包是数据发送方,ack 值为 1。在数据接收方,该字段代表数据流上的下一个预期字节数据,例如,该数据流中下一个数据包的 ack 值应该是 568。

  • 接下来字段是接收窗口大小 win 309,它表示接收缓冲区中可用的字节数,后跟 TCP 选项如 MSS(最大段大小)或者窗口比例值。更详尽的 TCP 协议内容请参考 Transmission Control Protocol(TCP) Parameters。

  • length 372 代表数据包有效载荷字节长度。这个长度和 seq 序列号中字节数值长度是不一样的。