拼装数据包:如何使用Scapy发送L2-L7帧并读取

拼装数据包:如何使用Scapy发送L2-L7帧并读取

_

拼接数据包:使用 Scapy 发送和读取 L2-L7 帧

网络排错常常如同在黑暗中组装拼图——你正在拼凑数据碎片,以理解基础设施中发生的情况。这种复杂性在需要调试数据链路(第 2 层)、网络(第 3 层)或传输(第 4 层)层的问题时尤其明显,更不用说像应用层(第 7 层)这样的更高层了。在本深入探讨中,我们将探索如何使用 Scapy(一个强大的 Python 网络数据包操作库)在 OSI 模型的多个层上构建、发送和分析自定义帧。

实验目标:调试 L2 vs L3 vs L4

本实验的目标是获得在 OSI 模型低层进行数据包构建和分析的实践经验。我们将使用 VyOS(一个基于 OpenWrt 的轻量级操作系统),在 Oracle VM 上运行,并通过单个终端与其交互。此设置允许我们在不同层上操作数据包时实时观察数据包的行为。

设置环境

在深入数据包构建之前,让我们确保我们的环境已准备就绪。以下是如何在 Oracle VM 上安装 VyOS 并为实验做准备的方法:

在 Oracle VM 上安装 VyOS

  1. 下载 Oracle VM VirtualBox:如果你还没有安装,请从 官方网站 下载并安装 Oracle VM VirtualBox。

  2. 获取 VyOS ISO:从 VyOS GitHub 发布页面 下载 VyOS ISO。

  3. 创建新虚拟机:

  4. 打开 VirtualBox 并点击“新建”。
  5. 为你的虚拟机命名(例如,“VyOS Lab”),选择类型为“Linux”,版本为“其他 Linux (64 位)”。
  6. 分配内存(例如,1024 MB)。
  7. 创建虚拟硬盘(例如,20 GB),并选择“VDI”作为存储格式。

  8. 安装 VyOS:

  9. 启动虚拟机并选择下载的 VyOS ISO 作为启动磁盘。
  10. 按照屏幕上的说明完成安装。
  11. 安装完成后,关闭虚拟机并移除 ISO。

  12. 配置网络:

  13. 启动虚拟机并登录。
  14. 通过输入 configure 打开 VyOS 配置模式。
  15. 导航到网络配置: bash set interfaces ethernet eth0 address 192.168.56.100/24 set interfaces ethernet eth0 description 'Lab Interface' set routing protocol static route 192.168.56.0/24 next-hop 192.168.56.1 commit save exit
  16. 重启网络服务: bash service networking restart

  17. 连接到网络:

  18. 使用终端连接到虚拟机的网络接口。如果虚拟机已设置 SSH,你可以使用 ssh,或者通过 VirtualBox 控制台连接。

使用 tcpdump 进行初始数据包分析

在我们构建自己的数据包之前,让我们观察网络上的活动。我们将使用 tcpdump(一个命令行数据包分析器)来监听 ARP(地址解析协议)请求和回复,这些通常是第 2 层(数据链路)数据包。

在终端中运行以下命令,在 eth0 上监听 ARP 数据包:

sudo tcpdump -i eth0 -e arp -n &

-i eth0 标志指定要监听的网络接口,-e arp 过滤 ARP 数据包,-n 防止 IP 地址解析为主机名,使输出更易读。

使用 Scapy 构建 L2 帧

现在我们对网络设置有了基本的了解,并且可以观察 ARP 流量,让我们使用 Scapy 构建第一个自定义数据包。

安装 Scapy

如果你还没有安装 Scapy,请使用 pip 安装:

pip install scapy

发送 ARP 数据包

ARP 是一个第 2 层协议,用于将 IP 地址映射到 MAC 地址。让我们向 192.168.56.101 发送一个 ARP 请求以查找其 MAC 地址。

from scapy.all import Ether, ARP, sendp

# 将目标 MAC 设置为广播地址
destination_mac = "ff:ff:ff:ff:ff:ff"

# 构建 ARP 数据包
arp_packet = Ether(dst=destination_mac) / ARP(pdst="192.168.56.101")

# 发送数据包
sendp(arp_packet)

在此代码中: - Ether(dst=destination_mac) 创建一个以太网帧,其目标 MAC 地址设置为广播地址。 - ARP(pdst="192.168.56.101") 创建一个 ARP 数据包,其目标 IP 地址设置为 192.168.56.101。 - sendp(arp_packet) 发送数据包,但不等待响应。

观察ARP请求

返回你的 tcpdump 会话,你应该会看到类似以下内容的 ARP 请求:

14:30:01.1234 192.168.56.100 > ff:ff:ff:ff:ff:ff, ARP who has 192.168.56.101 says 192.168.56.100 on eth0

此输出显示,你的机器(192.168.56.100)正在向 192.168.56.101 广播 ARP 请求,以查找与之关联的 MAC 地址。

使用 Scapy 的 a.summary() 和 a.show() 分析数据包

