1. tcpdump介绍

1.1 官方man手册

帮助手册

1.2 维基百科

tcpdump 是一个运行在命令行下的嗅探工具。它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包。tcpdump 是一个在BSD许可证下发布的自由软件。
tcpdump 适用于大多数的类Unix系统 操作系统:包括Linux、Solaris、BSD、Mac OS X、HP-UX和AIX 等等。在这些系统中,tcpdump 需要使用libpcap这个捕捉数据的库。其在Windows下的版本称为WinDump;它需要WinPcap驱动,相当于在Linux平台下的libpcap.

1.3 百度百科

TCPDump可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。

2. 参数详解

2.1 图示

lALPGpqNbM0WWbbNAgbNBDc_1079_518.png_720x720q90g.jpg

2.2 option 可选参数


  • -n:不把ip转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多
  • -nn:不把协议和端口号转化成名字,速度也会快很多。
  • -N:不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump 将会打印’nic’ 而不是 ‘nic.ddn.mil’.
  • -w :参数后接一个以 .pcap 后缀命令的文件名,就可以将 tcpdump 抓到的数据保存到文件中。
  • -v:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。
  • -vv:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过)
  • -vvv:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过)
  • -t:在每行的输出中不输出时间
  • -tt:在每行的输出中会输出时间戳
  • -ttt:输出每两行打印的时间间隔(以毫秒为单位)
  • -tttt:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观)
  • -i:指定要过滤的网卡接口,如果要查看所有网卡,可以 -i any
  • -Q:选择是入方向还是出方向的数据包,可选项有:in, out, inout
  • -l : 基于行的输出,便于你保存查看,或者交给其它工具分析
  • -q : 简洁地打印输出。即打印很少的协议相关信息, 从而输出行都比较简短.
  • -c : 捕获 count 个包 tcpdump 就退出
  • -s : tcpdump 默认只会截取前 96 字节的内容,要想截取所有的报文内容,可以使用 -s number, number 就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。
  • -C:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加.
  • -W:与-C选项一起使用,这会将创建的文件数量限制为指定的数量,并从头开始覆盖文件,从而创建“旋转”缓冲区。此外,它将使用足够的前导0命名文件,以支持最大数量的文件,从而使它们能够正确排序。

    2.3 过滤参数

    2.3.1 基于IP地址过滤:host

    使用 host 就可以指定 host ip 进行过滤
    1. $ tcpdump host 192.168.1.109
    数据包的 ip 可以再细分为源ip和目标ip两种
    1. # 根据源ip进行过滤
    2. $ tcpdump -i eth0 src 192.168.1.109
    3. # 根据目标ip进行过滤
    4. $ tcpdump -i eth0 dst 192.168.1.109

    2.3.2 基于网段进行过滤:net

    若你的ip范围是一个网段,可以直接这样指定
    1. $ tcpdump net 192.168.1.0/200
    网段同样可以再细分为源网段和目标网段
    1. # 根据源网段进行过滤
    2. $ tcpdump src net 192.168
    3. # 根据目标网段进行过滤
    4. $ tcpdump dst net 192.168

    2.3.3 基于端口进行过滤:port

    使用 port 就可以指定特定端口进行过滤
    1. $ tcpdump port 80
    端口同样可以再细分为源端口,目标端口
    1. # 根据源端口进行过滤
    2. $ tcpdump src port 80
    3. # 根据目标端口进行过滤
    4. $ tcpdump dst port 80
    如果你想要同时指定两个端口你可以这样写
    1. $ tcpdump port 80 or port 443
    但也可以简写成这样
    1. $ tcpdump port 80 or 443
    对于httphttps的常见端口也可以写成这样
    1. $ tcpdump tcp port http

    2.3.4 基于协议进行过滤:proto

    常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等
    若你只想查看 tcp的包,可以直接这样写
    1. $ tcpdump tcp

2.3.5 基本IP协议的版本进行过滤

当你想查看 tcp 的包,你也许会这样子写

  1. $ tcpdump tcp

这样写不够准确,因为有IPv4和IPv6(数字 6 表示的是 tcp 在ip报文中的编号)

  1. $ tcpdump 'ip proto tcp'
  2. # example-1
  3. $ tcpdump ip proto 6
  4. # example-2
  5. $ tcpdump 'ip protochain tcp'
  6. # example-3
  7. $ tcpdump ip protochain 6

而如果是 IPv6 的 tcp 包 ,就这样写

  1. $ tcpdump 'ip6 proto tcp'
  2. # example-1
  3. $ tcpdump ip6 proto 6
  4. # example-2
  5. $ tcpdump 'ip6 protochain tcp'
  6. # example-3
  7. $ tcpdump ip6 protochain 6

2.4 组合过滤

2.4.1 根据IP和port过滤

举个例子,我想需要抓一个来自108.79.108.89,发往任意主机的3359端口的包

  1. $ tcpdump src 108.79.108.89 and dst port 3359

当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因为你需要使用引号将其包含。例子如下:

  1. $ tcpdump 'src 108.79.108.89 and (dst port 3359 or 8888)'

而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用判断符号:

  • 等于,不等和值相等:=、!=、==

    2.4.2 根据其他关键字过滤

    当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如

  • if:表示网卡接口名、

  • proc:表示进程名
  • pid:表示进程 id
  • svc:表示 service class
  • dir:表示方向,in 和 out
  • eproc:表示 effective process name
  • epid:表示 effective process ID

