下面我将从基础到进阶,详细讲解如何使用 Scapy 伪造 IP 数据包。

重要声明:法律与道德警告
在开始之前,必须强调:未经授权对任何网络或计算机系统进行扫描、嗅探或数据包注入都是非法的。 这些技术仅应在你拥有所有权或明确书面许可的网络环境中使用,例如你自己的家庭实验室或公司内部测试环境,滥用这些技术可能导致严重的法律后果。
环境准备
确保你已经安装了 Scapy,如果尚未安装,可以通过 pip 安装:
pip install scapy
由于 Scapy 需要 raw socket 权限,直接运行 Python 脚本时可能会遇到权限问题,在 Linux 或 macOS 上,你可能需要使用 sudo 来运行脚本,在 Windows 上,可能需要以管理员身份运行 PowerShell 或 CMD。
基础:伪造一个简单的 IP 数据包
最简单的伪造 IP 数据包就是创建一个 IP 层,并填入你想要的源 IP 和目的 IP。

核心代码
from scapy.all import IP, send
# 1. 构造 IP 数据包
# src: 源 IP 地址(你伪造的 IP)
# dst: 目的 IP 地址(你要发送到的目标)
# 其他字段如 ttl, proto 等,Scapy 会自动填充默认值
ip_packet = IP(src="192.168.1.100", dst="10.0.0.1")
# 2. 发送数据包
# send() 函数会发送这个数据包 3 次(默认行为)
# 你会看到输出显示数据包已发送
send(ip_packet)
print("IP 伪造数据包已发送。")
代码解析
from scapy.all import IP, send: 从 Scapy 库中导入IP类(用于构造 IP 层)和send函数(用于发送数据包)。IP(src="...", dst="..."): 创建一个 IP 数据包实例。src参数是你想要伪造的源 IP 地址,dst是你想要发送到的目标 IP 地址。send(ip_packet): 将构造好的数据包发送到网络,Scapy 会自动处理底层的以太网帧(它会根据dstIP 地址通过 ARP 协议获取 MAC 地址,然后封装成以太网帧)。
当你运行这段代码时,目标 0.0.1 的机器可能会在日志中记录一个来自 168.1.100 的“幽灵”连接尝试,由于 168.1.100 可能不存在,目标机器的回复包会找不到路径,从而消失在网络中。
进阶:在 IP 数据包中添加负载
通常我们发送伪造的 IP 包不只是为了让对方“看到”,而是为了发送一些具体的数据,TCP SYN 包(端口扫描)或 UDP 包。
示例 1:伪造一个 TCP SYN 包(半开放端口扫描)
这是一个非常经典的用途,用于探测目标主机是否开放了特定端口。
from scapy.all import IP, TCP, send
# 1. 构造 IP 层
ip_layer = IP(src="192.168.1.100", dst="10.0.0.1")
# 2. 构造 TCP 层
# dport: 目标端口 (80 for HTTP)
# sport: 源端口 (Scapy 会随机生成一个)
# flags: TCP 标志位。'S' 代表 SYN,用于发起连接
tcp_layer = TCP(dport=80, flags='S')
# 3. 将 IP 层和 TCP 层组合起来
# 使用斜杠 `/` 是 Scapy 的层叠语法
syn_packet = ip_layer / tcp_layer
# 4. 发送数据包
# count=1 只发送一次
send(syn_packet, count=1, verbose=0) # verbose=0 减少输出
print(f"已向 {syn_packet[IP].dst} 的 {syn_packet[TCP].dport} 端口发送伪造的 SYN 包。")
示例 2:伪造一个 UDP 包
UDP是无连接的协议,发送伪造的 UDP 包非常简单。

