tcpdump与Wireshark抓包分析

一、tcpdump与Wireshark介绍

在网络问题的调试中,tcpdump应该说是一个必不可少的工具,和大部分linux下优秀工具一样,它的特点就是简单而强大。它是基于Unix系统的命令行式的数据包嗅探工具,可以抓取流动在网卡上的数据包

默认情况下,tcpdump不会抓取本机内部通讯的报文。根据网络协议栈的规定,对于报文,即使是目的地是本机,也需要经过本机的网络协议层,所以本机通讯肯定是通过API进入了内核,并且完成了路由选择。【比如本机的TCP通信,也必须要socket通信的基本要素:src ip port dst ip port】

如果要使用tcpdump抓取其他主机MAC地址的数据包,必须开启网卡混杂模式,所谓混杂模式,用最简单的语言就是让网卡抓取任何经过它的数据包,不管这个数据包是不是发给它或者是它发出的。一般而言,Unix不会让普通用户设置混杂模式,因为这样可以看到别人的信息,比如telnet的用户名和密码,这样会引起一些安全上的问题,所以只有root用户可以开启混杂模式,开启混杂模式的命令是:ifconfig en0 promisc, en0是你要打开混杂模式的网卡

Linux抓包原理

Linux抓包是通过注册一种虚拟的底层网络协议来完成对网络报文(准确的说是网络设备)消息的处理权。当网卡接收到一个网络报文之后,它会遍历系统中所有已经注册的网络协议,例如以太网协议、x25协议处理模块来尝试进行报文的解析处理,这一点和一些文件系统的挂载相似,就是让系统中所有的已经注册的文件系统来进行尝试挂载,如果哪一个认为自己可以处理,那么就完成挂载。

当抓包模块把自己伪装成一个网络协议的时候,系统在收到报文的时候就会给这个伪协议一次机会,让它来对网卡收到的报文进行一次处理,此时该模块就会趁机对报文进行窥探,也就是把这个报文完完整整的复制一份,假装是自己接收到的报文,汇报给抓包模块。

Wireshark是一个网络协议检测工具,支持Windows平台、Unix平台、Mac平台,一般只在图形界面平台下使用Wireshark,如果是Linux的话,直接使用tcpdump了,因为一般而言Linux都自带的tcpdump,或者用tcpdump抓包以后用Wireshark打开分析。

在Mac平台下,Wireshark通过WinPcap进行抓包,封装的很好,使用起来很方便,可以很容易的制定抓包过滤器或者显示过滤器,具体简单使用下面会介绍。Wireshark是一个免费的工具,只要google一下就能很容易找到下载的地方。

所以,tcpdump是用来抓取数据非常方便,Wireshark则是用于分析抓取到的数据比较方便。

二、tcpdump使用

2.1 语法

