Scapy 是 Python 中最强大的网络数据包操作库 ,可用于构造、发送、捕获、解析、修改几乎所有网络协议数据包,广泛用于网络测试、安全审计、协议开发与渗透测试。
一、安装与环境准备
1. 安装 Scapy
bash
# 基础安装
pip install scapy
# 查看版本
python -c "import scapy; print(scapy.__version__)"
2. Windows 额外依赖
Windows 需安装 Npcap(兼容 WinPcap)才能正常抓包/发包:
- 下载:https://npcap.com/
- 安装时勾选 Install Npcap in WinPcap API-compatible Mode
3. Linux/macOS 权限
抓包/发包通常需要管理员权限:
bash
# Linux/macOS
sudo python your_script.py
# 或直接进入 Scapy 交互
sudo scapy
二、核心概念:数据包分层与构造
Scapy 用 / 运算符 拼接协议层(从链路层到应用层),非常直观。
1. 常用协议层
- 链路层:
Ether()(以太网)、ARP() - 网络层:
IP()、IPv6()、ICMP() - 传输层:
TCP()、UDP() - 应用层:
DNS()、HTTP()、Raw 数据
2. 构造示例
python
from scapy.all import *
# 1. 简单 ICMP (Ping)
pkt1 = IP(dst="8.8.8.8") / ICMP()
# 2. 以太网 + IP + TCP + 数据
pkt2 = Ether() / IP(dst="192.168.1.1") / TCP(dport=80, flags="S") / b"GET /"
# 3. ARP 请求
pkt3 = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="192.168.1.0/24")
3. 查看/修改字段
python
# 查看协议字段(如 TCP)
ls(TCP)
# 修改字段
pkt = IP() / TCP()
pkt[IP].dst = "10.0.0.1"
pkt[TCP].dport = 443
pkt[TCP].flags = "SA" # SYN-ACK
# 显示数据包详情
pkt.show()
三、发送与接收数据包
1. 仅发送(不等待响应)
python
# 三层发送(IP 层)
send(IP(dst="8.8.8.8")/ICMP())
# 二层发送(以太网层,需指定网卡)
sendp(Ether()/IP(dst="8.8.8.8")/ICMP(), iface="以太网")
2. 发送并等待响应(最常用)
python
# sr1: 发送并等待第一个响应(超时 2 秒)
resp = sr1(IP(dst="8.8.8.8")/ICMP(), timeout=2)
if resp:
print(f"响应来自: {resp[IP].src}")
resp.show() # 打印完整响应包
else:
print("无响应")
# sr: 发送并等待所有响应,返回 (应答包, 未应答包)
ans, unans = sr(IP(dst="192.168.1.1-10")/ICMP(), timeout=1)
for snd, rcv in ans:
print(f"{snd[IP].dst} 存活")
四、数据包捕获(sniff)
python
# 1. 基础抓包(过滤 ICMP,抓 5 个包)
def packet_callback(pkt):
if ICMP in pkt:
print(f"捕获 ICMP: {pkt[IP].src} -> {pkt[IP].dst}")
pkt.show()
# 启动抓包
sniff(
filter="icmp", # BPF 过滤语法
prn=packet_callback, # 每个包的回调
count=5, # 抓 5 个包后停止
iface="以太网" # 指定网卡
)
# 2. 保存抓包到文件
pkts = sniff(filter="tcp port 80", count=10)
wrpcap("http_traffic.pcap", pkts) # 可在 Wireshark 打开
# 3. 读取 pcap 文件
pkts = rdpcap("http_traffic.pcap")
for pkt in pkts:
pkt.summary()
五、常用场景示例
1. ARP 扫描(发现局域网存活主机)
python
from scapy.all import *
def arp_scan(net):
ans, _ = srp(
Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=net),
timeout=2,
verbose=0
)
return [rcv[ARP].psrc for snd, rcv in ans]
# 扫描 192.168.1.0/24
hosts = arp_scan("192.168.1.0/24")
print("存活主机:")
for ip in hosts:
print(ip)
2. TCP 端口扫描
python
from scapy.all import *
def port_scan(ip, ports):
open_ports = []
for port in ports:
resp = sr1(
IP(dst=ip)/TCP(dport=port, flags="S"),
timeout=0.5,
verbose=0
)
if resp and resp[TCP].flags == "SA": # SYN-ACK 表示端口开放
open_ports.append(port)
return open_ports
# 扫描 80,443,8080 端口
ports = port_scan("192.168.1.1", [80, 443, 8080])
print(f"开放端口: {ports}")
3. 简单流量嗅探与解析
python
from scapy.all import *
def parse_packet(pkt):
if IP in pkt:
src = pkt[IP].src
dst = pkt[IP].dst
proto = pkt[IP].proto
print(f"IP: {src} -> {dst} (协议: {proto})")
if TCP in pkt:
sport = pkt[TCP].sport
dport = pkt[TCP].dport
flags = pkt[TCP].flags
print(f"TCP: {sport} -> {dport} 标志: {flags}")
sniff(prn=parse_packet, count=10)
六、关键函数速览
| 函数 | 作用 |
|---|---|
send() |
三层发包(不等待响应) |
sendp() |
二层发包(不等待响应) |
sr1() |
发包并等待第一个响应 |
sr() |
发包并等待所有响应,返回 (ans, unans) |
srp() |
二层发包并等待响应 |
sniff() |
捕获数据包 |
wrpcap() |
保存数据包到 pcap 文件 |
rdpcap() |
读取 pcap 文件 |
ls() |
查看协议字段 |
pkt.show() |
打印数据包详情 |
七、注意事项
- 权限:抓包/发包通常需要管理员/root 权限。
- 性能:Python 性能有限,高流量场景建议用 C/C++ 工具(如 tcpdump、nmap)。
- 合法性 :仅在授权网络中使用,遵守当地法律法规,禁止未经许可的扫描/嗅探。
- 过滤 :
sniff()用 BPF 语法过滤,大幅提升效率。