前言:

网络这块知识点一直都是笔者的薄弱之处。虽然也一直在学习理论知识,但是总觉得学完就忘,一直在重复学习。

究其原因,应该还是理论没有与实践结合在一起。而想学习具象的网络知识,那么工具就是必不可少的。

目前的工具有两类:wireshark(可视化工具)、tcpdump(命令)。本文我们就来学习下tcpdump命令的使用

1.tcpdump作用

tcpdump是一款强大的网络抓包工具。通过tcpdump命令的使用可以帮助我们分析、调试网络数据

2.tcpdump语法

同样我们通过man命令来查看其基本语法

NAME
       tcpdump - dump traffic on a network

SYNOPSIS
       tcpdump [ -AbdDefhHIJKlLnNOpqStuUvxX# ] [ -B buffer_size ]
               [ -c count ]
               [ -C file_size ] [ -G rotate_seconds ] [ -F file ]
               [ -i interface ] [ -j tstamp_type ] [ -m module ] [ -M secret ]
               [ --number ] [ -Q in|out|inout ]
               [ -r file ] [ -V file ] [ -s snaplen ] [ -T type ] [ -w file ]
               [ -W filecount ]
               [ -E spi@ipaddr algo:secret,...  ]
               [ -y datalinktype ] [ -z postrotate-command ] [ -Z user ]
               [ --time-stamp-precision=tstamp_precision ]
               [ --immediate-mode ] [ --version ]
               [ expression ]

具体参数细节可以通过man tcpdump查看,下面列举一些常用的参数

-A 以ASCII格式打印出所有分组,并将链路层的头最小化。

-c 在收到指定的数量的分组后,tcpdump就会停止。

-C 在将一个原始分组写入文件之前,检查文件当前的大小是否超过了参数file_size 中指定的大小。如果超过了指定大小,则关闭当前文件,然后在打开一个新的文件。
参数 file_size 的单位是兆字节(是1,000,000字节,而不是1,048,576字节)。

-d 将匹配信息包的代码以人们能够理解的汇编格式给出。

-dd 将匹配信息包的代码以c语言程序段的格式给出。

-ddd 将匹配信息包的代码以十进制的形式给出。

-D 打印出系统中所有可以用tcpdump截包的网络接口。

-e 在输出行打印出数据链路层的头部信息。

-E 用spi@ipaddr algo:secret解密那些以addr作为地址,并且包含了安全参数索引值spi的IPsec ESP分组。

-f 将外部的Internet地址以数字的形式打印出来。

-F 从指定的文件中读取表达式,忽略命令行中给出的表达式。

-i 指定监听的网络接口。

-l 使标准输出变为缓冲行形式,可以把数据导出到文件。

-L 列出网络接口的已知数据链路。

-m 从文件module中导入SMI MIB模块定义。该参数可以被使用多次,以导入多个MIB模块。

-M 如果tcp报文中存在TCP-MD5选项,则需要用secret作为共享的验证码用于验证TCP-MD5选选项摘要(详情可参考RFC 2385)。

-b 在数据-链路层上选择协议,包括ip、arp、rarp、ipx都是这一层的。

-n 不把网络地址转换成名字。

-nn 不进行端口名称的转换。

-N 不输出主机名中的域名部分。例如,‘nic.ddn.mil‘只输出’nic‘。

-t 在输出的每一行不打印时间戳。

-O 不运行分组分组匹配(packet-matching)代码优化程序。

-P 不将网络接口设置成混杂模式。

-q 快速输出。只输出较少的协议信息。

-r 从指定的文件中读取包(这些包一般通过-w选项产生)。

-S 将tcp的序列号以绝对值形式输出,而不是相对值。

-s 从每个分组中读取最开始的snaplen个字节,而不是默认的68个字节。

-T 将监听到的包直接解释为指定的类型的报文,常见的类型有rpc远程过程调用)和snmp(简单网络管理协议;)。