1
2
3
4
5
6
7
8
9
tcpdump --help
Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ]
[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
[ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ]
[ -Q|-P in|out|inout ]
[ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ]
[ --immediate-mode ] [ -T type ] [ --version ] [ -V file ]
[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]
[ -Z user ] [ expression ]

2.1.1 类型的关键字

host(缺省类型): 指明一台主机,如:host 210.27.48.2

net: 指明一个网络地址,如:net 202.0.0.0

port: 指明端口号,如:port 23

2.1.2 确定方向的关键字

src: src 210.27.48.2, IP包源地址是210.27.48.2

dst: dst net 202.0.0.0, 目标网络地址是202.0.0.0

dst or src(缺省值)

dst and src

2.1.3 协议的关键字

缺省值是监听所有协议的信息包

fddi

ip

arp

rarp

tcp

udp

2.1.4 其他关键字

gateway

broadcast

less

greater

2.1.5 常用表达式

多条件时可以用括号,但是要用\转义

非 : ! or “not” (去掉双引号)

且 : && or “and”

或 : || or “or”

2.2 选项

-A:以ASCII编码打印每个报文(不包括链路层的头),这对分析网页来说很方便;
-a:将网络地址和广播地址转变成名字;
-c<数据包数目>:在收到指定的包的数目后,tcpdump就会停止;
-C:用于判断用 -w 选项将报文写入的文件的大小是否超过这个值,如果超过了就新建文件(文件名后缀是1、2、3依次增加);
-d:将匹配信息包的代码以人们能够理解的汇编格式给出;
-dd:将匹配信息包的代码以c语言程序段的格式给出;
-ddd:将匹配信息包的代码以十进制的形式给出;
-D:列出当前主机的所有网卡编号和名称,可以用于选项 -i;
-e:在输出行打印出数据链路层的头部信息;
-f:将外部的Internet地址以数字的形式打印出来;
-F<表达文件>:从指定的文件中读取表达式,忽略其它的表达式;
-i<网络界面>:监听主机的该网卡上的数据流,如果没有指定,就会使用最小网卡编号的网卡(在选项-D可知道,但是不包括环路接口),linux 2.2 内核及之后的版本支持 any 网卡,用于指代任意网卡;
-l:如果没有使用 -w 选项,就可以将报文打印到 标准输出终端(此时这是默认);
-n:显示ip,而不是主机名;
-N:不列出域名;
-O:不将数据包编码最佳化;
-p:不让网络界面进入混杂模式;
-q:快速输出,仅列出少数的传输协议信息;
-r<数据包文件>:从指定的文件中读取包(这些包一般通过-w选项产生);
-s<数据包大小>:指定抓包显示一行的宽度,-s0表示可按包长显示完整的包,经常和-A一起用,默认截取长度为60个字节,但一般ethernet MTU都是1500字节。所以,要抓取大于60字节的包时,使用默认参数就会导致包数据丢失;
-S:用绝对而非相对数值列出TCP关联数;
-t:在输出的每一行不打印时间戳;
-tt:在输出的每一行显示未经格式化的时间戳记;
-T<数据包类型>:将监听到的包直接解释为指定的类型的报文,常见的类型有rpc (远程过程调用)和snmp(简单网络管理协议);
-v:输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
-vv:输出详细的报文信息;
-x/-xx/-X/-XX:以十六进制显示包内容,几个选项只有细微的差别,详见man手册;
-w<数据包文件>:直接将包写入文件中,并不分析和打印出来;
expression:用于筛选的逻辑表达式;

2.3 tcpdump常用抓包示例

2.3.1 默认启动

1
tcpdump

普通情况下,直接启动tcpdump将监视第一个网络接口上所有流过的数据包。

2.3.2 获取指定网络接口的数据包

1
tcpdump -i en0

2.3.3 获取指定主机的数据包

打印所有进入或离开sundown的数据包.

1
tcpdump host sundown

也可以指定ip,例如截获所有210.27.48.1 的主机收到的和发出的所有的数据包

1
tcpdump host 210.27.48.1

打印helios 与 hot 或者与 ace 之间通信的数据包

1
tcpdump host helios and \( hot or ace \)

截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信

1
tcpdump host 210.27.48.1 and \ (210.27.48.2 or 210.27.48.3 \)

打印ace与任何其他主机之间通信的IP 数据包, 但不包括与helios之间的数据包.

1
tcpdump ip host ace and not helios

如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:

1
tcpdump ip host 210.27.48.1 and ! 210.27.48.2

截获主机hostname发送的所有数据

1
tcpdump -i eth0 src host hostname

监视所有送到主机hostname的数据包

1
tcpdump -i eth0 dst host hostname

2.3.4 获取指定主机和端口的数据包

如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令

1
tcpdump tcp port 23 and host 210.27.48.1

对本机的udp 123 端口进行监视 123 为ntp的服务端口

1
tcpdump udp port 123

抓取主机10.37.63.3发送的所有数据

1
tcpdump -i en0 src host 10.37.63.3

抓取主机10.37.63.3接收的所有数据

1
tcpdump -i en0 dst host 10.37.63.3

2.3.5 获取指定网络的数据包

打印本地主机与Berkeley网络上的主机之间的所有通信数据包(nt: ucb-ether, 此处可理解为’Berkeley网络’的网络地址,此表达式最原始的含义可表达为: 打印网络地址为ucb-ether的所有数据包)

1
tcpdump net ucb-ether

打印所有通过网关snup的ftp数据包(注意, 表达式被单引号括起来了, 这可以防止shell对其中的括号进行错误解析)

1
tcpdump 'gateway snup and (port ftp or ftp-data)'

打印所有源地址或目标地址是本地主机的IP数据包

(如果本地网络通过网关连到了另一网络, 则另一网络并不能算作本地网络.(nt: 此句翻译曲折,需补充).localnet 实际使用时要真正替换成本地网络的名字)

1
tcpdump ip and not net localnet

2.3.6 获取指定协议的数据包

打印TCP会话中的的开始和结束数据包, 并且数据包的源或目的不是本地网络上的主机.(nt: localnet, 实际使用时要真正替换成本地网络的名字))

