帮助手册
tcpdump 是一个运行在命令行下的嗅探工具。它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包。tcpdump 是一个在BSD许可证下发布的自由软件。
tcpdump 适用于大多数的类Unix系统 操作系统:包括Linux、Solaris、BSD、Mac OS X、HP-UX和AIX 等等。在这些系统中,tcpdump 需要使用libpcap这个捕捉数据的库。其在Windows下的版本称为WinDump;它需要WinPcap驱动,相当于在Linux平台下的libpcap.
TCPDump可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。
-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.1 基于IP地址过滤:host
使用 host 就可以指定 host ip 进行过滤
$ tcpdump host 192.168.1.109数据包的 ip 可以再细分为源ip和目标ip两种
# 根据源ip进行过滤 $ tcpdump -i eth0 src 192.168.1.109 # 根据目标ip进行过滤 $ tcpdump -i eth0 dst 192.168.1.1092.3.2 基于网段进行过滤:net
若你的ip范围是一个网段,可以直接这样指定
$ tcpdump net 192.168.1.0/200网段同样可以再细分为源网段和目标网段
# 根据源网段进行过滤 $ tcpdump src net 192.168 # 根据目标网段进行过滤 $ tcpdump dst net 192.1682.3.3 基于端口进行过滤:port
使用 port 就可以指定特定端口进行过滤
$ tcpdump port 80端口同样可以再细分为源端口,目标端口
# 根据源端口进行过滤 $ tcpdump src port 80 # 根据目标端口进行过滤 $ tcpdump dst port 80如果你想要同时指定两个端口你可以这样写
$ tcpdump port 80 or port 443但也可以简写成这样
$ tcpdump port 80 or 443对于http和https的常见端口也可以写成这样
$ tcpdump tcp port http2.3.4 基于协议进行过滤:proto
常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等
若你只想查看 tcp的包,可以直接这样写
$ tcpdump tcp
2.3.5 基本IP协议的版本进行过滤
当你想查看 tcp 的包,你也许会这样子写
$ tcpdump tcp这样写不够准确,因为有IPv4和IPv6(数字 6 表示的是 tcp 在ip报文中的编号)
$ tcpdump 'ip proto tcp' # example-1 $ tcpdump ip proto 6 # example-2 $ tcpdump 'ip protochain tcp' # example-3 $ tcpdump ip protochain 6而如果是 IPv6 的 tcp 包 ,就这样写
$ tcpdump 'ip6 proto tcp' # example-1 $ tcpdump ip6 proto 6 # example-2 $ tcpdump 'ip6 protochain tcp' # example-3 $ tcpdump ip6 protochain 62.4.1 根据IP和port过滤
举个例子,我想需要抓一个来自108.69.207.22,发往任意主机的3359端口的包
$ tcpdump src 108.69.207.22 and dst port 3359
当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因为你需要使用引号将其包含。例子如下:
$ tcpdump 'src 108.69.207.22 and (dst port 3359 or 8888)'而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用判断符号:
等于,不等和值相等:=、!=、==2.4.2 根据其他关键字过滤
当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如
if:表示网卡接口名、proc:表示进程名pid:表示进程 idsvc:表示 service classdir:表示方向,in 和 outeproc:表示 effective process nameepid:表示 effective process ID
比如我现在要过滤来自进程名为 openvpn 发出的流经 eth0 网卡的数据包,或者不流经 eth0 的入方向数据包,可以这样子写
$ tcpdump '( if=eth0 and proc =openvpn ) || (if != eth0 and dir=in)'2.4.3 根据数据包大小过滤
若你想查看指定大小的数据包,也是可以的
$ tcpdump less 64 $ tcpdump greater 128 $ tcpdump <= 2562.4.4 根据 mac 地址进行过滤
如下:
$ tcpdump ether host [ehost] $ tcpdump ether dst [ehost] $ tcpdump ether src [ehost]2.4.5 根据数据内容进行过滤
获取到请求方式为GET的数据包
注:以下理解来自于google
$ 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 = 00110xf0:是 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)进行比对。
0x47 --> 71 --> G 0x45 --> 69 --> E 0x54 --> 84 --> T 0x20 --> 32 --> 空格
如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。
随便看一行的输出内容
11:08:03.779056 IP 192.168.1.109.41234 > 108.69.212.96.26778: Flags [.], ack 3751226663, win 5574, length 0第一列:时间,11时08分03秒779056第二列:网络协议为IP第三列:发送方的IP和port,IP为192.168.1.109,端口为41234第四列:箭头 >, 表示数据流向第五列:接收方的IP和port,IP为108.69.212.96,端口为26778第六列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [.] 表示没有Flag,更多标识符内容看下面介绍
TCP 报文 Flags,有以下几种:
[S] : SYN(开始连接)[P] : PSH(推送数据)[F] : FIN (结束连接)[R] : RST(重置连接)[.] : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK注:以下例子摘自:https://fuckcloudnative.io/posts/tcpdump-examples/
从 HTTP 请求头中提取 HTTP 用户代理:
$ tcpdump -nn -A -s1500 -l | grep "User-Agent:"通过 egrep 可以同时提取用户代理和主机名(或其他头文件):
$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'抓取 HTTP GET 请求包:
$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' # or $ tcpdump -vvAls0 | grep 'GET'可以抓取 HTTP POST 请求包:
$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' # or $ tcpdump -vvAls0 | grep 'POST'找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令:
$ 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 : 按照数值大小逆向排序DNS 的默认端口是 53,因此可以通过端口进行过滤
$ tcpdump -i any -s0 port 53从 HTTP POST 请求中提取密码和主机名:
$ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:"提取 HTTP 请求的主机名和路径:
$ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:"