-t 不在每一行中输出时间戳。

-tt 在每一行中输出非格式化的时间戳。

-ttt 输出本行和前面一行之间的时间差。

-tttt 在每一行中输出由date处理的默认格式的时间戳。

-u 输出未解码的NFS句柄。

-v 输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息。

-vv 输出详细的报文信息。

-w 直接将分组写入文件中,而不是不分析并打印出来。

以上是输入参,那么拦截到请求后,输出是怎样的呢,我们拦截一次请求来看下(nginx80端口拦截)

root@aa4e7f274852:/tmp# tcpdump port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:23:24.776819 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [S], seq 4130437317, win 65495, options [mss 65495,sackOK,TS val 4219824570 ecr 0,nop,wscale 7], length 0
13:23:24.776863 IP aa4e7f274852.80 > 172.17.0.1.61002: Flags [S.], seq 2368836658, ack 4130437318, win 65160, options [mss 1460,sackOK,TS val 1442703107 ecr 4219824570,nop,wscale 7], length 0
13:23:24.776942 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [.], ack 1, win 512, options [nop,nop,TS val 4219824570 ecr 1442703107], length 0
13:23:24.778381 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [P.], seq 1:74, ack 1, win 512, options [nop,nop,TS val 4219824572 ecr 1442703107], length 73: HTTP: GET / HTTP/1.1
13:23:24.778414 IP aa4e7f274852.80 > 172.17.0.1.61002: Flags [.], ack 74, win 509, options [nop,nop,TS val 1442703109 ecr 4219824572], length 0
13:23:24.778705 IP aa4e7f274852.80 > 172.17.0.1.61002: Flags [P.], seq 1:239, ack 74, win 509, options [nop,nop,TS val 1442703109 ecr 4219824572], length 238: HTTP: HTTP/1.1 200 OK
13:23:24.778751 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [.], ack 239, win 511, options [nop,nop,TS val 4219824572 ecr 1442703109], length 0
13:23:24.778793 IP aa4e7f274852.80 > 172.17.0.1.61002: Flags [P.], seq 239:854, ack 74, win 509, options [nop,nop,TS val 1442703109 ecr 4219824572], length 615: HTTP
13:23:24.778810 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [.], ack 854, win 507, options [nop,nop,TS val 4219824572 ecr 1442703109], length 0
13:23:24.788749 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [F.], seq 74, ack 854, win 512, options [nop,nop,TS val 4219824582 ecr 1442703109], length 0
13:23:24.788896 IP aa4e7f274852.80 > 172.17.0.1.61002: Flags [F.], seq 854, ack 75, win 509, options [nop,nop,TS val 1442703119 ecr 4219824582], length 0
13:23:24.789485 IP 172.17.0.1.61002 > aa4e7f274852.80: Flags [.], ack 855, win 512, options [nop,nop,TS val 4219824583 ecr 1442703119], length 0

这里的输出基本与wireshark输出类似,需要读者熟悉TCP基本知识。

在实际使用中,我们不会直接监听很大范围,那样的话输出就太多了,所以我们一般会使用命令参数来过滤。下面我们来看下那些比较常用的参数

3.tcpdump命令参数

3.1 tcpdump -i 指定网络接口

通过ifconfig命令可以看到当前服务器的网络接口,我们可以指定监听某个网络接口的信息,如下

# 查看网络接口
root@7bc18553126f:/tmp# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 5708257  bytes 34583301223 (34.5 GB)
        RX errors 0  dropped 3025  overruns 0  frame 0
        TX packets 6428544  bytes 352862121 (352.8 MB)
        TX errors 0  dropped 2759 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
# 抓包eth0接口网络包
root@7bc18553126f:/tmp# tcpdump -i eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

但是一般不会这么直接做,因为输出实在太多

3.2 tcpdump -c count 指定打印条数

在接收到count条报文后,tcpdump命令就停止抓包,否则该命令会一直抓包下去