1
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0 and not src and dst net localnet'

打印所有源或目的端口是80, 网络层协议为IPv4, 并且含有数据,而不是SYN,FIN以及ACK-only等不含数据的数据包.(ipv6的版本的表达式可做练习)

1
tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

(nt: 可理解为, ip[2:2]表示整个ip数据包的长度, (ip[0]&0xf)<<2)表示ip数据包包头的长度(ip[0]&0xf代表包中的IHL域, 而此域的单位为32bit, 要换算

成字节数需要乘以4, 即左移2. (tcp[12]&0xf0)>>4 表示tcp头的长度, 此域的单位也是32bit, 换算成比特数为 ((tcp[12]&0xf0) >> 4) << 2,
即 ((tcp[12]&0xf0)>>2). ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0 表示: 整个ip数据包的长度减去ip头的长度,再减去
tcp头的长度不为0, 这就意味着, ip数据包中确实是有数据.对于ipv6版本只需考虑ipv6头中的’Payload Length’ 与 ‘tcp头的长度’的差值, 并且其中表达方式’ip[]’需换成’ip6[]’.)

打印长度超过576字节, 并且网关地址是snup的IP数据包

1
tcpdump 'gateway snup and ip[2:2] > 576'

打印所有IP层广播或多播的数据包, 但不是物理以太网层的广播或多播数据报

1
tcpdump 'ether[0] & 1 = 0 and ip[16] >= 224'

打印除’echo request’或者’echo reply’类型以外的ICMP数据包( 比如,需要打印所有非ping 程序产生的数据包时可用到此表达式 .
(nt: ‘echo reuqest’ 与 ‘echo reply’ 这两种类型的ICMP数据包通常由ping程序产生))

1
tcpdump 'icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply'

抓取所有经过 en0,目的地址是 192.168.1.254 或 192.168.1.200 端口是 80 的 TCP 数据

1
tcpdump -i en0 '((tcp) and (port 80) and ((dst host 192.168.1.254) or (dst host 192.168.1.200)))'

抓取所有经过 en0,目标 MAC 地址是 00:01:02:03:04:05 的 ICMP 数据

1
tcpdump -i eth1 '((icmp) and ((ether dst host 00:01:02:03:04:05)))'

抓取所有经过 en0,目的网络是 192.168,但目的主机不是 192.168.1.200 的 TCP 数据

1
tcpdump -i en0 '((tcp) and ((dst net 192.168) and (not dst host 192.168.1.200)))'

只抓 SYN 包

1
tcpdump -i en0 'tcp[tcpflags] = tcp-syn'

抓 SYN, ACK

1
tcpdump -i en0 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0'

抓 DNS 请求数据

1
tcpdump -i en0 udp dst port 53

三、tcpdump抓取TCP包分析

TCP传输控制协议是面向连接的可靠的传输层协议,在进行数据传输之前,需要在传输数据的两端(客户端和服务器端)创建一个连接,这个连接由一对插口地址唯一标识,即是在IP报文首部的源IP地址、目的IP地址,以及TCP数据报首部的源端口地址和目的端口地址。TCP首部结构如下:

图片1

注意:通常情况下,一个正常的TCP连接,都会有三个阶段:1、TCP三次握手;2、数据传送;3、TCP四次挥手

