Python Scapy模块详解:从基础命令到高级网络扫描
本文详细介绍Python中Scapy模块的使用方法,涵盖常用命令、数据包构造(IP、ARP、TCP等)、发包收包技巧、SYN半开式扫描以及数据序列化与离线解析。通过具体代码示例,帮助开发者掌握网络通信底层操作,提升网络安全测试与开发效率。
前言
日常网络浏览涉及大量数据包的发送、接收与处理循环。

本文将解析网络数据包的构成,并介绍Scapy这一强大的发包与收包工具。
数据包(Packet)是网络通信的基本单位,通常包含源地址、目标地址及有效载荷数据。
数据包结构由固定长度的包头和可变长度的包体组成。
了解数据包基础后,接下来介绍Scapy的具体用法。
一、常用命令
1. ls():用于列出所有支持的数据包对象,可带参数查看特定包的详细信息,也可不带参数查看所有。

通过该命令可以查看完整的内容列表。若需深入查看特定模块,例如ARP或TCP,可指定参数进行查询。

注意命令的大小写,例如必须使用ls(ARP)才能获取正确结果,ls(arp)将报错。
2. lsc():用于列出Scapy支持的所有函数。

该命令输出包含大量英文函数名,初学者可能需要一定时间熟悉。
3. hide_defaults():用于隐藏与默认值相同的参数项,简化输出显示。
a=IP()
print(a.hide_defaults())
4. display():用于查看当前数据包各参数的具体取值情况。
a=IP()
a.display()
5. 更多命令:Scapy提供了丰富的辅助命令,如show_interfaces()显示网卡信息,str(pkt)组装数据包,hexdump(pkt)进行十六进制转储,ls(pkt)显示字段值列表,pkt.summary()显示一行摘要,pkt.show()展开视图,pkt.show2()显示聚合数据包(如计算校验和),pkt.sprintf()用字段填充格式字符串,pkt.decode_payload_as()改变负载解码方式,pkt.psdump()绘制PostScript图表,pkt.pdfdump()绘制PDF,pkt.command()返回生成该数据包的Scapy命令,nsummary()规定数量的摘要,conversations()显示会话图表,filter()返回过滤后的数据包列表,hexdump()返回所有数据包的十六进制转储,import_hexcap()将十六进制导入Scapy,make table()根据Lambda函数显示表格,traceroute()查看IP路径,export_object()将数据包转换为Base64编码的Python数据结构,import_object()重新导入输出,save_session()保存会话变量,load_session()读取保存的会话,fuzz()随机更改某些未计算的值(如校验和),但符合字段值范围。
命令 作用
show_interfaces() 显示网卡信息
str(pkt) 组装数据包
hexdump(pkt) 十六进制转储
ls(pkt) 显示出字段值的列表
pkt.summary() 一行摘要
pkt.show() 针对数据包的展开视图
pkt.show2() 显示聚合的数据包(例如,计算好了的校验和)
pkt.sprintf() 用数据包字段填充格式字符串
pkt.decode_payload_as() 改变payload的decode方式
pkt.psdump() 绘制一个解释说明的PostScript图表
pkt.pdfdump() 绘制一个解释说明的PDF
pkt.command() 返回可以生成数据包的Scapy命令
nsummary() 同上,但规定了数据包数量
conversations() 显示一个会话图表
filter() 返回一个lambda过滤后的数据包列表
hexdump() 返回所有数据包的一个hexdump
import_hexcap() 将hexdump重新导入到Scapy中
hexraw() 返回所有数据包Raw layer的hexdump
padding() 返回一个带填充的数据包的hexdump
nzpadding() 返回一个具有非零填充的数据包的hexdump
plot() 规划一个应用到数据包列表的lambda函数
make table() 根据lambda函数来显示表格
traceroute("baidu.com") 查看IP路径的traceroute功能
export_object() 数据包转换成base64编码的Python数据结构
import_object() 可以将输出重新导入
save_session() 保存所有的session变量
load_session() 读取保存的session
fuzz() 更改一些默认的不被计算的值(比如校验和checksums),更改的值是随机的,但是是符合字段的值的。
二、嗅探数据包
from scapy.all import *
pkt = sniff(iface = "Realtek PCIe GBE Family Controller",count = 3 ,filter='tcp',prn = lambda x: x.sprintf('{IP:%IP.src%->%IP.dst%\n}{Raw:%Raw.load%\n}'))
filter:过滤条件
iface:网卡接口名称
count:数据包数量
prn:回调函数,通常与lambda搭配使用
sprintf()函数控制输入信息
抓取源地址为192.168.3.3的端口为80的tcp报文:
sniff(filter="ip src 192.168.3.3 and tcp and tcp port 80", prn=lambda x:x.summary())
抓取目的地址网段为192.168.3.3/24的报文:
sniff(filter="dst net 192.168", prn=lambda x:x.summary())
抓取非ICMP的报文:
sniff(filter="not icmp", prn=lambda x:x.summary())
将抓取到的报文的summary打印出来:
sniff(filter="icmp", prn=lambda x:x.summary(), count=10)
将所有IP报文的源地址打印出来:
sniff(filter="icmp", prn=lambda x:x[IP].src, count=10)
三、构造数据包
pkt= Ether()/IP(dst='192.168.1.2')/TCP(dport=80)
涉及网络协议时,需参考OSI七层模型。
| OSI 七层网络模型 | TCP/IP 四层概念模型 | 对应网络协议 |
| 应用层(Application) | 应用层 | HTTP、TFTP, FTP, NFS, WAIS、SMTP |
| 表示层(Presentation) | 应用层 | Telnet, Rlogin, SNMP, Gopher |
| 会话层(Session) | 应用层 | SMTP, DNS |
| 传输层(Transport) | 传输层 | TCP, UDP |
| 网络层(Network) | 网络层 | IP, ICMP, ARP, RARP, AKP, UUCP |
| 数据链路层(Data Link) | 数据链路层 | FDDI, Ethernet, Arpanet, PDN, SLIP, PPP |
| 物理层(Physical) | 数据链路层 | IEEE 802.1A, IEEE 802.2到IEEE 802.11 |
上述表格展示了各网络协议对应的OSI模型层级,接下来介绍具体协议的使用方法。
四、各个协议用法
1. 构造IP包:可通过传入参数创建IP数据包,例如指定目标IP和TTL值。
#构造一个 IP 包,并传入一些参数
pkt = IP(dst="192.168.1.2",ttl=10)
ls(pkt)
version:版本号
ihl:头长度
tos:服务类型
len:IP数据包总长
id:标识符
flags:标记
flag:片偏移
ttl:生存时间
proto:协议类型
chksum:头部校验
src:源IP地址
dst:目的IP地址
options:可选项
2. 构造ARP包:通过指定操作类型、目标MAC和目的IP来创建ARP请求或响应。
#构造ARP包
ARP(op=1, hwdst="ff:ff:ff:ff:ff:ff", pdst=ip_address)
#arp类的构造函数列表:
ls(ARP)
hwtype : XShortField = (1) 值为1表示以太网地址,其它还可能表示令牌环地址
ptype : XShortEnumField = (2048) 0x0800表示IP地址,其它还可能是ICMP/IGMP
hwlen : ByteField = (6) ARP报文中,它的值为6
plen : ByteField = (4) ARP报文中,它的值为4
op : ShortEnumField = (1) 取值为1或者2,代表ARP请求或者响应包。1.ARP请求,2.ARP应答,3.RARP请求,4.RARP应答
hwsrc : ARPSourceMACField = (None) 发送方Mac地址。
psrc : SourceIPField = (None) 发送方IP地址。
hwdst : MACField = ('00:00:00:00:00:00') 目标Mac地址。
pdst : IPField = ('0.0.0.0') 目标IP地址。
3. 构造Ether包:创建以太网帧,通常需指定目的和源MAC地址,若不指定则默认为广播地址ff:ff:ff:ff:ff:ff。
#构造Ether
Ether(dst="ff:ff:ff:ff:ff:ff")
ls(Ether)
dst : DestMACField = (None) 目的MAC
src : SourceMACField = (None) 源MAC
type : XShortEnumField = (36864)
构造一个以太网数据包通常需要指定目标和源MAC地址,如果不指定,默认发出的就是广播包ff:ff:ff:ff:ff:ff
4. 构造TCP包:指定源端口、目的端口、序列号、确认号、标志位(如S表示SYN)、窗口大小等参数。
#构造TCP包
sport : ShortEnumField = 20 (20) 目标端口
dport : ShortEnumField = 80 (80) 源端口
seq : IntField = 0 (0)
ack : IntField = 0 (0)
dataofs : BitField (4 bits) = None (None)
reserved : BitField (3 bits) = 0 (0)
flags : FlagsField (9 bits) = <Flag 2 (S)> (<Flag 2 (S)>)
window : ShortField = 8192 (8192)
chksum : XShortField = None (None)
urgptr : ShortField = 0 (0)
options : TCPOptionsField = [] (b'')
五、发包,收包
Scapy提供两种主要模式:只发不收,以及发包且收包。
1. 只发不收:使用send()在第三层发包,或sendp()在第二层发包,均无接收功能。
send(pkt, inter=0, loop=0, count=1, iface=N)
pkt:数据包
inter:发包间隔时间
count:发包数量
iface:网卡接口名称
send(),在第三层发包,没有接收功能;send(IP(dst="www.baidu.com",ttl=2)/ICMP())
sendp(),在第二层发包,没有接收功能。sr(Ether()/IP(dst=www.baidu.com))
2. 发包且收包:使用sr()或sr1()在第三层发包并接收回复,sr1仅接收第一个回复;使用srp()或srp1()在第二层发包并接收回复。
sr()和sr1()都是在第三层发包,sr1表示只接收第一个回复。
sr(IP(dst="www.baidu.com",ttl=(1,4))/TCP(dport=[21,23,80],flags="S")) 返回两个值
sr1(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
srloop(IP(dst="www.baidu.com",ttl=1)/ICMP()) #不停的ping百度
srloop(IP(dst="www.baidu.com",ttl=1)/ICMP(),inter=3,count=2) #每隔3秒ping一次,一共执行两次
#inter表示间隔,count记录次数
srp()和srp1()都是根据第二层发包,srp1表示只接收第一个回复
srp(Ether()/IP(dst="www.baidu.com"))
srp1(Ether()/IP(dst=www.baidu.com))
六、SYN半开式扫描
当TCP连接指定端口时,若flags参数设为S(SYN),则执行半开式扫描。若端口监听,返回syn/ack;否则返回rst/ack。
sr1(IP(dst="192.168.1.2")/TCP(dport=80,flags="S"))
七、数据包序列化,反序列化
序列化:使用wrpcap()将数据包对象保存为pcap文件。
反序列化:使用rdpcap()读取pcap文件内容。
pkt= Ether()/IP(dst='192.168.1.2')/TCP(dport=80)
#将嗅探到的数据包内容写到pcap文件中
wrpcap("hw.pcap",pkt)
#读取pcap文件。
read=rdpcap('hw.pcap')
print(read[1])#打印嗅探到的包的数据
八、数据包与字符串转换
通过str()将数据包转换为字符串,或使用Ether()等构造函数从字符串还原数据包,便于直观分析。
zfc= str(pkts[0])
z= Ether(zfc)
九、导入导出 base64 编码
使用export_object()导出数据包为Base64编码字符串,利用import_object()重新导入,这是一种数据加密与传输方式。
export_object(str(pkts[0])) 导出数据包
new_Pkt = import_object() #将上一步导出的字符串填入
十、离线数据包的解析
在未联网环境下,可通过离线文件解析数据包。
使用sniff(offline="hw.pcap")即可对捕获的离线数据包进行分析。
sniff(offline = "hw.pcap")#离线数据包
总结
Scapy功能强大,通过少量命令即可实现复杂的发包收包操作,显著节省开发时间。
深入学习其命令可发现更多实用功能,但请确保仅用于合法的技术研究与开发。
以上就是关于Python使用scapy模块发包收包的详细内容,更多相关技术文章请关注本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









