tcpdump 详解

通俗的说,tcpdump 是一个抓包工具,用于抓取互联网上传输的数据包。形象的说,tcpdump 是国家海关,驻扎在出入境的咽喉要道,凡是要入境和出境的集装箱,海关人员总要打开箱子,看看里面都装了点啥。学术的说,tcpdump 是一种嗅探器(sniffer),利用以太网的特性,通过将网卡适配器(NIC)置于混杂模式(promiscuous)来获取传输在网络中的信息包。

tcpdump 详解

tcpdump 是基于 libpcap 开发的命令行抓包工具,tcpdump 还可以将抓的包保存为 .pcap 文件供日后分析;
而大家比较熟悉的 Wireshark 也是基于 libpcap 的,Wireshark 的出色不在于抓包,而是在于数据包分析;
因此我们完全可以使用 tcpdump 抓包(使用 -w 参数保存为文件),然后使用 Wireshark 进行数据包分析。

安装 tcpdump
yum -y install tcpdump(CentOS)、pacman -S --need tcpdump(ArchLinux);

抓人生中的第一个包

从 –help 信息中可以看到 libpcap 版本、openssl 版本、tcpdump 版本;
不过 tcpdump 的帮助信息中实在是看不出什么东西,只能去查看 man 文档;

tcpdump -i ens33 -nnvvv 'udp port 53'参数解释:

  • -i ens33,用来指定监听哪张网卡,我这里是 ens33;
  • -nn,不解析主机名或端口名称,而是以数字的形式显示;
  • -vvv,显示最详细的信息(还有-v-vv选项);
  • 'udp port 53',过滤模式,只有匹配成功的数据包才会被 tcpdump 处理;

启用 tcpdump 后,另起一个终端,使用dig @114.114.114.114 www.baidu.com解析 DNS;
从 tcpdump 打印的数据包信息中也可以看到 www.baidu.com 字样,因为 dns 是明文的;

最后,使用 CTRL + C 关闭 tcpdump,tcpdump 会输出抓包统计信息,意义如下:

  • 2 packets captured:表示有 2 个数据包被 tcpdump 处理了;
  • 2 packets received by filter:表示有 2 个数据包符合过滤条件;
  • 0 packets dropped by kernel:表示有 0 个数据包被内核丢弃了;

我们只需关心前两个,理想情况下,符合条件的数据包和被 tcpdump 处理的数据包数量应该是一样的;
有时会因为 tcpdump 处理不及时(或其他原因),导致captured数小于received by filter的数;

tcp 标志位

  • SYN,显示为S,同步标志位,用于建立会话连接,同步序列号;
  • ACK,显示为.,确认标志位,对已接收的数据包进行确认;
  • FIN,显示为F,完成标志位,表示我已经没有数据要发送了,即将关闭连接;
  • RESET,显示为R,重置标志位,用于连接复位、拒绝错误和非法的数据包;
  • PUSH,显示为P,推送标志位,表示该数据包被对方接收后应立即交给上层应用,而不在缓冲区排队;
  • URGENT,显示为U,紧急标志位,表示数据包的紧急指针域有效,用来保证连接不被阻断,并督促中间设备尽快处理;