其中在TCP连接和断开连接过程中的关键部分如下:

  1. 源端口号:即发送方的端口号,在TCP连接过程中,对于客户端,端口号往往由内核分配,无需进程指定;

  2. 目的端口号:即发送目的的端口号;

  3. 序号:即为发送的数据段首个字节的序号;

  4. 确认序号:在收到对方发来的数据报,发送确认时期待对方下一次发送的数据序号;

  5. SYN:同步序列编号,Synchronize Sequence Numbers;

  6. ACK:确认编号,Acknowledgement Number;

  7. FIN:结束标志,FINish;

3.1 TCP三次握手

三次握手的过程如下

图片2

  1. 由客户端向服务器端发起TCP连接请求。Client发送:同步序列编号SYN置为1,发送序号Seq为一个随机数,这里假设为X,确认序号ACK置为0;

  2. 服务器端接收到连接请求。Server响应:同步序列编号SYN置为1,并将确认序号ACK置为X+1,然后生成一个随机数Y作为发送序号Seq(因为所确认的数据报的确认序号未初始化);

  3. 客户端对接收到的确认进行确认。Client发送:将确认序号ACK置为Y+1,然后将发送序号Seq置为X+1(即为接收到的数据报的确认序号);

tcpdump抓TCP三次握手抓包分析

1
sudo tcpdump -n -S -i lo0 host 10.37.63.3 and tcp port 8080

接着再运行

1
curl http://10.37.63.3:8080/atbg/doc

控制台输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# TCP三次握手 start
16:00:13.486776 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [S], seq 1944916150, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 906474698 ecr 0,sackOK,eol], length 0
16:00:13.486850 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [S.], seq 1119565918, ack 1944916151, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 906474698 ecr 906474698,sackOK,eol], length 0
16:00:13.486860 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [.], ack 1119565919, win 12759, options [nop,nop,TS val 906474698 ecr 906474698], length 0
16:00:13.486868 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [.], ack 1944916151, win 12759, options [nop,nop,TS val 906474698 ecr 906474698], length 0
# TCP三次握手 end

# 传输数据 start
16:00:13.486923 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [P.], seq 1944916151:1944916238, ack 1119565919, win 12759, options [nop,nop,TS val 906474698 ecr 906474698], length 87: HTTP: GET /atbg/doc HTTP/1.1
16:00:13.486944 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [.], ack 1944916238, win 12756, options [nop,nop,TS val 906474698 ecr 906474698], length 0
16:00:13.489750 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [P.], seq 1119565919:1119571913, ack 1944916238, win 12756, options [nop,nop,TS val 906474701 ecr 906474698], length 5994: HTTP: HTTP/1.1 200 OK
16:00:13.489784 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [.], ack 1119571913, win 12572, options [nop,nop,TS val 906474701 ecr 906474701], length 0
# 传输数据 end

# TCP四次挥手 start
16:00:13.490836 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [F.], seq 1944916238, ack 1119571913, win 12572, options [nop,nop,TS val 906474702 ecr 906474701], length 0
16:00:13.490869 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [.], ack 1944916239, win 12756, options [nop,nop,TS val 906474702 ecr 906474702], length 0
16:00:13.490875 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [.], ack 1119571913, win 12572, options [nop,nop,TS val 906474702 ecr 906474702], length 0
16:00:13.491004 IP 10.37.63.3.8080 > 10.37.63.3.61725: Flags [F.], seq 1119571913, ack 1944916239, win 12756, options [nop,nop,TS val 906474702 ecr 906474702], length 0
16:00:13.491081 IP 10.37.63.3.61725 > 10.37.63.3.8080: Flags [.], ack 1119571914, win 12572, options [nop,nop,TS val 906474702 ecr 906474702], length 0
# TCP四次挥手 end

每一行中间都有这个包所携带的标志:

S=SYN,发起连接标志。

P=PUSH,传送数据标志。

F=FIN,关闭连接标志。

ack,表示确认包。

RST=RESET,异常关闭连接。

.,表示没有任何标志。

