2. Scapy环境搭建与核心对象
Scapy 的工程化使用从环境开始。与许多“装完即用”的库不同,Scapy 的能力边界高度依赖运行环境:是否具备原始套接字权限、是否安装了 libpcap、Python 版本是否匹配、是否在虚拟环境中隔离依赖,都会直接影响后续发包、嗅探与协议构造能否成功。本节围绕“装得上、跑得通、看得懂对象”三个目标展开。
一、安装与权限配置
推荐使用 Python 3.9 及以上版本,并在虚拟环境中安装,避免与系统包管理器冲突。基础安装命令如下:
python -m venv scapy-env
source scapy-env/bin/activate # Windows 使用 scapy-env\Scripts\activate
pip install scapy
# 需要图形化抓包与部分链路层能力时,额外安装
pip install scapy[complete]
在 Linux 上,嗅探与二层发送通常需要 CAP_NET_RAW 能力。最直接的方式是以 root 运行,但工程上更推荐给 Python 解释器赋权:
sudo setcap cap_net_raw,cap_net_admin=eip $(readlink -f $(which python3))
Windows 需要安装 Npcap(勾选 WinPcap API 兼容模式),否则 sniff() 与二层发送会失败。macOS 自带 BPF 设备,一般无需额外驱动,但需注意系统完整性保护对部分接口的限制。安装完成后,用一条最小命令验证环境:
from scapy.all import *
print(conf.iface) # 查看默认网卡
print(version) # 查看 Scapy 版本
ans, unans = sr(IP(dst="192.168.1.1")/ICMP(), timeout=2)
ans.summary()
若 sr() 返回 ans 非空,说明收发链路已通;若全部进入 unans,优先排查网卡选择、防火墙与目标可达性,而不是怀疑协议构造。
二、交互式与脚本模式
Scapy 提供两种典型使用方式。交互式模式通过 scapy 命令进入,自带 conf、ls()、lsc() 等辅助函数,适合探索协议字段、临时验证构造结果。脚本模式则是把逻辑写入 .py 文件,便于版本管理与自动化调度。
交互式下最常用的三个探索命令是:ls(IP) 查看某层的全部字段与默认值;ls() 列出所有可用协议层;lsc() 列出所有发送/嗅探类函数。脚本模式中应显式导入所需层,避免 from scapy.all import * 带来的命名污染:
from scapy.layers.inet import IP, TCP, ICMP
from scapy.layers.l2 import Ether, ARP
from scapy.sendrecv import sr1
三、Packet 与 Layer 对象模型
Scapy 的核心抽象是“包由层叠加而成”。一个 Packet 对象内部维护一个层列表,每层是一个 Layer 子类实例,层与层之间通过 / 运算符连接。构造 IP()/TCP() 时,Scapy 并不会立即生成字节流,而是先建立对象树;只有在发送或调用 bytes() 时才逐层序列化。
理解对象模型的关键在于三个操作:show() 以缩进形式打印各层字段;summary() 输出一行摘要;haslayer() 与 getlayer() 用于判断和提取特定层。例如:
pkt = Ether()/IP(dst="10.0.0.5")/TCP(dport=443, flags="S")
pkt.show()
pkt.haslayer(TCP) # True
pkt[TCP].dport # 443
pkt[IP].ttl = 64 # 修改字段后重新序列化即生效
字段赋值遵循“后写覆盖”原则:同一层重复设置同一字段,以最后一次为准;不同层之间字段名可以相同(如 len),访问时需通过 pkt[层名].字段 明确指定,否则会命中第一个匹配层。
四、常用协议层导入与构造方法
Scapy 按协议族组织模块,工程中应养成按需导入的习惯。常用导入路径如下表所示:
| 协议族 | 导入路径 | 典型层 |
|---|---|---|
| 链路层 | scapy.layers.l2 | Ether, ARP, Dot1Q |
| 网络层 | scapy.layers.inet | IP, ICMP, TCP, UDP |
| IPv6 | scapy.layers.inet6 | IPv6, ICMPv6ND_NS |
| 应用层 | scapy.layers.dns / http | DNS, DNSQR, HTTP |
| 无线 | scapy.layers.dot11 | Dot11, Dot11Beacon |
构造方法遵循“自下而上”的书写顺序,但 Scapy 会自动推导部分字段。例如构造一个 DNS 查询:
from scapy.layers.inet import IP, UDP
from scapy.layers.dns import DNS, DNSQR
pkt = IP(dst="8.8.8.8")/UDP(dport=53)/DNS(rd=1, qd=DNSQR(qname="example.com"))
resp = sr1(pkt, timeout=3)
resp[DNS].summary()
这里 IP.proto 会由 UDP 层自动填充为 17,UDP.len 也会在序列化时计算。若手动指定了与下层冲突的值,Scapy 默认以显式赋值为准,因此排查异常时应先用 show() 确认最终字段值,而不是只看构造语句。
scapy_base.py 模块,在项目内统一引用。这样既避免重复导入,也便于在 CI 中通过环境变量切换网卡与超时参数。
sr() 可能静默返回空结果而不报错;在容器中运行时,若未加 --cap-add=NET_RAW,嗅探会直接失败。遇到“构造正确但收不到回包”,先确认权限与网卡,再检查协议字段。