3.3 tcpdum -vv 展示详细信息

默认是简略版的,我们来看下详细的有啥不同(同样还是监听nginx80端口报文)

root@aa4e7f274852:/tmp# tcpdump port 80 -vv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:36:45.949940 IP (tos 0x0, ttl 64, id 52376, offset 0, flags [DF], proto TCP (6), length 60)
    172.17.0.1.61004 > aa4e7f274852.80: Flags [S], cksum 0x5857 (incorrect -> 0x0962), seq 964764998, win 65495, options [mss 65495,sackOK,TS val 4220625795 ecr 0,nop,wscale 7], length 0
13:36:45.950123 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    aa4e7f274852.80 > 172.17.0.1.61004: Flags [S.], cksum 0x5857 (incorrect -> 0x307a), seq 1828122747, ack 964764999, win 65160, options [mss 1460,sackOK,TS val 1443504333 ecr 4220625795,nop,wscale 7], length 0
13:36:45.950248 IP (tos 0x0, ttl 64, id 52377, offset 0, flags [DF], proto TCP (6), length 52)
    172.17.0.1.61004 > aa4e7f274852.80: Flags [.], cksum 0x584f (incorrect -> 0x5bce), seq 1, ack 1, win 512, options [nop,nop,TS val 4220625796 ecr 1443504333], length 0
