资讯动态

端口扫描工具的实现:从零构建一个并发 TCP 端口扫描器

发布时间:2026/10/23 10:38:46 来源:尧图企业网站定制
端口扫描工具的实现从零构建一个并发 TCP 端口扫描器端口扫描是网络探测与安全审计的基石。本文不依赖现成的 Nmap而是用 Python 从零实现一个并发 TCP 端口扫描工具覆盖原理、架构、并发调度、服务指纹识别与可运行的完整代码并配有原理与架构图。目录端口扫描是什么原理基础端口与 TCP 三次握手常见的扫描方式对比工具架构设计核心实现Python 完整代码扫描主流程性能与并发优化进阶方向总结1. 端口扫描是什么端口Port是传输层用来区分不同网络服务的逻辑编号范围 0–65535。一台主机上可能同时运行 Web、SSH、数据库等多个服务它们各自绑定一个端口。端口扫描的目的就是探测目标主机上哪些端口处于开放状态从而推断其上运行了哪些服务常用于安全审计检查自身网络对外暴露了哪些不必要的服务缩小攻击面。渗透测试作为信息收集的第一步为后续漏洞探测定位目标。运维巡检确认服务是否正常监听、端口是否被防火墙误拦。网络资产盘点摸清网段内主机与开放端口分布。一个基本的端口扫描器本质上是对目标每个端口发起一次连接尝试并根据响应判定其状态。2. 原理基础端口与 TCP 三次握手TCP 是面向连接的协议建立连接需要经过三次握手Three-way Handshake。端口扫描正是利用握手过程来判断端口状态客户端发送SYN报文请求建立连接服务端若在该端口监听会回复SYN ACK表示同意连接客户端再回复ACK连接正式建立。借助这一过程我们可以在应用层通过一次connect()调用的结果来判定端口状态——这就是TCP Connect 扫描的核心原理判定的核心connect()成功返回 → 端口开放连接被拒绝或超时 → 端口关闭Closed或被防火墙过滤Filtered。3. 常见的扫描方式对比不同扫描方式在是否完成完整握手、是否留下连接痕迹、是否需要系统权限上各有取舍扫描方式原理完成握手隐蔽性权限要求适用场景TCP Connect完整三次握手是低留下完整连接日志普通用户通用、实现最简单本文采用SYN 半开扫描只发 SYN收到 SYNACK 即判定开放不发 ACK否较高需要 root / RAW Socket快速探测、减少连接痕迹FIN / NULL / Xmas发送异常标志位报文根据响应差异判定否较高需要 root穿透部分简单防火墙对多数系统不可靠ACK 扫描只发 ACK探测防火墙过滤规则否中需要 root探测防火墙状态无法判定端口是否开放UDP 扫描发送 UDP 报文依据 ICMP 端口不可达响应否中部分需 root探测 UDP 服务DNS、SNMP 等本文采用 TCP Connect 扫描——它不需要特殊系统权限实现清晰足以说明端口扫描工具的完整工程结构半开扫描等进阶思路放在第 8 节。4. 工具架构设计把端口扫描器拆成清晰的模块便于维护与扩展。整体分为四层配置 / CLI 层解析命令行参数确定目标主机、端口范围、超时与并发数。扫描引擎核心调度逻辑负责并发分配端口探测任务控制并发度与超时。网络交互层真正执行TCP Connect探测、读取服务 Banner 的底层操作。结果处理对探测结果做服务指纹识别与格式化输出控制台 / JSON 文件。分层的好处是换一种探测方式如 SYN 半开只需替换网络交互层调度与输出逻辑完全复用。5. 核心实现Python 完整代码5.1 技术选型语言Python 3标准库即可零第三方依赖。并发concurrent.futures.ThreadPoolExecutor线程池简单可靠。目标解析ipaddress库处理 IP / 网段socket解析主机名。超时socket.settimeout()防止单端口探测卡死。5.2 完整代码#!/usr/bin/env python3 port_scanner.py —— 一个并发 TCP 端口扫描器 用法: python port_scanner.py 192.168.1.1 python port_scanner.py 192.168.1.0/24 --ports 1-1000 python port_scanner.py example.com --ports 22,80,443,3306 --timeout 2 --json result.json importargparseimportconcurrent.futuresimportipaddressimportjsonimportsocket# 常见服务端口 - 服务名映射用于快速标注已知端口COMMON_PORTS{21:FTP,22:SSH,23:Telnet,25:SMTP,53:DNS,80:HTTP,110:POP3,135:RPC,139:NetBIOS,143:IMAP,443:HTTPS,445:SMB,3306:MySQL,3389:RDP,5432:PostgreSQL,6379:Redis,8080:HTTP-Alt,9200:Elasticsearch,}defparse_targets(targets):解析目标支持 IP、IP/掩码网段、主机名。返回 IP 字符串列表。result[]fortintargets:if/int:# 网段如 192.168.1.0/24result.extend([str(h)forhinipaddress.ip_network(t,strictFalse).hosts()])else:try:result.append(str(ipaddress.ip_address(t)))# 单个 IPexceptValueError:result.append(socket.gethostbyname(t))# 主机名 - IPreturnresultdefparse_ports(ports_spec):解析端口范围支持 1-1000、22,80,443 或两者混合。portsset()forpartinports_spec.split(,):partpart.strip()if-inpart:lo,hipart.split(-)ports.update(range(int(lo),int(hi)1))else:ports.add(int(part))returnsorted(ports)defgrab_banner(sock,timeout):读取服务 Banner用于指纹识别。sock.settimeout(timeout)try:sock.sendall(b\r\n)returnsock.recv(256).decode(utf-8,errorsignore).strip()exceptException:returnNonedefprobe(host,port,timeout):对单个端口执行 TCP Connect 探测返回结构化结果。socksocket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.settimeout(timeout)result{port:port,state:closed,service:None,banner:None}try:ifsock.connect_ex((host,port))0:result[state]openresult[service]COMMON_PORTS.get(port,unknown)result[banner]grab_banner(sock,timeout)sock.close()exceptsocket.timeout:result[state]filtered# 超时可能被防火墙丢弃sock.close()exceptException:sock.close()returnresultdefscan_host(host,ports,timeout,workers):并发扫描单个主机的一组端口返回该主机的开放端口列表。open_ports[]withconcurrent.futures.ThreadPoolExecutor(max_workersworkers)asex:futures{ex.submit(probe,host,p,timeout):pforpinports}forfutinconcurrent.futures.as_completed(futures):rfut.result()ifr[state]open:open_ports.append(r)returnhost,sorted(open_ports,keylambdax:x[port])defmain():parserargparse.ArgumentParser(description并发 TCP 端口扫描器)parser.add_argument(targets,nargs,help目标IP / 网段 / 主机名)parser.add_argument(--ports,default1-1024,help端口范围如 1-1000、22,80,443)parser.add_argument(--timeout,typefloat,default1.0,help单端口连接超时秒)parser.add_argument(--workers,typeint,default100,help并发线程数)parser.add_argument(--json,help将结果输出到 JSON 文件)argsparser.parse_args()ipsparse_targets(args.targets)portsparse_ports(args.ports)print(f目标主机数:{len(ips)}端口数:{len(ports)}并发线程数:{args.workers})all_results{}foripinips:host,opensscan_host(ip,ports,args.timeout,args.workers)all_results[host]opensforrinopens:linef [OPEN]{host}:{r[port]:5}{r[service]}ifr[banner]:linef banner{r[banner][:60]}print(line)ifargs.json:withopen(args.json,w,encodingutf-8)asf:json.dump(all_results,f,ensure_asciiFalse,indent2)print(f结果已写入{args.json})if__name____main__:main()5.3 关键实现要点connect_ex()vsconnect()connect_ex()用返回码而非抛异常表示结果0即连接成功免去异常捕获的繁琐适合高频探测。并发模型ThreadPoolExecutor把每个端口探测任务丢进线程池as_completed一边收结果一边处理主线程不阻塞。超时兜底settimeout()保证单端口最多等待timeout秒避免因丢包导致整轮扫描卡死超时统一归为filtered。Banner 识别连接成功后发送一个空行或协议触发指令读取服务返回的第一段文本作为指纹线索如 SSH 会回显SSH-2.0-OpenSSH_...。结果结构化每条记录统一为{port, state, service, banner}便于后续序列化成 JSON 或接入其他系统。6. 扫描主流程工具的完整执行流程自顶向下推进输入目标用户给定 IP、网段或主机名及端口范围目标解析把目标拆成 IP 列表 端口列表并发调度由线程池按并发度分发探测任务端口探测对每个端口执行 TCP Connect结果判定根据响应归为开放 / 关闭 / 过滤服务指纹识别对开放端口抓 Banner结果输出控制台打印或写入 JSON 文件。每一步职责单一、边界清晰新增功能如存活主机探测、速率控制可在对应步骤扩展。7. 性能与并发优化端口扫描是典型的 I/O 密集任务——大多数时间都花在等待网络响应上CPU 几乎不参与。因此并发是提升扫描速度的关键。以扫描 1000 个端口为例不同并发方式的耗时差异巨大单线程串行逐个等待1000 个端口即使全部秒回也要耗时数十秒慢在网络往返的串行等待上多线程ThreadPoolExecutor用线程池同时发起数百个连接速度提升一个数量级asyncio 协程单线程内用事件循环管理海量 socket 连接开销更低在大规模扫描时进一步缩短耗时。并发数如何取舍并发过低浪费带宽扫描慢并发过高触发目标防火墙限速、占用本地文件描述符socket资源耗尽甚至造成网络拥塞。生产工具通常支持可配置并发数 速率限制rate limiting并加入随机延迟降低被检测概率。8. 进阶方向方向说明SYN 半开扫描用 RAW Socket 构造并发送SYN报文收到SYNACK判定开放、RST判定关闭不完成握手更隐蔽高效需要 root 权限跨平台需用scapy或底层套接字。服务指纹库将 Banner 与已知服务特征库比对甚至主动发送多种探测载荷识别更精确的服务与版本。全端口 / 分布式扫描 1–65535 全端口时配合多主机/多线程分布式调度缩短整体时长。异步 I/O用asyncio替换线程池管理上万并发连接降低线程切换与内存开销。防御对抗增加探测随机化、低慢速、报文伪造等降低被入侵检测系统IDS标记的概率——需在授权范围内使用。⚠️合规提醒对未经授权的目标进行端口扫描属于探测行为在多数地区可能违反法律法规。请务必只在自己拥有或有明确书面授权的网络范围内使用或用于自身资产的合规巡检。9. 总结本文从端口扫描的原理出发梳理了常见扫描方式并用 Python 从零实现了一个结构清晰、可运行的并发 TCP 端口扫描器。核心要点原理利用 TCP 三次握手的connect()结果判定端口开放/关闭/过滤架构配置层、扫描引擎、网络交互层、结果处理四层分离便于替换探测方式并发I/O 密集任务用线程池/协程显著提速需平衡并发度与稳定性指纹识别抓取 Banner 推断服务为安全审计提供更丰富的信息。在此基础上可以继续向 SYN 半开扫描、服务指纹库、异步 I/O 等方向演进把它打磨成功能完整的网络探测工具。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