Wireshark 是网络包分析工具。网络包分析工具的主要作用是尝试捕获网络包,并尝试显示包的尽可能详细的情况。本文初衷是为了以后解决实验室内网排查问题的,后变成了一篇实验和记录文章,我们将从 Wireshark 的安装开始记录,并进行一些常用的抓包分析工作。
Wireshark 快速入门(labex.io)是一个在线体验网站,包含了 Linux 系统上 Wireshark 的安装和使用。
具体的安装过程可以参考后面给出的网站,主要知道 Wireshark 如何开始抓包,以及抓包前和抓包后的过滤规则怎么设置就行。
进入 Nmap 官网安装 Namp 后,可在 Windows 上使用 netcat 工具。
在有些场景下,其实不存在服务端和客户端的区别,只是被动监听和主动发送的区别,这种情况我们也表述为服务端和客户端,在此澄清。
实验主要在 Windows 系统上进行,部分实验需要另外的计算机配合。
ARP(Address Resolution Protocol,地址解析协议)是一种用于将网络层地址(如 IP 地址)解析为数据链路层地址(如 MAC 地址)的协议。ARP 在局域网(LAN)中广泛使用,帮助设备在通信时确定目标设备的物理地址。
获取电脑网卡的 IP 和 MAC:
cmdipconfig /all
查看 ARP 缓存表:
cmdarp -a
开启抓包,并清空 ARP 缓存:
cmdarp -d *
此时可以看到 ARP 请求与响应,网关等 IP 的解析会比较快。稍后结束抓包,使用获取的 MAC arp&ð.addr==XX-XX-XX-XX-XX-XX
进行过滤,结果如下。
设备 | IP | MAC |
---|---|---|
本机 | xxx.xxx.xxx.104 | xx-xx-xx-xx-xx-ef |
网关 | xxx.xxx.xxx.254 | xx-xx-xx-xx-xx-1f |
设备1 | xxx.xxx.xxx.14 | xx-xx-xx-xx-xx-9c |
另一次抓取的结果如下:
设备 | IP | MAC |
---|---|---|
本机 | xxx.xxx.xxx.104 | xx-xx-xx-xx-xx-ef |
网关 | xxx.xxx.xxx.254 | xx-xx-xx-xx-xx-1f |
网盘 | xxx.xxx.xxx.51 | xx-xx-xx-xx-xx-90 |
设备1 | xxx.xxx.xxx.14 | xx-xx-xx-xx-xx-9c |
可以看到,ARP 过程主要是两步:
ARP 请求不是广播的吗?为啥第一次抓取中 xxx.xxx.xxx.104
和 xxx.xxx.xxx.14
在互相单播?
可能是触发了 ARP 缓存确认机制。
第二次抓取中,两个 IP 地址怎么解析到同一个 MAC 地址?
可能的原因如下:
虚拟化宿主机:在一台物理服务器上,你可能会运行多个虚拟机。每个虚拟机都有自己的 IP 地址,但它们共享同一个物理网卡。
NAT(网络地址转换):如果你的路由器或电脑开启了 NAT 功能,内网中所有设备的私有 IP 地址(比如 192.168.x.x)在对外通信时,都会被转换成路由器唯一的公网 IP 地址。
负载均衡:在某些服务器集群中,多个后端服务器可能共享一个“虚拟”的 IP 地址。所有发往这个 IP 的请求都会被分发到不同的服务器上,但从网络物理层面看,它们可能都通过同一个设备进行通信。
ARP 代理:一种网络技术,由路由器或三层设备代替目标主机响应 ARP 请求,使请求方误以为目标在同一局域网内,实际通信数据先发往代理设备,再由其进行转发,从而实现跨子网通信或兼容不支持网关配置的设备。
对我们的实验而言,应该是 ARP 代理的原因导致网关在响应不同 IP 的 ARP 请求。
第二次抓取中,一个 IP 地址怎么解析出两个 MAC 地址?
这个可能是我测试的内网出现了问题。正常情况下,不应出现这种情况。不过 AI 提供了几种情景:
TUN/TAP 代理时 ARP 会怎么样
TUN 不是相当于虚拟网卡吗?那 ARP 的时候 MAC 会不同吗?TUN 虚拟网卡是点对点设备,没有 MAC 也不会包装以太网帧。TAP 模式是以太网设备,有 MAC 地址。根据我的理解,TUN 和 TAP 本质上是虚拟的网卡,实际发送用的还是物理网卡,使用物理网卡的 MAC(具体情况需要后续做实验研究)。
ICMP(Internet Control Message Protocol,互联网控制消息协议)是 TCP/IP 协议族中的一种网络层协议,用于在 IP 网络中传递控制消息和错误报告。
ICMP 大致可以分为两大类:
常见的 ICMP 类型如下,示意图来自小林 coding
ping
获取 IP:
cmdipconfig
开启抓包,设置过滤 icmp && ip.addr == xxx.xxx.xxx.xxx
使用以下命令:
ping -n 4 8.8.8.8
命令行结果为:
正在 Ping 8.8.8.8 具有 32 字节的数据: 来自 8.8.8.8 的回复: 字节=32 时间=52ms TTL=106 来自 8.8.8.8 的回复: 字节=32 时间=51ms TTL=106 来自 8.8.8.8 的回复: 字节=32 时间=53ms TTL=106 来自 8.8.8.8 的回复: 字节=32 时间=54ms TTL=106 8.8.8.8 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 51ms,最长 = 54ms,平均 = 52ms
结束抓包:
在 ping 的过程中,我们向目标 IP 会发送 Echo request 包,而后目标 IP 会回复相应的 Echo reply。收包时的 TTL 为 106,结合常见的 TTL 初始值 128 可知,我们的 Echo reply 包中间经过了 22 跳。
tracert
计算中间跳数的时候是猜的初始值,有没有更准确的法子?有的,兄弟,有的。我们利用 tracert 不仅能知道中间跳数,连中间路由的 IP 都可以看到。
我们使用以下指令查看路由的信息:
tracert -d -h 30 -w 3000 blog.zerolacqua.top
其中,-d
表示不做域名解析(抓包更干净,IP 直接可见),-h 30
表示最大跳数为 30,-w 3000
表示超时为 3000 ms(可按需调),抓包结果如下:
可以看到 Echo request 的 TTL 是从 1 开始增加的。然后利用 TTL 衰减到 0 时,路由器会返回一个 TTL Exceeded(type=11)的包。我们可以通过这个包获取到路由的 IP 信息。通过不断增加 TTL 就可以获取到路由路径上的全部路由器信息啦。
等一下,不是说 tracert 探测过程中路由器返回 TTL Exceeded,我抓的包怎么是 Echo request 啊?
中间路由器发回的实际上是 ICMP Time Exceeded (type=11),而在抓包里看到的 Echo Request 是被嵌在这个 TTL Exceeded 报文里的原始探测包(tracert 发出的那个 ICMP Echo request)。路由器会把那个原始探测包作为 data 放在这个 TTL Exceeded 报文中。由 Wireshark 解析的时候,它会把嵌入的报文也解析出来。所以是你在查看 Wireshark 解析结果时点错了,看到的是嵌入的原始探测包。
UDP(User Datagram Protocol,用户数据报协议)是一种简单的、无连接的传输层协议,用于在网络中传输数据。与 TCP 不同,UDP 不提供可靠性、顺序性和流量控制,但它具有低延迟和高效的特点,适合对实时性要求较高的应用。
先开启抓包,再开放并监听 UDP 端口(服务端是 linux 系统自带 netcat):
bashnc -ul 12345
参数 -u
表示使用 UDP,-l
表示监听。
并在 Windows 上使用 netcat (没有可自行安装 Nmap 获取)作为客户端进行 UDP 连接,并发送消息:
bashncat -u xxx.xxx.xxx.237 12345
结束抓包,用 udp.port==12345
进行过滤,每个包对应一条消息,非常简单:
以下是客户端 xxx.xxx.xxx.40
发送的消息:
>ncat -u xxx.xxx.xxx.237 12345 hello world! hi! i'm acqua
以下是服务端 xxx.xxx.xxx.237
接收的消息
~# nc -ul 12345 hello world! hi! i'm acqua
使用 loopback 适配器
可以在本机的不同的终端上使用,一个作为服务端,一个作为客户端,ip 使用回环地址。但需要注意这时 Wireshark 需要使用 loopback 适配器,否则捕获不了网络包。
服务端没有打开接收端口会怎么样?
UDP 是无状态的且不保证交付,所以实际上服务端没有打开端口,网络包也能发出,也不会有如 TCP 那样的确认包。
服务端发送消息是确认目标 IP 的?
服务端能知道客户端 IP,是因为客户端发出的 UDP 报文本身就包含了源 IP 和源端口,内核会把这信息交给服务端应用程序。
服务端先发消息会怎么样?
服务端先发消息无法确定目标 IP 和端口,理论上无法发送。但神奇的是,客户端发送消息后,服务端会将之前那条无法发送的消息发送给客户端。这可能和 netcat 的实现有关,由 netcat 缓存着消息,待收到客户端 UDP 包确认目标 IP 和端口后一并发出。
TCP(Transmission Control Protocol,传输控制协议)是互联网协议套件中的核心协议之一,位于传输层。它提供了一种可靠的、面向连接的、基于字节流的数据传输服务。TCP 的主要特点是确保数据在传输过程中不丢失、不重复,并且按顺序到达。
TCP 包结构如下,示意图来自小林 coding
相关信息
由于 TCP 涉及内容较多,拥塞控制、粘包拆包等内容并未放出。
先开启抓包,再开放并监听 TCP 端口(服务端是 linux 系统自带 netcat):
bashnc -lk 12345
参数 -k
代表持续监听,避免连接断开后停止监听。
然后在 Windows 上使用 netcat 作为客户端进行 TCP 连接:
bashncat -p 12340 xxx.xxx.xxx.237 12345
参数 -p
表示以指定端口为 TCP 源端口。
互相发送消息后,结束抓包,用 tcp.port == 12345 || tcp.port==12340
进行过滤。然后右键数据包选择追踪流。
服务端没有在相应端口上进行监听时
三次握手、收发消息、超时断开
ACK=X+1
为对方 SEQ=X
值再加 1,意味着接收对方 SEQ=X
的包 ,同时意味着要求对方下一次传输 SEQ
应为 X+1
。hello from client
,长度为 19。服务端接收并返回 ACK=1+19=20
hello from server
,长度为 18。客户端接收并返回 ACK=1+18=19
四次挥手
SEQ
和 ACK
值是一样的。
SEQ
是不变的。SYN
或 FIN
,所以 ACK
是不变的。ACK
。超时重传
ACK
RST
包结束连接。ACK 与 SEQ 的关系?
由于背八股的时候基本只看三次握手和四次挥手,所以传输数据时是什么情况基本不了解,还以为和建立连接时一样是对方包的 SEQ
值加 1 就行。实则不然,ACK
号代表着期望下次发的字节序号,SEQ
表示包内第一个字节在字节流中的编号。上次发送的长度为 0 的话,这次的 SEQ
号就不变。当然如果涉及 SYN
或者 FIN
的话,也额外占一个序号。因此如上文解释的发送消息的情况,出现了 ACK=20
,是因为接收到了 19 个字节,所以期望下次发送字节编号为 1+19=20 的数据。
字符数一样,怎么一个长度 19 字节,一个长度 18?
Linux 和 Windows 系统的换行符不一样。0x0D = CR (Carriage Return, 回车),而 0x0A = LF (Line Feed, 换行)
系统 | 换行符组成 | 换行符十六进制码 |
---|---|---|
Windows / DOS | \r\n | 0D 0A |
Unix / Linux / macOS (现代) | \n | 0A |
老 macOS (pre-OSX) | \r | 0D |
主动断开连接之后想重新执行之前的命令,提示:通常每个套接字地址(协议/网络地址/端口)只允许使用一次
windows 上执行以下命令
cmdnetstat -aon|findstr "12340"
会显示
TCP xxx.xxx.xxx.40:12340 xxx.xxx.xxx.237:12345 TIME_WAIT 0
说明上一次的连接还处于 TIME_WAIT
状态,至于为什么要在 TIME_WAIT
状态等一段时间,相信大家都明白的吧。
若要在 Linux 上排查,可使用:
bashss -tulnp | grep 12340
服务端发送消息是确认目标 IP 的?
情况与前述 UDP 是一样的,未建立连接无从知晓目标 IP。
服务端先发消息会怎么样?
与前述 UDP 实验一样,应当是 netcat 的机制。
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一种用于自动分配 IP 地址和其他网络配置信息(如子网掩码、默认网关、DNS 服务器)的协议。DHCP 简化了网络管理,允许设备在加入网络时自动获取必要的配置,而无需手动设置。
DHCP 是一个工作在 UDP 上的应用层协议。一般的工作流程可以参见 小林 Coding 中给出的示意图,分为 Discover、Offer、Request、Ack 四个阶段。
先开始抓包,然后通过 Windows 命令行执行以下命令释放 IP 并重新请求分配 IP:
CMDipconfig /release ipconfig /renew
然后结束抓包,用 bootp 或 dhcp 进行过滤,得到以下包
第一个包是 Release,用于释放 IP,后续四个包是请求分配 IP 过程中收发的包。我们只讨论后面的四个包。后面的四个包就是对应 Discover、Offer、Request、Ack 的四个阶段。其中 xxx.xxx.xxx.254
是 DHCP 服务器的 IP,xxx.xxx.xxx.134
是本机 IP(释放和请求)。
具体的 DHCP 中字段的格式与含义在此不表,可以在后文的参考文章中看到。
Offer、ACK 包中都有目标 IP 了,但是 DHCP 明明还没完成呢!
如果是广播会使用 255.255.255.255
,但这里是单播使用的是客户端可能的 IP,实际处理不是依赖这里的 IP 获取 MAC 的,通知客户端的分配的 IP 是在 DHCP 的负载里的
不是说好了 Offer 和 ACK 是广播(255.255.255.255
)吗?怎么变成单播了(xxx.xxx.xxx.134
的内网 IP)?
在 Discover 包中的 dhcp.flags
字段会告诉 DHCP 服务器使用单播还是广播。广播的问题在于会影响到广播域里其它所有主机。当主机收到广播报文时,网卡会产生一次硬件中断 CPU,CPU 会暂停手头的工作来处理这个中断,处理完中断,CPU 继续工作。IP 协议栈取走数据,检查之后和自己无关,丢弃。会造成资源的浪费。
在我们 Release 掉 IP 之后,DHCP 服务器怎么找到我们的 MAC 的?
不是通过 ARP 协议获取的,而是 Discover 包的 dhcp.hw.mac_addr
字段,由客户端给出的。
为什么重新申请之后又拿到了同样的 IP?
在 Discover 包中的 dhcp.option.requested_ip_address
字段包含了上一次获取到的 IP,如果这个 IP 没有被指派出去,那么依然会重新分配给这台客户端。
租约问题
另外,在 Offer 包的 dhcp.option
中有 ip_address_lease_time
、dhcp.option.renewal_time_value
、dhcp.option.rebinding_time_value
分别代表了租约时长和两个租约续期的时间点。(查看 DHCP 发现租约只有 8 小时,这么抠门?)
DNS(Domain Name System,域名系统)是互联网中用于将域名(如 www.example.com )转换为 IP 地址(如 93.184.216.34)的分布式系统。目前 DNS 的设计采用的是分布式、层次数据库结构,DNS 是应用层协议,基于 UDP 协议之上,端口为 53 。
整个DNS报文格式主要分为 3 部分内容,即基础结构部分、问题部分、资源记录部分。
DNS 数据库中存储了多种类型的记录,常见的有:
由于代理软件可能会对 DNS 过程造成影响,因此抓包过程中需要关闭代理软件,降低分析难度。机器上设置的 IPv4 DNS 服务器为 8.8.8.8
(谷歌)和 223.5.5.5
(阿里); IPv6 DNS 服务器为 2001:4860:4860::8888
(谷歌)和 2400:3200::1
(阿里)。本机 IP 为 10.102.21.0
和 2001:250:4001:3011::3200:34
设置捕获过滤器 udp port 53
先开始抓包,然后执行命令发起 DNS 查询
nslookup example.com
命令行结果:
cmd> nslookup example.com 服务器: dns.google Address: 2001:4860:4860::8888 非权威应答: 名称: example.com Addresses: 2600:1406:bc00:53::b81e:94c8 2600:1406:5e00:6::17ce:bc1b 2600:1406:5e00:6::17ce:bc12 2600:1408:ec00:36::1736:7f31 2600:1406:bc00:53::b81e:94ce 2600:1408:ec00:36::1736:7f24 23.215.0.138 23.220.75.245 23.192.228.80 23.215.0.136 23.192.228.84 23.220.75.232
停止抓包,使用 dns.qry.name contains "example.com"
进行过滤。
可以看到是使用了谷歌的 IPv6 DNS 服务器,本机向谷歌 DNS 请求了三次。
0x0001
,是 PTR 查询,用于获取 IP 对应的主机名。可以看到谷歌 DNS 响应结果为 dns.google
0x0002
,查询域名的 A 记录(IPv4)。可以看到谷歌 DNS 响应了若干个 IPv4 结果,Anwser RRs 为 6,说明返回了 6 个结果。0x0003
,查询域名的 AAAA 记录(IPv6)。可以看到谷歌 DNS 响应了若干个 IPv6 结果,Anwser RRs 为 6,说明返回了 6 个结果。dns.google 是正经域名?
是的,.google
是顶级域名(没想到吧,博主以前也不知道)
那如果开着代理 DNS 过程会怎么变化?
代理软件一般会有自己的 DNS 规则,DoT 甚至是加密的,如果在实验过程中你是通过浏览器访问域名进行测试的,就可能抓不到想要的 DNS 的包。展开讲就太复杂了,想学习了解的可以参考:『Blog』Dive into Clash DNS
如果 DNS 服务器是无效的,会怎么样?
设置 IPv6 地址为 2001:db8::1
。DNS 请求被发送到了 2001:db8::1
,但没有响应,最后返回超时。当然其他情况下还可能出现返回 ICMP 包说明网络层传输失败。
SSH(Secure Shell,安全外壳协议)是一种用于安全远程登录和其他网络服务的加密协议。SSH 通过加密通信来保护数据在传输过程中的安全性,广泛应用于系统管理、文件传输和远程命令执行等场景。
设置捕获过滤器 tcp port 22
,开始抓包,然后发起 SSH 连接(使用了密钥登录,为方便查看主机名用的是 IP):
cmdssh acqua@xxx.xxx.xxx.29
结果如下:
可以看到,xxx.xxx.xxx.0 和 xxx.xxx.xxx.29 在传输数据前进行了 SSH 连接建立的过程:
中间人攻击问题
实际上,在以上步骤中,如果客户端是第一次连接到服务端,客户端会让用户对服务端公钥指纹进行确认。因为是存在风险的,这一步是可能存在中间人攻击。因为我们没有类似 HTTPS 那样的证书链保证公钥是属于服务端的,所以中间人完全可以冒充服务端与你进行连接。这种情况下,如果使用密码登录,你将会用中间人发给你的公钥加密传输你的密钥,然后你的密码就会被中间人拿到了(寄)。如果通过上传公钥进行登录,被中间人攻击的可能性将降低,但是同样存在风险,TOFU 环节确认服务端公钥指纹时要慎重!
HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的协议之一,用于在客户端(如浏览器)和服务器之间传输超文本(如网页)。HTTP 是万维网(WWW)的基础,支持网页浏览、文件下载、API 调用等应用场景。
HTTPS(HyperText Transfer Protocol Secure,安全超文本传输协议)是 HTTP 的安全版本,通过在 HTTP 和传输层之间加入 TLS/SSL 加密层,保护数据传输的安全性和完整性。HTTPS 广泛用于保护敏感信息(如登录凭证、支付信息)的传输。
cmdcurl --local-port 12340-12340 https://example.com/
安装 Wireshark
查看 ARP 包
查看 ICMP 包
查看 UDP 包
查看 TCP 包
查看 DHCP 包
查看 DNS 包
查看 SSH 包
查看 HTTPS 包
本文作者:Zerol Acqua
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!