Scapy 提供了强大的方法来解码和分析数据包。让我们看看如何使用这些方法详细检查我们的 ARP 数据包。

使用 a.summary()

a.summary() 方法提供数据包的简洁摘要:

# 发送数据包并捕获它
response = sendp(arp_packet, verbose=0)

# 打印摘要
print(response.summary())

输出:

Ether / ARP who has 192.168.56.1 says 192.168.56.100

此摘要显示,该数据包是一个以太网帧,其中包含一个 ARP 数据包。ARP 数据包正在询问与 192.168.56.1(可能是网关)关联的 MAC 地址,并以其自己的 MAC 地址(192.168.56.100)进行响应。

使用 a.show()

对于更详细的分析,使用 a.show() 方法:

# 发送数据包并捕获它
response = sendp(arp_packet, verbose=0)

# 打印详细输出
response.show()

输出:

Ethernet II
  dst = ff:ff:ff:ff:ff:ff
  src = 00:50:56:ac:de:01
  type = ARP

ARP
  op = is at
  psrc = 192.168.56.100
  pdst = 192.168.56.101

此详细输出提供以下信息: - 以太网帧: - 目标 MAC:ff:ff:ff:ff:ff:ff(广播) - 源 MAC:00:50:56:ac:de:01(你的机器的 MAC 地址) - 类型:ARP(0x0806) - ARP 数据包: - 操作:is at(ARP 请求) - 源 IP:192.168.56.100 - 目标 IP:192.168.56.101

向 OSI 模型上层进发:构建 L3 数据包

现在我们对第 2 层感到舒适,让我们构建一个第 3 层(网络)数据包。IP 数据包是跨网络路由数据的基础。

发送 ICMP 数据包

ICMP(Internet 控制消息协议)用于网络诊断(如 ping)和错误报告。让我们向 192.168.56.101 发送一个 ICMP 回显请求(ping)。

from scapy.all import IP, ICMP, sendp

# 定义目标 IP
destination_ip = "192.168.56.101"

# 构建 ICMP 数据包
icmp_packet = IP(dst=destination_ip) / ICMP()

# 发送数据包
sendp(icmp_packet)

在此代码中: - IP(dst=destination_ip) 创建一个目标 IP 地址设置为 192.168.56.101 的 IP 数据包。 - ICMP() 创建一个 ICMP 回显请求数据包。 - sendp(icmp_packet) 发送数据包。

分析 ICMP 数据包

使用 a.summary() 和 a.show() 检查 ICMP 数据包:

# 发送数据包并捕获它
response = sendp(icmp_packet, verbose=0)

# 打印摘要
print(response.summary())

输出:

IP / ICMP

此摘要表明该数据包是一个 IP 数据包,其中包含一个 ICMP 数据包。

对于详细分析:

# 打印详细输出
response.show()

输出:

IP
  version = 4
  ihl = 5
  tos = 0
  total length = 28
  id = 1
  flags = DF
  fragment offset = 0
  ttl = 64
  protocol = icmp
  checksum = 0x6ed5
  src = 192.168.56.100
  dst = 192.168.56.101

ICMP
  type = 8
  code = 0
  checksum = 0x404b

此详细输出提供: - IP 数据包: - 版本:IPv4 - 头部长度:5 - 总长度:28 字节 - ID:1 - 标志:不进行分片 (DF) - 分片偏移:0 - TTL:64 - 协议:ICMP - 校验和:0x6ed5 - 源 IP:192.168.56.100 - 目标 IP:192.168.56.101 - ICMP 数据包: - 类型:回显请求 (8) - 代码:0 - 校验和:0x404b

深入探索:构建 L4 数据包

进入第 4 层(传输),我们将构建 TCP 和 UDP 数据包。这些协议对于建立连接和传输数据至关重要。

发送 TCP SYN 数据包

TCP(传输控制协议)是一种面向连接的协议,用于可靠的数据传输。让我们向 192.168.56.101 的端口 80(HTTP)发送一个 TCP SYN 数据包。

from scapy.all import TCP, sendp

# 定义目标 IP 和端口
destination_ip = "192.168.56.101"
destination_port = 80

# 构建 TCP SYN 数据包
tcp_packet = IP(dst=destination_ip) / TCP(sport=RandShort(), dport=destination_port, flags="S")

# 发送数据包
sendp(tcp_packet)

在此代码中: - TCP(sport=RandShort(), dport=destination_port, flags="S") 创建一个 TCP 数据包,具有: - 源端口:随机化 (RandShort()) - 目标端口:80 - 标志:SYN (0x02),表示连接请求

分析 TCP SYN 数据包

使用 a.summary() 和 a.show() 检查 TCP 数据包:

# 发送数据包并捕获它
response = sendp(tcp_packet, verbose=0)

# 打印摘要
print(response.summary())

输出:

IP / TCP

对于详细分析:

# 打印详细输出
response.show()

输出:

IP
  version = 4
  ihl = 5
  tos = 0
  total length = 42
  id = 1
  flags = DF
  fragment offset = 0
  ttl = 64
  protocol = tcp
  checksum = 0x6f2f
  src = 192.168.56.100
  dst = 192.168.56.101

TCP
  seq = 0
  ack = 0
  data offset = 5
  reserved = 0
  flags = S
  window = 5840
  checksum = 0x6f2f
  urgent pointer = 0
  options = [MSS 1460, WScale 2, WTimeout 74, SACKPermitted, Timestamp 1580000000 0, EOL]
  sport = 46706
  dport = 80

此详细输出提供: - IP 数据包: - 与之前相同的字段 - TCP 数据包: - 序列号:0 - 确认号:0 - 数据偏移:5 - 保留:0 - 标志:SYN (0x02) - 窗口大小:5840 - 校验和:0x6f2f - 紧急指针:0 - 选项:MSS(最大段大小)1460,窗口缩放 2,窗口超时 74,允许 SACK,时间戳,EOL(选项结束) - 源端口:46706 - 目标端口:80

发送 UDP 数据包

UDP(用户数据报协议)是一种无连接协议,用于快速、轻量级的数据传输。让我们向 192.168.56.101 的端口 53(DNS)发送一个 UDP 数据包。

from scapy.all import UDP, sendp

# 定义目标 IP 和端口
destination_ip = "192.168.56.101"
destination_port = 53

# 构建UDP数据包
udp_packet = IP(dst=destination_ip) / UDP(sport=RandShort(), dport=destination_port)

# 发送数据包
sendp(udp_packet)

在此代码中: - UDP(sport=RandShort(), dport=destination_port) 创建一个 UDP 数据包,具有: - 源端口:随机化 (RandShort()) - 目标端口:53

分析 UDP 数据包

使用 a.summary() 和 a.show() 检查 UDP 数据包:

# 发送数据包并捕获它
response = sendp(udp_packet, verbose=0)

# 打印摘要
print(response.summary())

输出:

IP / UDP

对于详细分析:

# 打印详细输出
response.show()

输出:

IP
  version = 4
  ihl = 5
  tos = 0
  total length = 38
  id = 1
  flags = DF
  fragment offset = 0
  ttl = 64
  protocol = udp
  checksum = 0x548c
  src = 192.168.56.100
  dst = 192.168.56.101

UDP
  sport = 46706
  dport = 53
  length = 8
  checksum = 0x548c

此详细输出提供: - IP 数据包: - 与之前相同的字段 - UDP 数据包: - 源端口:46706 - 目标端口:53 - 长度:8 字节 - 校验和:0x548c

探索更高层:L5-L7 与 Scapy

虽然 Scapy 主要关注低层,但我们也可以与更高层(如会话层(第 5 层)、表示层(第 6 层)和应用层(第 7 层))交互。这对于 HTTP、DNS 和 TLS 等协议特别有用。

构建 HTTP 请求

让我们构建一个 HTTP GET 请求。为了简单起见,我们将使用 Scapy 中的 HTTP 层,该层在 TCP 上运行。

from scapy.all import IP, TCP, Raw, sendp

# 定义目标 IP 和端口
destination_ip = "192.168.56.101"
destination_port = 80

# 构建 HTTP GET 请求
http_payload = "GET / HTTP/1.1\r\nHost: 192.168.56.101\r\n\r\n"
http_packet = IP(dst=destination_ip) / TCP(sport=RandShort(), dport=destination_port) / Raw(load=http_payload)

# 发送数据包
sendp(http_packet)

在此代码中: - Raw(load=http_payload) 创建一个包含 HTTP GET 请求的原始负载。

分析 HTTP 请求

使用 a.summary() 和 a.show() 检查 HTTP 数据包:

# 发送数据包并捕获它
response = sendp(http_packet, verbose=0)

# 打印摘要
print(response.summary())

输出:

IP / TCP / Raw

对于详细分析:

# 打印详细输出
response.show()

输出:

IP
  version = 4
  ihl = 5
  tos = 0
  total length = 58
  id = 1
  flags = DF
  fragment offset = 0
  ttl = 64
  protocol = tcp
  checksum = 0x548c
  src = 192.168.56.100
  dst = 192.168.56.101

TCP
  seq = 0
  ack = 0
  data offset = 5
  reserved = 0
  flags = S
  window = 5840
  checksum = 0x548c
  urgent pointer = 0
  options = [MSS 1460, WScale 2, WTimeout 74, SACKPermitted, Timestamp 1580000000 0, EOL]
  sport = 46706
  dport = 80

Raw
  load = 'GET / HTTP/1.1\r\nHost: 192.168.56.101\r\n\r\n'

此详细输出显示了 Raw 层中的 HTTP 请求负载。

实际应用和注意事项

在不同层上构建和解析数据包对于网络管理员和安全专业人员来说非常有价值。以下是一些实际应用:

  1. **网络
.NET聊天代码悄悄丢失3处OpenAI加密推理 2026-10-20
我在信任.NET 10中的多代理工作流之前会进行的5个测试 2026-10-20

评论区