简单的发包工具——网络协议编辑器(python+scapy+pycharm)

一、实现的功能

    基于python+scapy设计协议编辑器,基于Tkinter的Python  GUI界面设计。实现了MAC、ARP、IP 、TCP、UDP协议的编辑与发送,并且支持生成协议字段的默认值,支持用户输入协议字段值,发包前对协议字段的合理性进行检查,发包前自动计算并显示校验和,支持单次发包、多次发包,支持连续发包,连续发包时可随时停止和计算并显示数据包发送速度的功能。

注:

程序源代码:https://download.csdn.net/download/wmrem/10439779

运行前需要先安装第三方库scapy,安装方法:https://blog.csdn.net/wmrem/article/details/80004819

二、主要函数说明——IP包的编辑与发送为例

1.创建IP包编辑器界面,为每个按钮绑定单击响应时间。
def create_ip_sender():
   1.设置协议编辑器的界面
    ip_fields = 'IP协议的版本:',…… , '目的IP地址:'
    entries = create_protocol_editor(protocol_editor_panedwindow, ip_fields)
    2.创建发送,默认值,清空按钮,并绑定功能
    send_packet_button, reset_button, default_packet_button = create_bottom_buttons(protocol_editor_panedwindow)
    3.为"回车键"的Press事件编写事件响应代码,发送IP包
    tk.bind('', (lambda event: send_ip_packet(entries, send_packet_button)))  # 代表回车键
    4.为"发送"、默认值、按钮的单击事件编写事件响应代码,发送IP包
    send_packet_button.bind('', (
        lambda event: send_ip_packet(entries, send_packet_button)))

2. 在协议字段编辑框中填入默认IP包的字段值,为IP数据包的发送做好准备
def create_default_ip_packet(entries):
    1.清空各字段的值
    clear_protocol_editor(entries)
    2.创建默认的IP数据包,并将其各字段的值填入协议编辑器的对应的文本框中
    default_ip_packet = IP()
    entries[0].insert(0, int(default_ip_packet.version))

    …

3.获取协议编辑器文本框的值,创建对应的数据包并开启一个线程用于连续发送数据包。
def send_ip_packet(entries, send_packet_button):
    if 按钮为发送时:
1. 从协议编辑框中获取要发送的数据包的各个字段的值
        ip_version = int(entries[0].get())

        2.用获取的数据包的各字段的值创建相应的数据包
        packet_to_send = IP(version=ip_version,……,src=ip_src, dst=ip_dst)
        3.开一个线程用于连续发送数据包,并启动
        t = threading.Thread(target=send_packet, args=(packet_to_send,))
        t.start()
        4.使协议导航树不可用
        toggle_protocols_tree_state()
        send_packet_button['text'] = '停止'
    else 按钮为停止时:
        5.按钮为停止时可以终止数据包发送线程
        stop_sending.set()
        6.恢复协议导航树可用
        toggle_protocols_tree_state()

        send_packet_button['text'] = '发送'

4.发送数据包的线程函数,并计算数据包发送速度
def send_packet(packet_to_send):
    stop_sending.clear()
    1.计算待发送数据包的长度(用于计算发送速度)
    packet_size = len(packet_to_send)
    2.推导数据包的协议类型
    3.计算发包开始发送时间点
    begin_time = datetime.now()
    4.连续发送数据包直到进程关闭
    while 进程没有关闭:
        if 发送Ether:
            sendp(packet_to_send, verbose=0)  # verbose=0,不在控制回显'Sent 1 packets'.
        else:
            send(packet_to_send, verbose=0)
        5.计算发送的总字节数
        total_bytes = packet_size * n
        6.计算发包用的总时间 
        total_time = (end_time - begin_time).total_seconds()
7.计算发包的速度
        bytes_per_second = total_bytes / total_time / 1024

三、基础知识准备

1.构造数据包,发送数据包——“/"运算符数据包的拼装,send()用于3层发包,可直接发送IP数据包,自动加以太帧;只发送以太帧使用sendp()。

简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第1张图片简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第2张图片

