如何使用Python实现UDP端口开放状态检测?
问题原因分析
你原来的代码存在两个核心问题:
- 普通
SOCK_DGRAM类型的UDP套接字无法接收关联的ICMP错误报文,所以你在Wireshark能抓到的端口不可达报文,不会传递给普通UDP套接字,导致除了端口开放且主动返回响应的情况外,其余请求都会触发TimeoutError,你的逻辑错误地把所有超时都判定为端口开放,同时永远触发不到兜底的except分支。 - 你尝试新增原始ICMP套接字的思路是对的,但缺少了ICMP报文解析、端口匹配的逻辑,也没有处理原始套接字的权限要求,所以无法得到正确结果。
可行实现方案
前置要求
运行使用原始套接字的代码需要满足权限要求:
- Linux系统需要以root身份运行
- Windows系统需要以管理员身份启动运行终端
完整代码实现
import socket import struct def udp_connection(ip, port, timeout=2): # 先创建UDP套接字发探测包 udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_sock.settimeout(timeout) # 创建原始ICMP套接字收错误报文 icmp_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) icmp_sock.settimeout(timeout) try: # 发送UDP探测包 udp_sock.sendto(b'test', (ip, port)) # 先尝试收UDP响应,收到说明端口明确开放 data, addr = udp_sock.recvfrom(1024) return f"[+] UDP Port Open: {port} 收到响应: {data}\n" except TimeoutError: # UDP收不到响应,尝试收ICMP报文判断 try: icmp_data, addr = icmp_sock.recvfrom(1024) # 解析ICMP包头:IP头前20字节,之后是ICMP头 icmp_header = icmp_data[20:28] icmp_type, icmp_code, _, _, _ = struct.unpack('!BBHHH', icmp_header) # ICMP类型3是目标不可达,代码3是端口不可达 if icmp_type == 3 and icmp_code == 3: # 解析ICMP载荷里的原始UDP头,确认是对应我们探测的端口的报错 original_udp_header = icmp_data[48:56] original_dst_port = struct.unpack('!H', original_udp_header[2:4])[0] if original_dst_port == port: return f"[-] UDP Port Closed: {port} 收到端口不可达ICMP报文\n" # 其他ICMP报文或者端口不匹配,视为无明确关闭信号,判定为开放|过滤 return f"[+] UDP Port Open|Filtered: {port} 无明确关闭响应\n" except TimeoutError: # 既收不到UDP响应也收不到ICMP报错,判定为开放|过滤 return f"[+] UDP Port Open|Filtered: {port} 超时无响应\n" finally: udp_sock.close() icmp_sock.close()
注意事项
- 不要直接把超时等同于端口开放,UDP端口无响应的情况有可能是被防火墙过滤,所以标注为
Open|Filtered更严谨 - 批量扫描的时候建议控制发包速率,避免被目标网络的安全设备拦截
- 不同操作系统对原始套接字的处理存在细微差异,如果Windows下收不到ICMP报文,可以尝试将ICMP套接字绑定到
0.0.0.0的任意端口。
内容的提问来源于stack exchange,提问作者Lima Victor
相关产品推荐
相关产品推荐