第1行:16:00:13.486776,从10.37.63.3(client)的临时端口61725向10.37.63.3(server)的8080监听端口发起连接,client初始包序号seq为1944916150,滑动窗口大小为65535字节(滑动窗口即tcp接收缓冲区的大小,用于tcp拥塞控制),mss大小为16344(即可接收的最大包长度,通常为MTU减40字节,IP头和TCP头各20字节)。【seq=1944916150,ack=0,syn=1】

第2行:16:00:13.486850,server响应连接,同时带上第一个包的ack信息,为client端的初始包序号seq加1,即1944916151,即server端下次等待接受这个包序号的包,用于tcp字节流的顺序控制。Server端的初始包序号seq为1119565918,mss也是16344。【seq=1119565918,ack=1944916151,syn=1】

第3行:15:46:13.084161,client再次发送确认连接,tcp连接三次握手完成,等待传输数据包。【ack=1119565919,seq=1944916151】

3.2 TCP四次挥手

连接双方在完成数据传输之后就需要断开连接。由于TCP连接是属于全双工的,即连接双方可以在一条TCP连接上互相传输数据,因此在断开时存在一个半关闭状态,即有有一方失去发送数据的能力,却还能接收数据。因此,断开连接需要分为四次。主要过程如下:

图片3

step1. 主机A向主机B发起断开连接请求,之后主机A进入FIN-WAIT-1状态;

step2. 主机B收到主机A的请求后,向主机A发回确认,然后进入CLOSE-WAIT状态;

step3. 主机A收到B的确认之后,进入FIN-WAIT-2状态,此时便是半关闭状态,即主机A失去发送能力,但是主机B却还能向A发送数据,并且A可以接收数据。此时主机B占主导位置了,如果需要继续关闭则需要主机B来操作了;

step4. 主机B向A发出断开连接请求,然后进入LAST-ACK状态;

step5. 主机A接收到请求后发送确认,进入TIME-WAIT状态,等待2MSL之后进入CLOSED状态,而主机B则在接受到确认后进入CLOSED状态;

为何主机A在发送了最后的确认后没有进入CLOSED状态,反而进入了一个等待2MSL的TIME-WAIT
主要作用有两个:

第一,确保主机A最后发送的确认能够到达主机B。如果处于LAST-ACK状态的主机B一直收不到来自主机A的确认,它会重传断开连接请求,然后主机A就可以有足够的时间去再次发送确认。但是这也只能尽最大力量来确保能够正常断开,如果主机A的确认总是在网络中滞留失效,从而超过了2MSL,最后也无法正常断开;

第二,如果主机A在发送了确认之后立即进入CLOSED状态。假设之后主机A再次向主机B发送一条连接请求,而这条连接请求比之前的确认报文更早地到达主机B,则会使得主机B以为这条连接请求是在旧的连接中A发出的报文,并不看成是一条新的连接请求了,即使得这个连接请求失效了,增加2MSL的时间可以使得这个失效的连接请求报文作废,这样才不影响下次新的连接请求中出现失效的连接请求。

为什么断开连接请求报文只有三个,而不是四个
因为在TCP连接过程中,确认的发送有一个延时(即经受延时的确认),一端在发送确认的时候将等待一段时间,如果自己在这段事件内也有数据要发送,就跟确认一起发送,如果没有,则确认单独发送。而我们的抓包实验中,由服务器端先断开连接,之后客户端在确认的延迟时间内,也有请求断开连接需要发送,于是就与上次确认一起发送,因此就只有三个数据报了。

四、Wireshark分析tcpdump抓包结果

4.1 启动8080端口,tcpdump抓包命令如下

1
tcpdump -i lo0 -s 0 -n -S host 10.37.63.3 and port 8080 -w ./Desktop/tcpdump_10.37.63.3_8080_20160525.cap

然后再执行curl

1
curl http://10.37.63.3:8080/atbg/doc

4.2 使用Wireshark打开tcpdump_10.37.63.3_8080_20160525.cap文件

图片4

No. 1-4 行:TCP三次握手环节;

No. 5-8 行:TCP传输数据环节;

No. 9-13 行:TCP四次挥手环节;

-------------本文结束感谢您的阅读-------------