2.计算校验和——IP包的校验和默认值为None,在IP包被发送时,其校验和被被自动计算并填充在IP包中
以下方式可以在发包前计算IP包的校验和,直接用packet.show2()命令也可以显示校验和。raw(packet)将数据包的内容转换为字节。

简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第3张图片 简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第4张图片

3.查看数据包的字段值(使用ls()),查看某层协议数据包对象支持的函数(使用help())

简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第5张图片简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第6张图片

4.ls()列出scapy支持的所有数据包,协议;lsc()列出scapy支持的全部命令;conf列出scapy的当前配置。

四、主要功能实现代码——以IP包为例

1.创建IP包编辑器的界面

# 创建协议字段编辑区
def create_protocol_editor(root, field_names):
    """
    创建协议字段编辑区
    :param root: 协议编辑区
    :param field_names: 协议字段名列表
    :return: 协议字段编辑框列表
    """
    entries = []
    for field in field_names:
        row = Frame(root)
        label = Label(row, width=15, text=field, anchor='e')
        entry = Entry(row, font=('Courier', '12', 'bold'), state='normal')  # 设置编辑框为等宽字体
        row.pack(side=TOP, fill=X, padx=5, pady=5)
        label.pack(side=LEFT)
        entry.pack(side=RIGHT, expand=YES, fill=X)
        entries.append(entry)
    return entries
def create_ip_sender():
    """
    创建IP包编辑器
    :return: None
    """
    # IP帧编辑区
    ip_fields = 'IP协议的版本:', '首部长度(5-15):', '区分服务:', '总长度:', '标识:', '标志(0-2)DF,MF:', \
                '片偏移:', '生存时间:', '协议(数据部分):', '首部校验和:', '源IP地址:', '目的IP地址:'
    entries = create_protocol_editor(protocol_editor_panedwindow, ip_fields)
    send_packet_button, reset_button, default_packet_button = create_bottom_buttons(protocol_editor_panedwindow)
    # 为"回车键"的Press事件编写事件响应代码,发送ARP包
    tk.bind('', (lambda event: send_ip_packet(entries, send_packet_button)))  # 代表回车键
    # 为"发送"按钮的单击事件编写事件响应代码,发送ARP包
    send_packet_button.bind('', (
        lambda event: send_ip_packet(entries, send_packet_button)))  # 代表鼠标左键单击
    # 为"清空"按钮的单击事件编写事件响应代码,清空协议字段编辑框
    reset_button.bind('', (lambda event: clear_protocol_editor(entries)))
    # 为"默认值"按钮的单击事件编写事件响应代码,在协议字段编辑框填入ARP包字段的默认值
    default_packet_button.bind('', (lambda event: create_default_ip_packet(entries)))

2.在协议字段编辑框中填入默认IP包的字段值,填入前需要先清空当前值。

def clear_protocol_editor(entries):
    """
    清空协议编辑器的当前值
    :param entries: 协议字段编辑框列表
    :return: None
    """
    for entry in entries:
        # 如果有只读Entry,也要清空它的当前值
        state = entry['state']
        entry['state'] = 'normal'
        entry.delete(0, END)
        entry['state'] = state
# 当前网卡的默认网关
default_gateway = [a for a in os.popen('route print').readlines() if ' 0.0.0.0 ' in a][0].split()[-3]
def create_default_ip_packet(entries):
    """
        在协议字段编辑框中填入默认IP包的字段值
         :param entries: 协议字段编辑框列表
         :return: None
         """
    clear_protocol_editor(entries)
    default_ip_packet = IP()
    entries[0].insert(0, int(default_ip_packet.version))
    entries[1].insert(0, 5)
    entries[3].insert(0, 20)
    entries[2].insert(0, hex(default_ip_packet.tos))
    entries[4].insert(0, int(default_ip_packet.id))
    entries[5].insert(0, int(default_ip_packet.flags))
    entries[6].insert(0, int(default_ip_packet.frag))
    entries[7].insert(0, int(default_ip_packet.ttl))
    entries[8].insert(0, int(default_ip_packet.proto))
    entries[9]['state'] = NORMAL # 可操作
    entries[9].insert(0, "单机发送时自动计算")
    entries[9]['state'] = DISABLED  # 不可操作
    # 目标IP地址设成本地默认网关
    entries[11].insert(0, default_gateway)
    default_ip_packet = IP(dst=entries[11].get())#可以省略
    entries[10].insert(0, default_ip_packet.src)