from scapy.all import IP, UDP, Raw, send
# 1. 构造 IP 层
ip_layer = IP(src="192.168.1.100", dst="8.8.8.8") # Google DNS
# 2. 构造 UDP 层和负载
# dport: 目标端口 (53 for DNS)
# load: UDP 负载数据,这里我们放一个简单的 DNS 查询
udp_layer = UDP(dport=53)
dns_query = Raw(b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07google\x03com\x00\x00\x01\x00\x01')
# 3. 组合数据包
udp_packet = ip_layer / udp_layer / dns_query
# 4. 发送数据包
send(udp_packet, count=1, verbose=0)
print(f"已向 {udp_packet[IP].dst} 的 {udp_packet[UDP].dport} 端口发送伪造的 UDP 包。")
高级技巧与注意事项
技巧 1:随机化源 IP 和端口
为了避免被轻易识别或跟踪,可以随机化源 IP 和端口。
from scapy.all import IP, TCP, RandIP, RandShort, send
# RandIP() 生成一个随机的源 IP 地址
# RandShort() 生成一个随机的 16 位源端口号
random_syn_packet = IP(src=RandIP(), dst="10.0.0.1") / TCP(dport=80, flags='S', sport=RandShort())
send(random_syn_packet, count=5, verbose=0)
print("已发送 5 个带有随机源 IP 和端口的 SYN 包。")
技巧 2:发送并捕获响应
send() 函数是“即发即忘”的,很多时候,我们更关心目标主机的响应,这时可以使用 sr() (send and receive)。
from scapy.all import IP, TCP, sr, conf
# 设置 iface 为你的网卡名称,"eth0" 或 "Wi-Fi"
# 这可以确保数据包从正确的接口发送和接收
# conf.iface = "eth0"
# 构造数据包
syn_packet = IP(src="192.168.1.100", dst="10.0.0.1") / TCP(dport=22, flags='S')
# sr() 会发送数据包并等待响应
# 它返回两个列表:已收到响应的包对和未收到响应的包
ans, unans = sr(syn_packet, timeout=2, verbose=0)
print(f"收到 {len(ans)} 个响应,{len(unans)} 个未响应。")
# 遍历收到的响应
for sent_pkt, received_pkt in ans:
# received_pkt[0] 是收到的数据包
# 我们可以检查它是否是一个 SYN-ACK 包
if received_pkt.haslayer(TCP) and received_pkt[TCP].flags == 'SA':
print(f"端口 {sent_pkt[TCP].dport} 是开放的 (响应: {received_pkt[TCP].flags})")
else:
print(f"端口 {sent_pkt[TCP].dport} 是关闭的或被过滤的 (响应: {received_pkt[TCP].flags})")
技巧 3:伪造 MAC 地址
在局域网中,通信基于 MAC 地址,伪造 IP 的同时,通常也需要伪造源 MAC 地址。
from scapy.all import Ether, IP, TCP, send
# 1. 构造以太网层 (Layer 2)
# src: 伪造的源 MAC 地址
eth_layer = Ether(src="00:0a:95:9d:68:16", dst="ff:ff:ff:ff:ff:ff") # dst为广播地址
# 2. 构造 IP 层 (Layer 3)
ip_layer = IP(src="192.168.1.100", dst="10.0.0.1")
# 3. 构造 TCP 层 (Layer 4)
tcp_layer = TCP(dport=80, flags='S')
# 4. 组合所有层
full_packet = eth_layer / ip_layer / tcp_layer
# 5. 发送数据包
# 注意:发送这种二层包通常需要指定 iface
send(full_packet, iface="eth0", verbose=0)
print("已发送带有伪造 IP 和 MAC 地址的数据包。")
| 功能 | Scapy 代码 | 说明 |
|---|---|---|
| 伪造 IP | IP(src="FAKE_IP", dst="TARGET_IP") |
最基础的 IP 伪造。 |
| 添加 TCP | TCP(dport=80, flags='S') |
创建 TCP 层,可指定端口和标志位。 |
| 添加 UDP | UDP(dport=53) |
创建 UDP 层。 |
| 组合层 | ip_layer / tcp_layer |
使用 符号将不同层的数据包堆叠起来。 |
| 发送数据包 | send(packet) |
发送数据包,不关心响应。 |
| 发送并响应 | sr(packet) |
发送并等待响应,适用于端口扫描等。 |
| 随机化 | RandIP(), RandShort() |
随机化源 IP 和端口,增加匿名性。 |
| 伪造 MAC | Ether(src="FAKE_MAC") |
在局域网中伪造 MAC 地址。 |
Scapy 的功能远不止于此,它还可以处理更复杂的协议(如 ARP, ICMP, DNS, SSL/TLS),进行数据包嗅探、路由跟踪等,掌握 Scapy 是网络编程和安全研究人员的必备技能,再次提醒,请务必在合法合规的前提下使用它。
