抓包工具
抓包工具
在排查网络问题的时候,我们最常见的需求就是判断从一个机器到另一个机器能否正常通信,这个时候,我们就需要一个工具来帮助我们判断机器是否发出了数据包,发出的网络包发经过了哪些机器,这些机器有没有正常转发,在哪一步中断了,确定了在哪中断的,我们就能对症下药解决问题。
目前市面上有很多好用的抓包工具,比如
-
Wireshark:适用于 Windows/macOS/Linux,非常好用,极力推荐。tshark是 Wireshark 工具的命令行版本,一般在 Linux 环境下,我们都是用 tshark。
-
Network Monitor,当前(2023 年 12 月 9 日)已不再继续更新
-
TCPDUMP:Linux 中使用最广泛的抓包工具,tcpdump 是一个通用抓包工具,一般用它来抓各种协议的网络包数据,然后再使用 wireshark 分析。
这里,我们要将抓包工具和包分析工具区分开,抓包工具和包分析工具都可以获取并输出整个数据包,不过抓包工具主要分析包头,包分析工具主要分析包体,如果问题发生在链路层,则需要抓包工具判断包是否成功发送,是否成功接收。如果问题发生在网络层,也就是 TCP/IP 层面,则需要 TCP 报文的捕获和分析工具。比较常见的网络层问题包括 SSL 握手失败和 TCP 链接中断、重发等。
-
ngrep,jpr5/ngrep,ngrep 类似于网络层的 grep。它是一个基于pcap的工具,允许您指定扩展的正则表达式或十六进制表达式来匹配数据包的包体来过滤请求。它将抓到的包数据以文本形式直接显示出来,非常适用于包数据包含文本的抓包分析 (如 HTTP、MySQL)。
像ngrep、tcpdump和tshark -f使用的都是 pcap-filter 语法,用于抓取网络包时,对抓到的网络包进行过滤,pcap-filter 官网文档,而 Wireshark 界面上的显示过滤器,以及tshark -Y使用的都是 wireshark-filter 语法,用于对已抓取的数据包进行进一步过滤分析,具体请参考DisplayFilters。
tcpdump
tcpdump 采用命令行方式对本机的网络接口(一般是网卡、交换机)的收到和发送的数据包进行筛选,然后输出到前台,其原理主要是通过对数据包的 header 部分进行分析,然后过滤(tcpdump 也可以输出数据包的包体,但是一般不这样做),tcpdump 的强大主要体现在强大的筛选命令表达式上。这也是我们学习的重点。不带任何选项的 tcpdump,默认会抓取第一个网络接口,且 tcpdump 命令会一直输出日志,只有将 tcpdump 进程终止才会停止抓包。
命令格式:
|
|
具体参数如下,以下 option 不全,完整的 option 列表,请看在安装了 tcpdump 的主机中通过man tcpdump查看,或者直接查看官方文档:
|
|
常用配置项:
-
-c:指定要抓取的包数量。注意,是最终要获取这么多个包。例如,指定-c 10将获取 10 个包,但可能已经处理了 100 个包,只不过只有 10 个包是满足条件的包。 -
-i interface:指定 tcpdump 需要监听的接口。若未指定该选项,将从系统接口列表中搜寻编号最小的已配置好的接口 (不包括 loopback 接口,要抓取 loopback 接口使用tcpdump -i lo),一旦找到第一个符合条件的接口,搜寻马上结束,并开始按照搜寻到的接口开始抓包。可以使用any关键字表示所有网络接口。接口,指的就是网卡,这个在《用电信号传输 TCP/IP 数据 —— 探索协议栈和网卡》的
生成包含接收方 IP 地址的 IP 头部小节 提到过 -
-n:对地址以数字方式显式,否则显式为主机名,也就是说-n选项不做主机名解析。 -
-nn:除了-n的作用外,还把端口显示为数值,否则显示端口服务名。 -
-N:不打印出 host 的域名部分。例如 tcpdump 将会打印nic而不是nic.ddn.mil。 -
-P:指定要抓取的包是流入还是流出的包。可以给定的值为in、out和inout,默认为inout。 -
-s len:设置 tcpdump 的数据包抓取长度为 len,如果不设置默认将会是 65535 字节。对于要抓取的数据包较大时,长度设置不够可能会产生包截断,若出现包截断,输出行中会出现[|proto]的标志 (proto 实际会显示为协议名)。但是抓取 len 越长,包的处理时间越长,并且会减少 tcpdump 可缓存的数据包的数量,从而会导致数据包的丢失,所以在能抓取我们想要的包的前提下,抓取长度越小越好。
输出配置选项:
-
-e:输出的每行中都将包括数据链路层头部信息,例如源 MAC 和目标 MAC。 -
-q:快速打印输出。即打印很少的协议相关信息,从而输出行都比较简短。 -
-X:输出包的头部数据,会以 16 进制和 ASCII 两种方式同时输出。 -
-XX:输出包的头部数据,会以 16 进制和 ASCII 两种方式同时输出,更详细。 -
-v:当分析和打印的时候,产生详细的输出。 -
-vv:产生比-v更详细的输出。 -
-vvv:产生比-vv更详细的输出。
其他功能性选项:
-
-D:列出可用于抓包的接口。将会列出接口的数值编号和接口名,它们都可以用于-i后。 -
-F:从文件中读取抓包的表达式。若使用该选项,则命令行中给定的其他表达式都将失效。 -
-w:将抓包数据输出到文件中而不是标准输出。可以同时配合-G time选项使得输出文件每 time 秒就自动切换到另一个文件。可通过-r选项载入这些文件以进行分析和打印。这对于需要答应网络接口日志的场景很有用 -
-r:从给定的数据包文件中读取数据。使用-表示从标准输入中读取。
常用选项也就:
tcpdump -D和tcpdump -i int -nn -e -XX -vvv
筛选命令表达式
表达式用于筛选输出哪些类型的数据包,如果没有给定表达式,所有的数据包都将输出,否则只输出表达式为 true 的包。在表达式中出现的 shell 元字符建议使用单引号包围。
pcap_compile(3PCAP) 用于将字符串编译成过滤器程序。由此产生的过滤器程序可以应用于一些数据包流,以确定哪些数据包将被提供给pcap_loop(3PCAP), pcap_dispatch(3PCAP), pcap_next(3PCAP), 或者 pcap_next_ex(3PCAP)。
过滤器表达式由一个或多个单元组成。单元通常由 id(名称或数字) 和 id 前的一个或多个限定词组成。一个单元的表达式的格式为
|
|
三种不同的限定词:
-
type
type 限定词表示 id(名称或数字)指的是什么类型的东西。可能的类型有 host(指定域名或者主机)、net(指定网段)、port 和 portrange。例如,
host foo,net 128.3,port 20(port 是类型,20 就是 id),portrange 6000-6008。如果没有类型限定词,则默认为 host。net 可以是来自 networks 数据库 (
/etc/networks等) 的名称,也可以是网络号(其实就是网段信息)。IPv4 网络号可以写成点四元 (dotted quad,例如192.168.1.0)、点三元 (dotted triple,例如192.168.1)、点对 (dotted pair,例如172.16) 或单个数字 (例如 10),与之对应的,点四元的子网掩码是255.255.255.255(这意味着它直接是一个主机匹配),点三元的子网掩码是255.255.255.0,点对的子网掩码是255.255.0.0,单个数字的网掩码是255.0.0.0。IPv6 网络号必须完整地写出来;子网掩码是ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,所以 IPv6 的网络匹配实际上总是完整的主机 IP 匹配,而网络匹配需要一个子网络掩码长度。IP 地址由网络号和主机号构成,关于网络号的解释,请看《浏览器生成消息 —— 探索浏览器内部》的
IP 地址的基本知识小节 -
dir
dir 限定词指定到 id 和或从 id 传输的特定方向。可能的方向有 src、dst、src or dst、src and dst、ra、ta、addr1、addr2、addr3 和 addr4。举个例子:
src foo表示源主机为 foo 的数据包,dst net 128.3表示目标网络为128.3的数据包,src or dst port ftp-data表示源或目的端口为 22 的数据包,如果没有 dir 限定词,则默认为src or dst。注意:ra、ta、addr1、addr2、addr3 和 addr4 限定词只对
IEEE 802.11无线局域网链路层有效。 -
proto
proto 限定词将匹配限制为特定的协议。可能的协议有:ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, sctp, tcp 和 udp。举个例子:
ether src foo,arp net 128.3,tcp port 21,udp portrange 7000-7009,wlan addr2 0:2:3:4:5:6,如果没有 proto 限定词,则假定与 type 一致的所有协议都符合要求。举个例子,src foo的意思是(ip6 or IP or arp or rarp) src foo、net bar的意思是(IP or arp or rarp) net bar和port 53的意思是(tcp or udp or sctp) port 53。请注意,这些示例使用的是无效语法来说明原理。
除此之外,还有一些特殊的单元关键字不遵循以上描述的格式:gateway、broadcast、less、greater 和算术表达式。所有的合法的单元都可以在官网文档的Allowable primitives小节中找到。
更复杂的过滤器表达式是通过使用 and, or 和 not(或等价的:&&,||和!来组合单元。例如,host foo and not port ftp and not port ftp-data,表示筛选的数据包要满足"主机为 foo 且端口不是 ftp(端口 21) 和 ftp-data(端口 20) 的包"。为了节省输入,可以省略相同的限定词列表。例如,tcp dst port ftp or ftp-data or domain与tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain意思完全相同,表示包的协议为 tcp 且目的端口为 ftp 或 ftp-data 或 domain(端口 53)。
常用端口和名字的对应关系可在 Linux 系统中的
/etc/service文件中找到
使用括号()可以改变表达式的优先级,但需要注意的是括号会被 shell 解释,所以应该使用反斜线\转义,在需要的时候,还需要将整个表达式包围在引号中。
举个简单的例子:
host foo, host 127.0.0.1 :针对单个主机来进行过滤
net 192.168 :针对某个网段的包来进行过滤;
src host 127.0.0.1,dst net 192.168:同时加上来源 (src) 或目标 (dst) 限制
tcp port 21:还可以针对通讯协定侦测,如 tcp, udp, arp, ether 等
host helios and \( hot or ace \):打印helios<--> hot或helios<-->ace之间通信的数据包,因为没有指定 dir,因此默认是src or dst,因此直接写 host 会会默认配置入和出两个方向的包。
简单实践
打印 helios<–>hot 或 helios<–>ace 之间通信的数据包,因为没有指定 dir,因此默认是src or dst,因此直接写 host 会会默认配置入和出两个方向的包。
|
|
打印 ace 与任何其他主机之间通信的 IP 数据包,但不包括与 helios 之间的数据包。
|
|
截获主机 hostname 发送的所有数据
|
|
监视所有发送到主机 hostname 的数据包
|
|
监视指定主机和端口的数据包
|
|
对本机的 udp 123 端口进行监视
|
|
监视指定网络的数据包,如本机与 192.168 网段通信的数据包,"-c 10"表示只抓取 10 个包
|
|
打印所有通过网关 snup 的 ftp 数据包 (注意,表达式被单引号括起来了,这可以防止 shell 对其中的括号进行错误解析)
|
|
抓取 ping 包
|
|
如果明确要抓取主机为 192.168.100.70 对本机的 ping,则使用 and 操作符。
|
|
注意不能直接写 icmp src 192.168.100.70,因为 icmp 协议不支持直接应用 host 这个 type。
抓取到本机 22 端口包
|
|
解析包数据
|
|
Wireshark
GitHub 地址:GitHub - wireshark/wireshark:
下载地址:Download Wireshark
一个写的非常好的 Wireshark 教程:Wireshark 抓包,带你快速入门 - 农码一生 - 博客园
在 Windows 操作系统下,Wireshark 真的很好用。具体的初步上手的教程,请参考Wireshark 抓包,带你快速入门 - 农码一生 - 博客园,珠玉在前,我就不写重复的入门教程了。
观察包结构
选择一个封包列表中的一个包,然后查看网络包的内容,工具已经帮我们分好层了。
Frame 层表示的是物理层的数据帧概况,表示整个网络包,其他层从下往上,从 7 层网络协议的高层到低层:从应用层 (HTTP) 到传输层 (TCP) 再到网络层 (IP) 到链路层(MAC 协议,以太网),其对应的数据包也在对应的往前移。这跟我们《用电信号传输 TCP/IP 数据 —— 探索协议栈和网卡》中的包的基本知识小节学习到的网络包的结构是一致的。
右侧有两列数据,16 进制数字表示的是报文,字符列表示的是 16 进制的二进制表示的 ASCII 编码的字符。
过滤器
包列表内容刷新的到非常快,根本没法找到自己想要分析的内容。这里我们可以使用显示过滤器,只显示我们想要看的内容。
注意,Wireshark 有两种过滤语言:抓包过滤器(capture filters)和显示过滤器(display filters)
-
抓包过滤器用于抓包时的过滤,只抓取符合表达式的包,官方文档: Section 4.10, “Filtering while capturing”
就跟 tcpdump 的
筛选命令表达式小节中描述的语法是同一个 -
显示过滤器用于从已经抓到的包中过滤除哪些报文可以显示,官网文档: Section 6.4, “Building Display Filter Expressions”
显示过滤器语法:wireshark-filter(4),显示过滤器允许您将注意力集中在感兴趣的数据包上,同时隐藏当前不感兴趣的数据包。
显示过滤器
语法规则::wireshark-filter(4)
显示过滤器允许您将报文中指定协议中的指定字段与特定值进行比较,或者将报文中指定协议中的指定字段与另一个字段进行比较,并检查指定字段或协议的存在性。通过这个来筛选网络包。
比较符
|
|
查找符:
|
|
过滤器支持的方法
|
|
wireshark 将报文中每个协议的每一个字段都确定成了一个 key,字段对应的值都确定为了一个 value,我们编写的过滤规则,实际上就是对 value 进行过滤,value 也是有类型的
|
|
逻辑表达式
|
|
现在有一个问题,当我们写显示表达式的时候,我们需要知道 Wireshark 支不支持我们想要过滤的协议的字段,此时我们可以查看手册:Wireshark · Display Filter Reference: Index,以下是一些常见协议支持的字段的列表
-
IPv4:Wireshark · Display Filter Reference: Internet Protocol Version 4
-
TCP:Wireshark · Display Filter Reference: Transmission Control Protocol
-
HTTP:Wireshark · Display Filter Reference: Hypertext Transfer Protocol
-
HTTP2:Wireshark · Display Filter Reference: HyperText Transfer Protocol 2
Wireshark 还提供了一个新手帮助器帮助我们快速编写显示过滤器,菜单地址为:分析->显示过滤器表达式。
有了这个工具,我们就可以不用去查手册了。
常见的显示过滤器表达式:
针对 ip 的过滤
-
对源地址进行过滤:
ip.src == 192.168.0.1 -
对目的地址进行过滤:
ip.dst == 192.168.0.1 -
对源地址或者目的地址进行过滤:
ip.addr == 192.168.0.1 -
如果想排除以上的数据包,只需要将其用括号囊括,然后使用 “!” 即可:
!(ip.addr == 192.168.0.1)
针对协议的过滤
-
获某种协议的数据包,表达式很简单仅仅需要把协议的名字输入即可:
http注意:是否区分大小写?答:区分,
只能为小写 -
捕获多种协议的数据包:
http or telnet -
排除某种协议的数据包:
not arp或者!tcp
针对端口的过滤(视传输协议而定)
-
捕获某一端口的数据包(以 tcp 协议为例):
tcp.port == 80 -
捕获多端口的数据包,可以使用 and 来连接,下面是捕获高于某端口的表达式(以 udp 协议为例):
udp.port >= 2048
针对长度和内容的过滤
-
针对长度的过虑(这里的长度指定的是数据段的长度):
udp.length < 20或者http.content_length <=30 -
针对 uri 内容的过滤:
http.request.uri matches "user"(请求的 uri 中包含“user”关键字的)注意:
matches后的关键字是不区分大小写的!http.request.uri contains "User"(请求的 uri 中包含“user”关键字的)注意:
contains后的关键字是区分大小写的!
针对 http 请求的一些过滤实例。
-
过滤出请求地址中包含“user”的请求,不包括域名;
http.request.uri contains "User" -
精确过滤域名:
http.host==baidu.com -
模糊过滤域名:
http.host contains "baidu" -
过滤请求的 content_type 类型:
http.content_type =="text/html" -
过滤 http 请求方法:
http.request.method=="POST" -
过滤 tcp 端口:
tcp.port==80、http && tcp.port==80 or tcp.port==5566 -
过滤 http 响应状态码:
http.response.code==302 -
过滤含有指定 cookie 的 http 数据包:
http.cookie contains "userid"
我们还可以根据现有的包,直接将其协议字段值作为过滤条件:
抓包过滤器
显示过滤器是指捕获了所有经过网卡的封包,然后在显示的时候进行过滤显示。明显,如果流量过大会导致捕获的内容过多,筛选变得卡顿。所以,我们可以在抓包阶段的时候就过滤掉无用的流量。
udp、tcp前者表示只显示 tcp,后者表示只显示 udp。也可以!tcp,表示显示除了 tcp 之外的。还可以tcp or udp,表示显示 tcp 和 udp。host 192.168.1.110,表示只捕获 ip 地址为192.168.1.110的封包(这里的语法和显示过滤器不一样,请注意)dst port 80 or port 443、not port 53,表达端口的过滤(这里的语法和显示过滤器不一样,请注意)
抓包过滤器使用的是 libpcap 过滤语言,完整文档在 pcap-filter man page,我们可以发现,跟 tcpdump 的筛选命令表达式小节中描述的语法规则是一样的,本质上使用的是同一个模块pcap-filter。
常见抓包场景
我们选中包列表中的一个包,然后右键选择追踪流 -> TCP Stream 这样包列表中就会只展示这一个 TCP 连接中的数据包,方便我们只观察特定的连接,非常方便。
TCP 三次握手

TCP 四次挥手

HTTPS 抓包
配置的方式很简单,参考教程:如何使用 wireshark 抓取 HTTPS 数据包?-CSDN 博客
-
配置系统环境变量,变量名:
SSLKEYLOGFILE变量值为日志文件的路径,例如F:\sslkey.log,作用是告诉 chrome 输出 SSLKEY 的位置,而 wireshark 则可以使用此文件来解密 HTTPS 数据包。 -
打开 wireshark,在顶部菜单栏中选择编辑-> 首选项 -> Protocols -> TLS,在 (Pre)-Master-Secret log filename 中填入全局变量
SSLKEYLOGFILE的值,例如F:\sslkey.log。
然后我们抓包就可以直接看到 HTTP 请求的内容了。
tcpdump 配合 Wireshark
看看tcpdump抓包结合wireshark进行分析_tcpdump+wireshark-CSDN博客的tcpdump抓包与wireshark分析小节
小虾米