3.发送IP包

def send_ip_packet(entries, send_packet_button):
    """
    发IP包
    :param entries:
    :param send_packet_button:
    :return:
    """
    if send_packet_button['text'] == '发送':
        ip_version = int(entries[0].get())
        ip_ihl = int(entries[1].get())
        ip_tos = int(entries[2].get(), 16)
        ip_len = int(entries[3].get())
        ip_id = int(entries[4].get())
        ip_flags = int(entries[5].get())
        ip_frag = int(entries[6].get())
        ip_ttl = int(entries[7].get())
        ip_proto = int(entries[8].get())
        ip_src = entries[10].get()
        ip_dst = entries[11].get()
        # ip_options = entries[12].get()
        packet_to_send = IP(version=ip_version, ihl=ip_ihl, tos=ip_tos, len=ip_len, id=ip_id,
                            frag=ip_frag, flags=ip_flags, ttl=ip_ttl, proto=ip_proto, src=ip_src, dst=ip_dst)
        packet_to_send = IP(raw(packet_to_send))
        entries[9]['state'] = NORMAL  # 重新激活
        entries[9].delete(0, END)
        entries[9].insert(0, hex(packet_to_send.chksum))
        entries[9]['state'] = DISABLED  # 不可操作
        # 开一个线程用于连续发送数据包
        t = threading.Thread(target=send_packet, args=(packet_to_send,))
        t.setDaemon(True)
        t.start()
        # 使协议导航树不可用
        toggle_protocols_tree_state()
        send_packet_button['text'] = '停止'
    else:
        # 终止数据包发送线程
        stop_sending.set()
        # 恢复协议导航树可用
        toggle_protocols_tree_state()
        send_packet_button['text'] = '发送'

4.线程中运行的用于发送数据包的函数,可以计算发包速度

def send_packet(packet_to_send):
    """
    在我们给出的发包程序中,如果电脑速度太快,send_packet函数中的send(...)函数执行前后,
    datetetime返回的begin_time和end_time可能是相同的,结果会报除零错误,所以,send_packet函数应该做修改

    用于发送数据包的线程函数,持续发送数据包
    :type packet_to_send: 待发送的数据包
    """
    # print(packet.show(dump=True))
    # 对发送的数据包次数进行计数,用于计算发送速度
    n = 0
    stop_sending.clear()
    # 待发送数据包的长度(用于计算发送速度)
    packet_size = len(packet_to_send)
    # 推导数据包的协议类型
    proto_names = ['TCP', 'UDP', 'ICMP', 'IP', 'ARP', 'Ether', 'Unknown']
    packet_proto = ''
    for pn in proto_names:
        if pn in packet_to_send:
            packet_proto = pn
            break
    # 开始发送时间点
    begin_time = datetime.now()
    while not stop_sending.is_set():
        if isinstance(packet_to_send, Ether):
            sendp(packet_to_send, verbose=0)  # verbose=0,不在控制回显'Sent 1 packets'.
        else:
            send(packet_to_send, verbose=0)
        n += 1
        end_time = datetime.now()
        total_bytes = packet_size * n
        #修改
        total_time = (end_time - begin_time).total_seconds()
        if total_time == 0:
            total_time = 2.23E-308  # 当begin_time和end_time相等时,将total_time设为IEEE 745标准中规定的最小浮点数
        bytes_per_second = total_bytes / total_time / 1024
        # bytes_per_second = total_bytes / ((end_time - begin_time).total_seconds()) / 1024
        status_bar.set('已经发送了%d个%s数据包, 已经发送了%d个字节,发送速率: %0.2fK字节/秒',
                       n, packet_proto, total_bytes, bytes_per_second)

五、运行结果

简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第7张图片 简单的发包工具——网络协议编辑器(python+scapy+pycharm)_第8张图片

你可能感兴趣的:(Python,计算机网络编程,scapy创建并发送数据包,单线程发包,网络协议编辑器,python+pycharm)