python3使用scapy模块实现数据包回放工具

本文介绍了如何使用Python的Scapy库来配置数据包回放,包括设置回放参数、客户端和服务器过滤条件,以及对IPv4和IPv6报文的处理。作者提供了代码示例和配置文件结构,展示了如何根据需求定制数据包的发送行为。
摘要由CSDN通过智能技术生成

还是废话不多说,直接上代码,另外不足之处欢迎指正

# conding=utf-8
# import time
from scapy.all import *
import configparser
import re

config_load_flag = pkts_filter_flag = False
pktswithiface = []
gc = cc = sc = dict()
config = """#本文件是数据包回访配置文件,主要包含3部分:
#1、global_config:主要设置回放的参数,详细看每个参数上的注释
#2、client_config:设置客户端发送包的过滤条件和网卡
#3、server_config:设置服务器发送包的过滤条件和网卡
#回放的拓扑如下:客户端------设备--------服务器
#当然如果只是想一个网卡回放所有报文,只需server_config中与client_config设置相同网卡即可,或者client_config参数能过滤所有报文
#注意事项:1、不要删除文件内任何参数,哪怕为空 2、所有参数值不需要加引号,有空格也不需要 3、有的参数不能为空,根据参数上注释填写
#
#本地网卡列表如下:
#{ifaces}
[global_config]
#文本路径不能为空
filepath = test.pcap
#回访报文的类型:ipv4/ipv6,不能为空
ip_version = ipv4
#是否需要去除报文内vlan,方便在虚拟化环境测试,1-去除,0-不去除
vlan_remove = 1
#回放每个报文的间隔,可以为空,因为是无状态发包,所以可能会存在第1个包还没转发出去,第2个包就已经收到的情况,这种情况可以配置时间间隔,例如:0.1
interval_time = 
#回放报文的轮数,填写0无限回放
loop_times = 1

#客户端发送流量信息配置,一个报文先匹配客户端过滤条件,再匹配服务器过滤条件,如都不匹配则不会回放
[client_config]
#源IP,可以为空
src = 192.169.11.85
#目的IP,可以为空
dst =
#协议类型(TCP/UDP),可以为空,在回放纯IP报文或者icmp报文时,pro、sport、dport都需要为空
pro = TCP
#源端口,可以为空
sport =
#目的端口,可以为空
dport = 20150
#回放网卡,不能为空
iface =

#服务端发送流量信息配置,注释可参考client_config
[server_config]
src = 172.16.4.206
dst =
pro = TCP
sport =
dport = 59232
iface =

"""


# 获取网卡描述列表
def getiface_desc():
    interfaces = []
    for key, value in conf.ifaces.data.items():
        interfaces.append(value.description)
    return interfaces


# 获取网卡名称列表
def getiface_name():
    return [value.name for key, value in conf.ifaces.data.items()]


def create_configfile(config):
    config = config.format(ifaces=getiface_name())
    with open("pktreplay.conf", mode="w", encoding="utf-8") as pf:
        pf.write(config)
        print("配置文件生成完毕,文件名:pktreplay.conf,请根据文件内说明按需修改")


# 包过滤
def is_correct_pkt(pkt: scapy.layers.l2.Ether, five_tuples: dict) -> bool:
    if pkt.haslayer('IP'):
        if five_tuples["src"] == "" or pkt["IP"].src == five_tuples["src"]:
            if five_tuples["dst"] == "" or pkt["IP"].dst == five_tuples["dst"]:
                if five_tuples["pro"] == "":
                    if five_tuples["sport"] == "":
                        if five_tuples["dport"] == "":
                            return True
                        elif pkt.haslayer("TCP") and pkt["TCP"].dport == five_tuples["dport"]:
                            return True
                        elif pkt.haslayer("UDP") and pkt["UDP"].dport == five_tuples["dport"]:
                            return True
                    elif pkt.haslayer("TCP") and pkt["TCP"].sport == five_tuples["sport"]:
                        if five_tuples["dport"] == "":
                            return True
                        elif pkt["TCP"].dport == five_tuples["dport"]:
                            return True

                    elif pkt.haslayer("UDP") and pkt["UDP"].sport == five_tuples["sport"]:
                        if five_tuples["dport"] == "":
                            return True
                        elif pkt["UDP"].dport == five_tuples["dport"]:
                            return True

                elif pkt.haslayer(five_tuples["pro"]):
                    if five_tuples["sport"] == "" or pkt[five_tuples["pro"]].sport == five_tuples["sport"]:
                        if five_tuples["dport"] == "":
                            return True
                        elif pkt[five_tuples["pro"]].dport == five_tuples["dport"]:
                            return True

    return False


def is_correct_ipv6_pkt(pkt: scapy.layers.l2.Ether, five_tuples: dict) -> bool:
    if not pkt.haslayer('IPv6'):
        return False
    if five_tuples.get("src") != "" and pkt["IPv6"].src != five_tuples.get("src"):
        return False
    if five_tuples.get("dst") != "" and pkt["IPv6"].src != five_tuples.get("src"):
        return False
    if five_tuples["pro"] == "":
        if five_tuples["sport"] == "":
            if five_tuples["dport"] == "":
                return True
            elif pkt.haslayer("TCP") and pkt["TCP"].dport == five_tuples["dport"]:
                return True
            elif pkt.haslayer("UDP") and pkt["UDP"].dport == five_tuples["dport"]:
                return True
        elif pkt.haslayer("TCP") and pkt["TCP"].sport == five_tuples["sport"]:
            if five_tuples["dport"] == "":
                return True
            elif pkt["TCP"].dport == five_tuples["dport"]:
                return True

        elif pkt.haslayer("UDP") and pkt["UDP"].sport == five_tuples["sport"]:
            if five_tuples["dport"] == "":
                return True
            elif pkt["UDP"].dport == five_tuples["dport"]:
                return True

    elif pkt.haslayer(five_tuples["pro"]):
        if five_tuples["sport"] == "" or pkt[five_tuples["pro"]].sport == five_tuples["sport"]:
            if five_tuples["dport"] == "":
                return True
            elif pkt[five_tuples["pro"]].dport == five_tuples["dport"]:
                return True

    return False


