Scapy
是一个强大的 Python 第三方库,用于处理网络数据包,包括抓取、解析、构造和发送各种类型的网络数据包。下面是关于 Scapy
模块的一些介绍:
主要特点:
1.灵活性:Scapy 允许用户以非常灵活的方式操作网络数据包,可以轻松地创建、修改、发送和捕获各种网络数据包。
2.协议支持:Scapy 支持大量的网络协议,包括但不限于 Ethernet、IP、TCP、UDP、ICMP 等,用户可以方便地对这些协议进行操作。
3.交互式 Shell:Scapy 提供了交互式 Shell 界面,用户可以通过命令行直接操作网络数据包,方便进行调试和实验。
4.快速原型开发:通过 Scapy 可以快速地进行网络协议的原型开发和测试,帮助开发人员快速验证想法和设计。
5.功能丰富:Scapy 提供了丰富的功能和方法,如数据包解析、编辑、发送、抓取、嗅探等,满足了不同场景下的网络数据包处理需求。
接下来我们就利用 scapy模块设计开发一个以太网数据构造小工具。
该以太网包构造工具是基于Scapy库实现的,旨在提供一个简单易用的接口,用于创建、发送以太网数据包,并支持捕获网络数据包。下面是该工具的设计说明:
1. 功能概述
- 创建数据包: 提供了创建以太帧、IP、ARP、ICMP、UDP、TCP、VLAN标签等不同类型数据包的方法。
- 发送数据包: 可以将构造好的数据包发送到指定的网络接口。
- 捕获数据包: 支持在指定网卡上捕获符合条件的数据包,并提供回调函数和停止条件的设置。
2. 主要类及方法
- EthLibrary类: 主要包含了创建数据包、发送数据包和捕获数据包的方法。
CreateEtherPack()
: 创建以太帧数据包。CreateIpPack()
: 创建IP数据包。CreateArpPack()
: 创建ARP数据包。CreateIcmpPack()
: 创建ICMP数据包。CreateUdpPack()
: 创建UDP数据包。CreateTcpPack()
: 创建TCP数据包。CreateVlanTag()
: 创建VLAN标签。SendMessage()
: 发送以太数据帧。EthGetPacketThread()
: 开启捕获以太网数据的线程。EthGetRecvPack()
: 获取当前捕获到的数据包列表。
3. 设计方案选择说明
- 使用Scapy库: Scapy是一个功能强大的网络数据包构造和解析库,具有良好的跨平台性和灵活性,能够满足数据包构造工具的需求。
- 面向对象设计: 采用面向对象的设计方法,将数据包的构造和操作封装成类和方法,提高了代码的可维护性和复用性。
- 异常处理: 在方法中添加了异常处理机制,能够及时捕获错误并输出异常信息,保证了程序的稳定性和可靠性。
- 线程支持: 通过多线程实现了数据包的捕获功能,能够在后台异步捕获数据包,不影响主程序的执行。
- 灵活配置: 提供了丰富的参数配置选项,用户可以根据需要灵活设置数据包的各种属性,满足不同场景下的需求。
4. 完整代码如下:
# -*- coding: gbk -*-
'''
Created on 2024年4月16日
@author: DanMo
'''
from scapy.layers.l2 import Ether, ARP, Dot1Q
from scapy.all import *
from scapy.layers.inet import ICMP, IP, UDP, TCP
class EthLibrary(object):
def __init__(self):
"""
初始化函数
:param pack_list : 实时捕获的以太网数据包以列表的形式保存在本地,每一个捕获的数据包格式可按层划分,各层对应的类名称为:Ether、IP、ARP、ICMP、UDP、TCP、Dot1Q
例如:
self.pack_list[0][Ether].dst即为取第一个数据包的以太网头部的dst字段
self.pack_list[0][IP].dst即为取第一个数据包的IP层的dst字段
"""
self.pack_list = []
def CreateEtherPack(self, srcMac, dstMac, dataType):
"""
创建以太帧数据包
CreateEtherPack(srcMac, dstMac, dataType)
:param srcMac: 源MAC
:param dstMac: 目的MAC,组播MAC地址以01-00-5E打头儿,随后的1个比特是0,广播MAC地址是"ff:ff:ff:ff:ff:ff"
:param dataType:上层协议类型,例如,字段为 0x0800 时,表示将数据交付给 IP 协议
:return 返回一个以太帧头部对象
例如:
ether = EthApi.CreateEtherPack("e0:be:03:39:05:1d", "00:0c:29:50:9f:e3", 0x8100)
"""
try:
etherHeader = Ether(src=srcMac, dst=dstMac, type=dataType)
return etherHeader
except:
print(traceback.format_exc())
assert True == False
def CreateIpPack(self, srcIp, dstIp, ttl):
"""
创建ip数据包
CreateIpPack(srcIp, dstIp, ttl)
:param srcIp: 源ip,字符串类型
:param dstIp: 目的ip,字符串类型
:param ttl:生存时间,int类型
:return 返回一个IP报文对象
例如:
ippac = EthApi.IP(srcIp="10.170.138.131", dstIp="10.170.138.102", ttl=3)
"""
try:
ipPack = IP(src=srcIp, dst=dstIp, ttl=ttl)
return ipPack
except:
print(traceback.format_exc())
assert True == False
def CreateArpPack(self, pSrc, pDst, hwSrc, hwDst):
"""
创建arp数据包
CreateArpPack(pSrc, pDst, hwSrc, hwDst)
:param pSrc: 源ip,字符串类型
:param pDst: 目的ip,字符串类型
:param hwSrc:源MAC地址,字符串类型
:param hwDst:目的MAC地址,字符串类型
:return 返回一个ARP报文对象
例如:
arppac = EthApi.CreateArpPack(pSrc="10.170.138.131", pDst="10.170.138.102", hwSrc="e0:be:03:39:05:1d", hwDst="4c:cc:6a:aa:53:51")
"""
try:
arpPack = ARP(psrc=pSrc, pdst=pDst, hwsrc=hwSrc, hwdst=hwDst)
return arpPack
except:
print(traceback.format_exc())
assert True == False
def CreateIcmpPack(self):
"""
创建icmp数据包
CreateIcmpPack()
例如:
icmppac = EthApi.CreateIcmpPack()
"""
try:
icmpPack = ICMP()
return icmpPack
except:
print(traceback.format_exc())
assert True == False
def CreateUdpPack(self, sPort, dPort, fcs):
"""
创建udp数据包
CreateUdpPack(sPort, dPort)
:param sPort: 源端口,int类型
:param dPort: 目的端口,int类型
:param fcs: 循环冗余检验
:return 返回一个UDP报文对象
例如:
udppac = EthApi.CreateUdpPack(sPort=30491, dPort=30491)
"""
try:
udpPack = UDP(sport=sPort, dport=dPort, chksum=fcs)
return udpPack
except:
print(traceback.format_exc())
assert True == False
def CreateTcpPack(self, sPort, dPort):
"""
创建tcp数据包
CreateTcpPack(sPort, dPort)
:param sPort: 源端口,int类型
:param dPort: 目的端口,int类型
:return 返回一个TCP报文对象
例如:
tcppac = EthApi.CreateTcpPack(sPort=30000, dPort=10000)
"""
try:
tcpPack = TCP(sport=sPort, dport=dPort)
return tcpPack
except:
print(traceback.format_exc())
assert True == False
def CreateVlanTag(self, pri, vid, datatype):
"""
创建Vlan标签
CreateVlanTag(pri, vid, datatype)
:param pri: 帧优先级,取值范围为0-7,值越大,优先级越高,占3bit,int类型
:param vid: 帧所属的vlan的id值,占12bit,int类型
:param datatype:标签后续的数据类型,int类型
:return 返回一个tag标签对象
例如:
vlantag = EthApi.CreateVlanTag(prio=0x1, vlan=0x001, type=0x0800)
"""
try:
vlanTag = Dot1Q(prio=pri, vlan=vid, type=datatype)
return vlanTag
except:
print(traceback.format_exc())
assert True == False
def SendMessage(self, packet):
"""
SendMessage(packet)
发送以太数据帧
:param packet: 待发送的以太网数据帧
:return 无
例如:
EthApi.SendMessage(packet)
"""
try:
sendp(packet)
except:
print(traceback.format_exc())
assert True == False
def EthGetPacketThread(self, ethIface = None, cnt = None, ethfilter = None, timeout = None, callbackfunc = None, stopfilter = None):
"""
EthGetPacketThread(ethIface = None, cnt = None, ethfilter = None, timeout = None, callbackfunc = None, stopfilter = None)
开启捕获以太网数据的线程
:param ethIface:指定抓包的网卡,不指定则代表所有网卡
:param cnt: 指定最多捕获的符合要求的报文个数,设置为0时则一直捕获
:param ethfilter: 捕获的过滤条件
:param timeout: 捕获的超时时间
:param callbackfunc: 为每个数据包定义一个回调函数,回调函数会在捕获到符合 filter 的报文时被调用
:param stopfilter: 捕获到符合此筛选条件的以太网数据则停止捕获
:return 无
例如:
EthApi.EthGetPacketThread(ethIface = "Realtek USB GbE Family Controller", cnt = 1, ethfilter = 'arp src net 192.168.99.4', timeout = 10, callbackfunc = packet_callback)
其中:
packet_callback是自己定义的回调函数
"""
try:
serverThread = threading.Thread(target=self._EthSniff, args=(ethIface, cnt, ethfilter, timeout, callbackfunc, stopfilter))
serverThread.setDaemon(True)
serverThread.start()
except:
print(traceback.format_exc())
assert True == False
def _EthSniff(self, ethIface = None, cnt = None, ethfilter = None, timeout = None, callbackfunc = None, stopfilter = None):
"""
捕获指定网卡的以太数据帧
:param ethIface: 指定抓包的网卡,不指定则代表所有网卡
:param cnt: 指定最多捕获的符合要求的报文个数,设置为0时则一直捕获
:param ethfilter: 捕获的过滤条件
:param timeout: 捕获的超时时间
:param callbackfunc: 为每个数据包定义一个回调函数,回调函数会在捕获到符合 filter 的报文时被调用,通常使用 lambda 表达式来编写
:param stopfilter: 捕获到符合此筛选条件的以太网数据则停止捕获
:return 无
"""
print("thread start")
pkt = sniff(iface = ethIface, count = cnt, filter = ethfilter, timeout = timeout, prn = callbackfunc, stop_filter = stopfilter)
print("thread stop")
self.pack_list.clear()
self.pack_list = pkt
def EthGetRecvPack(self):
"""
获取当前捕获到的数据包列表
:param 无
:return 当前捕获到的数据包列表
"""
try:
ret_list = self.pack_list
self.pack_list = []
return ret_list
except:
print(traceback.format_exc())
assert True == False