Python网络嗅探器无法捕获流量及跨平台适配问题咨询
Hey there, let's break down your two issues and fix the code step by step:
1. Why you can't capture WLAN traffic (only localhost ping works)
There are two critical reasons here:
- Wrong interface binding: Setting
host = "localhost"tells the sniffer to only listen on the loopback interface (lo), not your actual WLAN network card. To capture WLAN traffic, you need to bind to your WLAN adapter's IP address (like192.168.1.100) or use an empty string''to listen on all available interfaces. - Protocol restriction on Linux: Your code uses
IPPROTO_ICMPfor Linux, which limits the socket to only capturing ICMP packets (like ping). Windows usesIPPROTO_IPwhich lets it capture all IP-layer traffic—this is a key cross-platform difference you missed.
2. Why promiscuous mode is only handled on Windows
Windows and Linux have totally different mechanisms to enable traffic capture for all passing packets:
- On Windows,
ioctl(SIO_RCVALL, RCVALL_ON)enables a special "receive all" mode that captures all IP packets passing through the network card, acting like promiscuous mode for IP-layer traffic. - On Linux, you need two steps to capture all traffic:
- Enable promiscuous mode on the target network interface using
setsockoptwith theIFF_PROMISCflag. - Linux's raw
IPPROTO_IPsockets only capture packets destined for the local machine by default. Enabling promiscuous mode lets it see all packets passing through the adapter (as long as you have root privileges).
- Enable promiscuous mode on the target network interface using
The original code didn't implement any Linux-specific promiscuous mode logic, so it couldn't capture non-local traffic there.
Fixed Cross-Platform Code
Here's the adjusted code that works on both Linux and Windows to capture all IP-layer WLAN traffic:
import socket import os import struct from ctypes import * import fcntl # Required for Linux interface settings # Replace with your WLAN adapter's IP, or use '' to listen on all interfaces host = '' class IP(Structure): _fields_ = [ ("ihl", c_ubyte, 4), ("version", c_ubyte, 4), ("tos", c_ubyte), ("len", c_ushort), ("id", c_ushort), ("offset", c_ushort), ("ttl", c_ubyte), ("protocol_num", c_ubyte), ("sum", c_ushort), ("src", c_uint), ("dst", c_uint), ] def __new__(self, socket_buffer=None): return self.from_buffer_copy(socket_buffer) def __init__(self, socket_buffer=None): self.protocol_map = {1 : "ICMP", 6 : "TCP", 17 : "UDP"} self.src_address = socket.inet_ntoa(struct.pack("<L", self.src)) self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst)) try: self.protocol = self.protocol_map[self.protocol_num] except: self.protocol = str(self.protocol_num) # Use IPPROTO_IP on both platforms to capture all IP packets socket_protocol = socket.IPPROTO_IP try: sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) sniffer.bind((host, 0)) print("[*] Starting packet capture...") sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # Handle promiscuous mode per platform if os.name == "nt": sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) else: # Linux: Enable promiscuous mode on the WLAN interface # Replace 'wlan0' with your actual WLAN interface (check with `ip link show`) interface = b'wlan0' SIOCGIFFLAGS = 0x8913 SIOCSIFFLAGS = 0x8914 # Get current interface flags ifreq = struct.pack('16sH', interface, 0) flags = fcntl.ioctl(sniffer.fileno(), SIOCGIFFLAGS, ifreq) # Set promiscuous mode flag new_flags = struct.unpack('16sH', flags)[1] | 0x100 # IFF_PROMISC fcntl.ioctl(sniffer.fileno(), SIOCSIFFLAGS, struct.pack('16sH', interface, new_flags)) print(f"[*] Enabled promiscuous mode on {interface.decode()}") while True: raw_buffer = sniffer.recvfrom(65565)[0] ip_header = IP(raw_buffer[0:20]) print(f"Protocol: {ip_header.protocol} {ip_header.src_address} -> {ip_header.dst_address}") except PermissionError: print("\n[*] Error: You need root/administrator privileges to run this!") except KeyboardInterrupt: print("\n[*] Stopping capture and cleaning up...") # Disable promiscuous mode on exit if os.name == "nt": sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) else: interface = b'wlan0' SIOCGIFFLAGS = 0x8913 SIOCSIFFLAGS = 0x8914 ifreq = struct.pack('16sH', interface, 0) flags = fcntl.ioctl(sniffer.fileno(), SIOCGIFFLAGS, ifreq) new_flags = struct.unpack('16sH', flags)[1] & ~0x100 # Clear IFF_PROMISC fcntl.ioctl(sniffer.fileno(), SIOCSIFFLAGS, struct.pack('16sH', interface, new_flags)) except Exception as err: print(f"\n[*] Error: {err}") # Cleanup on unexpected error if os.name == "nt": sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) else: interface = b'wlan0' SIOCGIFFLAGS = 0x8913 SIOCSIFFLAGS = 0x8914 ifreq = struct.pack('16sH', interface, 0) flags = fcntl.ioctl(sniffer.fileno(), SIOCGIFFLAGS, ifreq) new_flags = struct.unpack('16sH', flags)[1] & ~0x100 fcntl.ioctl(sniffer.fileno(), SIOCSIFFLAGS, struct.pack('16sH', interface, new_flags))
Important Notes
- Permissions: On Linux, run this script with
sudo; on Windows, run your terminal as Administrator. Raw sockets require elevated privileges. - WLAN Interface: On Linux, replace
wlan0with your actual WLAN interface name (find it withip link show). - Capture Scope: Linux raw sockets still won't capture packets the kernel filters out, but this setup will capture most traffic passing through your WLAN adapter.
内容的提问来源于stack exchange,提问作者user12102400
相关产品推荐
相关产品推荐