13:36:45.950735 IP (tos 0x0, ttl 64, id 52378, offset 0, flags [DF], proto TCP (6), length 125)
    172.17.0.1.61004 > aa4e7f274852.80: Flags [P.], cksum 0x5898 (incorrect -> 0x05e5), seq 1:74, ack 1, win 512, options [nop,nop,TS val 4220625796 ecr 1443504333], length 73: HTTP, length: 73
	GET / HTTP/1.1
	Host: localhost
	User-Agent: curl/7.77.0
	Accept: */*
    ...

请求的明细信息都展示出来(这里HTTP头信息也展示出来了)

3.4 过滤器

tcpdump最强大之处就是过滤器功能,通过过滤器可以快速找到自己需要的报文

过滤器语法可以参考命令 (man pcap-filter):

root@7bc18553126f:/tmp# man pcap-filter
NAME
       pcap-filter - packet filter syntax

DESCRIPTION
       pcap_compile() is used to compile a string into a filter program.  The resulting filter program can then be applied to some stream of packets to determine
       which packets will be supplied to pcap_loop(), pcap_dispatch(), pcap_next(), or pcap_next_ex().

       The filter expression consists of one or more primitives.  Primitives usually consist of an id (name or number) preceded by one or more qualifiers.  There
       are three different kinds of qualifier:

       type   type qualifiers say what kind of thing the id name or number refers to.  Possible types are host, net , port and portrange.  E.g., `host foo', `net
              128.3', `port 20', `portrange 6000-6008'.  If there is no type qualifier, host is assumed.

       dir    dir qualifiers specify a particular transfer direction to and/or from id.  Possible directions are src, dst, src or  dst,  src  and  dst,  ra,  ta,
              addr1,  addr2,  addr3,  and  addr4.   E.g.,  `src  foo',  `dst net 128.3', `src or dst port ftp-data'.  If there is no dir qualifier, src or dst is
              assumed.  The ra, ta, addr1, addr2, addr3, and addr4 qualifiers are only valid for IEEE 802.11 Wireless LAN link layers.   For  some  link  layers,
              such as SLIP and the ``cooked'' Linux capture mode used for the ``any'' device and for some other device types, the inbound and outbound qualifiers
              can be used to specify a desired direction.

       proto  proto qualifiers restrict the match to a particular protocol.  Possible protos are: ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp and udp.
              E.g.,  `ether  src  foo', `arp net 128.3', `tcp port 21', `udp portrange 7000-7009', `wlan addr2 0:2:3:4:5:6'.  If there is no proto qualifier, all
              protocols consistent with the type are assumed.  E.g., `src foo' means `(ip or arp or rarp) src foo' (except the latter is not legal syntax),  `net
              bar' means `(ip or arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'.

根据该内容可以看出,tcpdump的过滤器主要分为三类:type 、dir 、proto

3.4.1 type

type主要有四种类型:host 、port 、 net、portrange

root@aa4e7f274852:/tmp# tcpdump host 172.17.0.1
root@aa4e7f274852:/tmp# tcpdump port 80
root@aa4e7f274852:/tmp# tcpdump net 172.17.0.0/24
root@aa4e7f274852:/tmp# tcpdump host 172.17.0.1 and port 80

3.4.2 dir

dir主要有源地址(src)和目标地址(dst)

# 只拦截src为172.17.0.1的报文
root@aa4e7f274852:/tmp# tcpdump src 172.17.0.1
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
14:13:36.699230 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [S], seq 4028567681, win 65495, options [mss 65495,sackOK,TS val 4222203183 ecr 0,nop,wscale 7], length 0
14:13:36.699476 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [.], ack 4235944116, win 512, options [nop,nop,TS val 4222203184 ecr 1445081721], length 0
14:13:36.700874 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [P.], seq 0:73, ack 1, win 512, options [nop,nop,TS val 4222203185 ecr 1445081721], length 73: HTTP: GET / HTTP/1.1
14:13:36.701364 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [.], ack 239, win 511, options [nop,nop,TS val 4222203186 ecr 1445081723], length 0
14:13:36.701471 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [.], ack 854, win 507, options [nop,nop,TS val 4222203186 ecr 1445081723], length 0
14:13:36.706212 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [F.], seq 73, ack 854, win 512, options [nop,nop,TS val 4222203191 ecr 1445081723], length 0
14:13:36.706519 IP 172.17.0.1.61012 > aa4e7f274852.80: Flags [.], ack 855, win 512, options [nop,nop,TS val 4222203191 ecr 1445081728], length 0
...

# 只拦截dst目标为172.17.0.1的报文
root@aa4e7f274852:/tmp# tcpdump dst 172.17.0.1
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
14:13:56.729678 IP aa4e7f274852.80 > 172.17.0.1.61014: Flags [S.], seq 3849319136, ack 1537888095, win 65160, options [mss 1460,sackOK,TS val 1445101751 ecr 4222223214,nop,wscale 7], length 0
14:13:56.730927 IP aa4e7f274852.80 > 172.17.0.1.61014: Flags [.], ack 74, win 509, options [nop,nop,TS val 1445101752 ecr 4222223215], length 0
14:13:56.731152 IP aa4e7f274852.80 > 172.17.0.1.61014: Flags [P.], seq 1:239, ack 74, win 509, options [nop,nop,TS val 1445101752 ecr 4222223215], length 238: HTTP: HTTP/1.1 200 OK
14:13:56.731208 IP aa4e7f274852.80 > 172.17.0.1.61014: Flags [P.], seq 239:854, ack 74, win 509, options [nop,nop,TS val 1445101752 ecr 4222223215], length 615: HTTP
14:13:56.744499 IP aa4e7f274852.80 > 172.17.0.1.61014: Flags [F.], seq 854, ack 75, win 509, options [nop,nop,TS val 1445101766 ecr 4222223228], length 0
...

3.4.3 proto协议

协议主要有:ip、arp、rarp、tcp、udp、icmp等

同样,这个单独使用的场景还是比较少的

3.5 条件组合

tcpdump支持通过条件组合的方式来过滤网络包。

主要包括:与 (and 或 &&)、或(or 或 ||)、非(not 或 !)

# 过滤host=172.17.0.1 并且 port=80 的包
root@aa4e7f274852:/tmp# tcpdump host 172.17.0.1 and port 80 

# 过滤host=172.17.0.1 并且 port!=80 的包
root@aa4e7f274852:/tmp# tcpdump host 172.17.0.1 and  port not 8080