def pkt_replay_with_sleep(x, pktswithiface, gc):
    pkts_len = len(pktswithiface)
    y = 0
    for pkt, iface in pktswithiface:
        sendp(pkt, iface=iface, verbose=0)
        y += 1
        z = int(y / pkts_len * 100)
        print("\r[%-100s]第%d轮-%d%%" % (z * "#", x, z), end="")
        if gc["interval_time"] != "":
            time.sleep(gc["interval_time"])


def pkt_replay(pktswithiface, gc):
    print(f'总计循环{gc["loop_times"]}轮,每轮回访{len(pktswithiface)}个报文,开始回放报文:')
    if gc["loop_times"] == 0:
        x = 1
        while True:
            pkt_replay_with_sleep(x, pktswithiface, gc)
            x += 1
    else:
        for x in range(1, gc["loop_times"] + 1):
            pkt_replay_with_sleep(x, pktswithiface, gc)
    print("\n")


def port_str_to_int(config_dict):
    config_dict["sport"] = int(config_dict["sport"]) if config_dict["sport"] != "" else ""
    config_dict["dport"] = int(config_dict["dport"]) if config_dict["dport"] != "" else ""
    return config_dict


def config_load():
    conf = configparser.ConfigParser()
    conf.read("pktreplay.conf", encoding="utf-8")

    global_config = dict(conf.items('global_config'))
    client_config = dict(conf.items('client_config'))
    server_config = dict(conf.items('server_config'))

    global_config["loop_times"] = int(global_config["loop_times"])
    global_config["interval_time"] = float(global_config["interval_time"]) if global_config["interval_time"] != "" else ""
    client_config = port_str_to_int(client_config)
    server_config = port_str_to_int(server_config)
    print("配置文件读取成功!")
    return global_config, client_config, server_config


def remove_vlan(pkt):
    pkt_com_str = re.sub('chksum=\d+,', '', pkt.command())
    pkt_com_str = re.sub('/Dot1Q\(.*?\)', '', pkt_com_str)
    return eval(pkt_com_str)


def pkts_filter(gc, cc, sc):
    pkts = rdpcap(gc.get("filepath"))
    pkt_with_inf_list = []
    if gc["ip_version"] == "ipv4":
        if gc["vlan_remove"] == "1":
            for pkt in pkts:
                if is_correct_pkt(pkt, cc):
                    pkt = remove_vlan(pkt)
                    pkt_with_inf_list.append((pkt, cc["iface"]))
                elif is_correct_pkt(pkt, sc):
                    pkt = remove_vlan(pkt)
                    pkt_with_inf_list.append((pkt, sc["iface"]))
        else:
            for pkt in pkts:
                if is_correct_pkt(pkt, cc):
                    pkt_with_inf_list.append((pkt, cc["iface"]))
                elif is_correct_pkt(pkt, sc):
                    pkt_with_inf_list.append((pkt, sc["iface"]))

    elif gc["ip_version"] == "ipv6":
        if gc["vlan_remove"] == "1":
            for pkt in pkts:
                if is_correct_ipv6_pkt(pkt, cc):
                    pkt = remove_vlan(pkt)
                    pkt_with_inf_list.append((pkt, cc["iface"]))
                elif is_correct_ipv6_pkt(pkt, sc):
                    pkt = remove_vlan(pkt)
                    pkt_with_inf_list.append((pkt, sc["iface"]))
        else:
            for pkt in pkts:
                if is_correct_ipv6_pkt(pkt, cc):
                    pkt_with_inf_list.append((pkt, cc["iface"]))
                elif is_correct_ipv6_pkt(pkt, sc):
                    pkt_with_inf_list.append((pkt, sc["iface"]))
    print("数据包过滤完成,报文数量为:", len(pkt_with_inf_list))
    return pkt_with_inf_list


def start_replay():
    global pktswithiface
    global config_load_flag
    global pkts_filter_flag
    global gc, cc, sc
    gc, cc, sc = config_load()
    config_load_flag = True
    pktswithiface = pkts_filter(gc, cc, sc)
    pkts_filter_flag = True
    pkt_replay(pktswithiface, gc)


def start_replay_without_configload():
    global config_load_flag, pkts_filter_flag, pktswithiface, gc
    if config_load_flag and pkts_filter_flag:
        pkt_replay(pktswithiface, gc)
    elif not config_load_flag:
        print("还未加载过配置文件,请先执行步骤2!")
    elif not pkts_filter_flag:
        print("过滤报文出错,请再尝试执行一次步骤2!")


def main():
    while True:
        try:
            start_line = """\n1、生成初始配置文件\n2、加载配置文件并回放报文\n3、仅回放报文(需要加载过配置)\n请输入选择【1-3】:"""
            num = input(start_line)
            match num:
                case "1":
                    global config
                    create_configfile(config)
                case "2":
                    start_replay()
                case "3":
                    start_replay_without_configload()
                case _:
                    print("输入有误,请重新输入!")
        except UnicodeDecodeError:
            pass
        except (configparser.NoSectionError, ValueError, KeyError) as e:
            print("请检查配置文件:", e)
        except KeyboardInterrupt:
            print("\n强制停止回放!")
        except Exception as e:
            print("发生错误!",e)


if __name__ == '__main__':
    main()

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值