Python教程:使用Scapy模块实现一个以太网数据包构造小工具(附完整代码)

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

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旦莫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值