Python编写端口扫描器:从零开始
端口扫描作为网络安全中最基础且关键的技术之一,广泛应用于安全评估、漏洞挖掘和网络态势感知。本文将从理论到实战,逐步深入讲解如何使用Python语言从零实现一个高效灵活的端口扫描器。文中内容面向具备网络基础和Python编程能力的技术人员,力求在专业和实用之间达到平衡,帮助读者掌握端口扫描的核心原理与实战技巧。
一、概述
端口扫描器主要目的是探测目标主机上哪些TCP或UDP端口处于开放状态,从而推断服务情况和可能的漏洞入口。市面上已有诸如Nmap、Masscan等高性能扫描工具,但自研端口扫描器能够:
- 深入理解端口扫描背后逻辑与网络机制
- 灵活定制扫描策略,满足特殊需求
- 在红蓝对抗、安全渗透测试中具备更强定制化拓展能力
Python因其丰富的网络库、简洁语法及多线程、多协程支持,成为实现此类工具的理想语言。
本文将重点讲解基于Python标准库socket的TCP端口扫描技术,涵盖扫描原理、扫描方法、并发优化、扫描策略设计,以及核心代码示例。
二、原理分析
在深入编码前,有必要理清端口扫描的网络层基本原理及相关知识:
1. TCP端口扫描原理
TCP端口的开放与否通过三次握手过程判断:
连接建立成功(端口开放)
欲连接的目标端口若有服务监听,会响应TCP SYN-ACK包。扫描器发送TCP SYN包后收到SYN-ACK,即判定端口开放。连接被拒绝(端口关闭)
当目标端口无服务时,TCP会返回RST包表示拒绝,扫描器接收RST即判定端口关闭。无响应(端口过滤/丢包)
部分防火墙可能丢弃请求,导致无响应,此时端口状态判定为过滤或超时。
由于三次握手中SYN-ACK响应确认了端口状态,因此基于socket的TCP连接尝试,是最直接的端口扫描方法。
2. 扫描方式对比
全连接扫描(Connect Scan)
使用操作系统的connect()函数尝试三次握手完成。优点是稳定、易用,缺点是连接完成后还需立即关闭连接,效率较低且容易被监测。半开扫描(SYN Scan)
发送SYN包,若回应SYN-ACK则端口开放,然后发送RST包断开连接,避免完全握手。效率更高且隐蔽性较强,但实现复杂,需要原始套接字权限。
本文重点实现简洁高效的全连接扫描,兼顾实用性与广泛适用性(无需管理员权限)。
3. 并发扫描与性能
单线程扫描受网络延迟、系统调用阻塞限制,速度较慢。多线程、多进程、甚至异步协程可极大提升扫描效率,核心思路是并行发起多个连接请求,减少等待时间。
优化点包括:
- 合理设置超时时间,避免长时间阻塞
- 控制线程池大小,防止系统资源耗尽
- 结果异步回写或缓存,提升吞吐率
三、实战步骤
构建一个简单实用的Python端口扫描器,一般包含以下步骤:
1. 设计需求
- 支持目标主机IP或域名输入
- 支持指定扫描端口范围(如1-1024)
- 使用TCP全连接扫描检测端口状态
- 支持多线程并发扫描加速
- 扫描结果包含端口号及状态(开放/关闭/过滤)
- 提供基本异常捕获和日志输出
2. 环境准备
Python3环境,使用标准库:
socket:网络连接操作threading或concurrent.futures:多线程管理queue(可选):任务调度argparse(可选):参数解析
无需安装额外依赖。
3. 核心代码示例
以下是一个基于线程池的TCP全连接端口扫描器示例,代码简洁清晰,适合作为实战参考。
import socket
import concurrent.futures
def tcp_scan(target: str, port: int, timeout=1.0) -> (int, bool):
"""
扫描目标主机指定端口,判断端口是否开放。
:param target: 目标主机IP或域名
:param port: 端口号
:param timeout: 连接超时时间(秒)
:return: (端口号, 是否开放)
"""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(timeout)
result = sock.connect_ex((target, port))
if result == 0:
return port, True # 端口开放
else:
return port, False # 端口关闭或过滤
except socket.gaierror:
print(f"[错误] 无法解析域名: {target}")
return port, False
except socket.error as e:
print(f"[错误] 连接异常: {e}")
return port, False
def scan_ports(target: str, start_port: int, end_port: int, max_threads=100):
"""
多线程扫描指定端口范围。
:param target: 目标IP或域名
:param start_port: 起始端口
:param end_port: 结束端口
:param max_threads: 最大并发线程数
:return: None
"""
print(f"扫描目标:{target},端口范围:{start_port}-{end_port}")
open_ports = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_threads) as executor:
futures = []
for port in range(start_port, end_port + 1):
futures.append(executor.submit(tcp_scan, target, port))
for future in concurrent.futures.as_completed(futures):
port, is_open = future.result()
if is_open:
print(f"端口 {port} - 开放")
open_ports.append(port)
print(f"扫描完成,共发现开放端口: {len(open_ports)}个")
if open_ports:
print("开放端口列表:", open_ports)
if __name__ == "__main__":
target_host = input("请输入目标IP或域名: ").strip()
port_start = int(input("起始端口: "))
port_end = int(input("结束端口: "))
scan_ports(target_host, port_start, port_end)
4. 代码说明
tcp_scan函数使用socket.connect_ex尝试连接,返回0表示连接成功,端口开放。- 使用
ThreadPoolExecutor最大化利用多核、多线程,提升扫描效率。 - 设置合理的网络超时(1秒),避免因无响应端口造成阻塞。
- 代码结构清晰,易于扩展(如增加结果输出格式、日志记录、扫描策略等)。
四、防御建议
针对端口扫描攻击,网络防御应采取多层手段降低企业资产被扫描的风险:
1. 网络层防御
部署防火墙策略
严格限制非必要端口入站访问。采用白名单模式托管服务端口。防火墙应支持状态检测(Stateful Inspection)和异常流量过滤。速率限制
对单IP同一时间内多个连接尝试进行限制,防止暴力扫描。结合IDS/IPS进行异常流量报警。端口隐蔽性
移动默认服务端口(如SSH从22换成非标准端口),提升攻击成本。
2. 主机层防御
关闭无用端口和服务
及时清理系统中不必要的监听服务,减少攻击面。防范信息泄露
关闭自动响应(如TCP RST包)的详细信息,减少扫描者识别端口状态的精度。
3. 应用层防御
入侵检测系统(IDS)
部署专用IDS(如Snort、Suricata)监测异常扫描行为,及时报警。日志审计
加强对连接日志的审计,分析扫描相关日志,辅助溯源。动态端口和跳板技术
结合代理和动态端口