比如我现在要过滤来自进程名为 openvpn 发出的流经 eth0 网卡的数据包,或者不流经 eth0 的入方向数据包,可以这样子写

  1. $ tcpdump '( if=eth0 and proc =openvpn ) || (if != eth0 and dir=in)'

2.4.3 根据数据包大小过滤

若你想查看指定大小的数据包,也是可以的

  1. $ tcpdump less 64
  2. $ tcpdump greater 128
  3. $ tcpdump <= 256

2.4.4 根据 mac 地址进行过滤

如下:

  1. $ tcpdump ether host [ehost]
  2. $ tcpdump ether dst [ehost]
  3. $ tcpdump ether src [ehost]

2.4.5 根据数据内容进行过滤

获取到请求方式为GET的数据包
注:以下理解来自于google

  1. $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]'
  • tcp[n]:表示 tcp 报文里 第 n 个字节
  • tcp[n:c]:表示 tcp 报文里从第n个字节开始取 c 个字节,tcp[12:1] 表示从报文的第12个字节(因为有第0个字节,所以这里的12其实表示的是13)开始算起取一个字节,也就是 8 个bit。查看 tcp 的报文首部结构,可以得知这 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 4个bit,也就是实际数据在整个报文首部中的偏移量。
  • &:是位运算里的 and 操作符,比如 0011 & 0010 = 0010
  • >>:是位运算里的右移操作,比如 0111 >> 2 = 0011
  • 0xf0:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。

分解完后,再慢慢合并起来看
1、tcp[12:1] & 0xf0 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 10110000,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。
2、tcp[12:1] & 0xf0) >> 2 tcp[12:1] & 0xf0) >> 2 这个表达式实际是 (tcp[12:1] & 0xf0) >> 4 ) << 2 的简写形式。所以要搞懂 tcp[12:1] & 0xf0) >> 2 只要理解了(tcp[12:1] & 0xf0) >> 4 ) << 2 就行了 。
从上一步我们算出了 tcp[12:1] & 0xf0 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 tcp[12:1] & 0xf0) >> 4,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 <<2,与前面的 >>4 结合起来一起算的话,最终的运算可以简化为 >>2
至此,我们终于得出了实际数据开始的位置是 tcp[12:1] & 0xf0) >> 2 (单位是字节)。
找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 GET 呢 还是 POST ,或者是其他的?
有了上面的经验,我们自然懂得使用 tcp[((tcp[12:1] & 0xf0) >> 2):4] 从数据开始的位置再取出四个字节,然后将结果与 GET (注意 GET最后还有个空格)的 16进制写法(也就是 0x47455420)进行比对。

  1. 0x47 --> 71 --> G
  2. 0x45 --> 69 --> E
  3. 0x54 --> 84 --> T
  4. 0x20 --> 32 --> 空格

如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。

3. 输出理解

随便看一行的输出内容

  1. 11:08:03.779056 IP 192.168.1.109.41234 > 108.69.110.98.54901: Flags [.], ack 3751226663, win 5574, length 0
  1. 第一列:时间,11时08分03秒779056
  2. 第二列:网络协议为IP
  3. 第三列:发送方的IP和port,IP为192.168.1.109,端口为41234(我的零终端连接的路由器,所以是192网段)
  4. 第四列:箭头 >, 表示数据流向
  5. 第五列:接收方的IP和port,IP为108.69.110.98,端口为54901
  6. 第六列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [.] 表示没有Flag,更多标识符内容看下面介绍

TCP 报文 Flags,有以下几种:

  • [S] : SYN(开始连接)
  • [P] : PSH(推送数据)
  • [F] : FIN (结束连接)
  • [R] : RST(重置连接)
  • [.] : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK

    4. 实例

    注:以下例子摘自:https://fuckcloudnative.io/posts/tcpdump-examples/

    4.1 提取 HTTP 的 User-Agent

    从 HTTP 请求头中提取 HTTP 用户代理:

    1. $ tcpdump -nn -A -s1500 -l | grep "User-Agent:"

    通过 egrep 可以同时提取用户代理和主机名(或其他头文件):

    1. $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'

    4.2 抓取 HTTP GET 和 POST 请求

    抓取 HTTP GET 请求包:

    1. $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'
    2. # or
    3. $ tcpdump -vvAls0 | grep 'GET'

    可以抓取 HTTP POST 请求包:

    1. $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'
    2. # or
    3. $ tcpdump -vvAls0 | grep 'POST'

    4.3 找出发包数最多的 IP

    找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令:

    1. $ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20
  • cut -f 1,2,3,4 -d ‘.’ : 以 . 为分隔符,打印出每行的前四列。即 IP 地址。

  • sort | uniq -c : 排序并计数
  • sort -nr : 按照数值大小逆向排序

    4.4 抓取 DNS 请求和响应

    DNS 的默认端口是 53,因此可以通过端口进行过滤
    1. $ tcpdump -i any -s0 port 53

    4.5 提取 HTTP POST 请求中的密码

    从 HTTP POST 请求中提取密码和主机名:
    1. $ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:"

    4.6 提取 HTTP 请求的 URL

    提取 HTTP 请求的主机名和路径:
    1. $ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:"