命令行选项

  • -i INTERFACE,指定监听的网卡,如果要监听所有网卡,可以使用 any;
  • -D,打印当前可用于 tcpdump 监听的网卡;
  • -n,不解析主机名,而是显示 IP 地址;
  • -nn,不解析主机名或端口对应的服务名,而是显示 IP 地址和端口号;
  • -q,显示更少的协议信息;
  • -v,详细输出,如 TTL、总长度、IP 包选项、IP/ICMP 报头校验和、使用 -w 写入文件时每隔 10 秒显示捕获的数据包数量等;
  • -vv,更详细的输出,如打印 NFS 附加字段、完全解码 SMB 数据包等;
  • -vvv,更详细的输出,如打印 telnet SB … SE 选项、以 16 进制打印 telnet 选项等;
  • -t,不显示任何时间戳;
  • -tt,显示自 1970-01-01 00:00:00 UTC 起的秒数;
  • -ttt,显示两个数据包之间的时间差(微秒);
  • -tttt,显示年月日、时分秒;
  • -ttttt,显示相对第一个数据包的时间差(微秒);
  • -A,以 ASCII 显示数据包(不包括链路层头部信息);
  • -X,以 ASCII 和 Hex 显示数据包(不包括链路层头部信息);
  • -XX,以 ASCII 和 Hex 显示数据包(包括链路层头部信息);
  • -e,打印链路层头部(比如 MAC 地址信息);
  • -c N,只捕获 N 个数据包,捕获完成后自动退出;
  • -s SIZE,定义捕获的数据包大小(字节为单位);
  • -S,打印绝对序列号而不是相对序列号(TCP);
  • -l,打印时使用行缓冲,这在使用管道时非常方便;
  • -w,将捕获到的数据包保存至 pcap 文件中;
  • -r,从 pcap 文件中读取数据包并进行分析;

过滤表达式

  • type类型host主机、net地址段、port端口号、portrange端口范围,默认为host
    • host,可以为主机名,也可以为 IP 地址;
    • netnet 172.16.0.0 mask 255.240.0.0net 172.16.0.0/12
    • port,可以为端口号,也可以为对应的服务名(/etc/services);
    • portrange,使用减号表示一个端口范围portrange 100-200
  • dir方向src来源、dst目的、src or dst来源或目的、src and dst来源和目的,默认为src or dst
  • proto协议ipip6arprarptcpudp,默认为对应的所有可用协议;

注意顺序:最前面是 proto,然后是 dir、最后是 type;同时也可以使用andornot逻辑连接符;如:

  • host 1.2.3.4 and not port 80 and not port 8080
  • tcp dst port 20 or 21 or 22等价于tcp dst port 20 or tcp dst port 21 or tcp dst port 22
  • 也可以使用大家熟知的&&||!,不过需要使用单引号或双引号扩起来,防止 shell 误解析;
  • 另外需要注意它们的优先级(从高到低):notandor,如果对它们不清楚,请使用()括起来。

更多 tcpdump 参数请查阅man tcpdump帮助文档;
更多 expression 语法请查阅man pcap-filter帮助文档。

tcpdump 分析

以最简单的 HTTP 为例,curl http://main.zfl9.com/,抓到了 11 个数据包:

TCP 三次握手

  1. Client 发送连接请求(SYN; seq: 1028579349),进入SYN-SENT状态;
  2. Server 响应连接请求(SYN+ACK; seq: 729495000; ack: 1028579350),进入SYN-RCVD状态;
  3. Client 确认 ack 值,进入ESTABLISHED状态,同时发送确认包(ACK; seq: 1028579350; ack: 729495001);Server 确认 ack 值,进入ESTABLISHED状态;

数据传输

TCP 四次挥手

  1. Client 发送 FIN 包,告诉 Server:我已经没有数据要发送了,我们关闭连接吧;进入FIN-WAIT-1状态;
  2. Server 收到 FIN 包后,知道 Client 要准备关闭连接了,于是回复 ACK 包,告诉 Client:我知道了;进入CLOSE-WAIT状态;Client 收到 ACK 包后,知道 Server 还有些事要准备(如将未发送的数据发送完),于是继续等待,进入FIN-WAIT-2状态;
  3. Server 准备完毕后,也发送 FIN 包,告诉 Client,我也没有数据要发送了,即将关闭连接了哦,进入LAST-ACK状态;
  4. Client 收到 FIN 包后,知道 Server 已经准备完毕并且即将关闭连接,于是发送 ACK 包,告诉 Server 已收到,进入TIME-WAIT状态;Server 收到 ACK 包后,进入CLOSED状态;Client 等待 2MSL 时间后,没有再次收到 Server 的 FIN 包,因此确认 Server 已经收到了 ACK 包并已关闭,于是 Client 也进入CLOSED状态;