
简介这是一套面向网络安全初学者与渗透测试从业者的Python信息收集工具源码集成端口扫描、子域名探测、敏感文件识别、WHOIS查询、服务器指纹识别等核心功能并基于PyQt5构建图形界面显著降低使用门槛。资源共39个文件包含14个核心Python脚本如port_scan.py、subdomain.py、whois.py、6个配置与字典文件如conf.ini、dir_brute_dict.txt、2个UI界面资源.ui及编译后的.py、以及说明文档与图标素材整体压缩包仅310KB轻量易部署。已有768人学习下载适合用于教学演示、CTF靶机信息侦察实践或安全工具二次开发。读者可直接运行collect_infor_tools.py启动GUI深入理解各模块调用逻辑与网络协议交互细节掌握从目标发现到服务识别的完整信息收集链路。1. 项目概述一个安全从业者的工具箱自研之路在安全测试和日常运维工作中信息收集和初步渗透是绕不开的环节。市面上工具虽多但要么功能分散需要频繁切换要么过于重型不适合快速排查要么就是闭源黑盒用起来心里没底。几年前我决定自己动手用Python打造一个集信息收集与基础渗透测试功能于一体的图形化工具。这个决定背后不仅仅是“造轮子”的冲动更多是源于实际工作中的痛点我需要一个能快速对目标进行“体检”的工具它要足够轻量、完全透明、并且能根据我的习惯和需求灵活定制。最终这个工具不仅成了我日常工作的得力助手其开发过程本身也是一次对Python网络编程、多线程、GUI设计以及安全协议理解的深度实践。今天我就把这个项目的核心思路、关键代码实现以及那些“踩坑”得来的经验毫无保留地分享出来。无论你是想学习如何构建一个实用的安全工具还是希望深入理解Python在这些场景下的应用这篇文章都能给你带来直接的参考价值。这个工具的核心定位是“辅助”与“效率提升”并非替代专业的大型渗透测试框架。它专注于解决几个常见且高频的需求快速获取目标的子域名、开放端口、服务指纹进行基础的目录扫描和漏洞探测如弱口令、特定CMS漏洞并将所有结果通过一个直观的图形界面进行管理和查看。整个项目采用模块化设计每个功能都是一个独立的Python模块通过主控的GUI进行调度和展示。这样做的好处是功能增删改非常灵活后续维护和扩展也清晰明了。2. 核心架构与设计思路拆解2.1 为什么选择Python与Tkinter选择Python作为开发语言几乎是必然的。在安全领域Python拥有无与伦比的生态库支持比如用于网络请求的requests、用于解析HTML的BeautifulSoup、用于处理DNS的dnspython以及进行网络扫描的scapy基础扫描或socket直接操作等。这让我们能快速实现核心功能而无需从零开始造轮子。至于图形界面GUI框架的选择我经历了几个阶段的考量。早期尝试过PyQt5它功能强大、界面美观但打包后的体积较大且对于初学者而言学习曲线稍陡。后来也考虑过Kivy或Web方案如FlaskElectron但前者更适合移动端后者则引入了前后端分离的复杂度对于一个旨在“快速启动、用完即走”的桌面工具来说略显笨重。最终我选择了Python标准库自带的Tkinter。原因有三第一零依赖无需用户额外安装任何GUI库打包部署极其简单第二足够满足工具类应用的需求按钮、文本框、标签页、进度条等组件一应俱全第三学习成本低能够让我更专注于业务逻辑而非界面细节。当然Tkinter的默认样式比较“复古”但通过ttk模块和一些简单的样式配置完全可以做出清晰、易用的界面。2.2 模块化设计高内聚低耦合整个工具的架构遵循“高内聚低耦合”的原则。我将核心功能拆分为以下几个独立的模块信息收集模块(collector)负责被动和主动的信息搜集。包含子域名枚举利用搜索引擎接口、DNS字典爆破、证书透明度日志查询等、端口扫描TCP Connect扫描、SYN半开扫描的简化实现、服务与版本探测。Web探测模块(web_detector)专注于Web层面。包含目录/文件扫描基于常见字典、基础指纹识别通过HTTP头、特定文件、关键字识别CMS或框架、基础漏洞检测如检查默认后台、特定版本的已知漏洞等。渗透辅助模块(penetration_helper)实现一些基础的渗透测试功能例如针对常见服务SSH, FTP, MySQL, Redis等的弱口令检测以及简单的Payload生成器。核心引擎模块(engine)提供公共工具函数如HTTP请求会话管理保持Cookie、处理代理、多线程/协程池封装、日志记录、配置文件读取等。所有功能模块都依赖这个引擎。图形界面模块(gui_main)基于Tkinter构建的用户界面。它不包含复杂的业务逻辑只负责接收用户输入、调用上述功能模块、并实时展示进度和结果。这种设计的好处非常明显。例如当我想增加一个“从Shodan API获取信息”的新功能时我只需要在collector模块中新增一个类或函数然后在GUI的相应菜单里添加一个调用入口即可完全不会影响其他模块。测试也可以针对单个模块进行提升了代码的可维护性。注意模块化初期需要规划好模块间的通信接口和数据格式。我定义了一个统一的ScanResult类来封装每次扫描的结果目标、模块名、状态、详情、风险等级这样GUI展示层只需要处理这一种数据格式大大简化了前端逻辑。3. 关键功能模块的代码实现与解析3.1 信息收集模块子域名枚举的三种策略子域名枚举是信息收集的起点。我实现了三种常见的策略并允许用户在GUI中勾选使用。策略一字典爆破这是最直接的方法加载一个包含常见子域名的字典文件如subdomains_top1000.txt然后拼接目标主域名尝试解析其A记录或CNAME记录。# 示例代码片段 - 字典爆破核心逻辑 import dns.resolver def brute_subdomains(domain, wordlist_path): found [] with open(wordlist_path, r) as f: subdomains [line.strip() for line in f if line.strip()] resolver dns.resolver.Resolver() resolver.timeout 2 resolver.lifetime 2 for sub in subdomains: target f{sub}.{domain} try: answers resolver.resolve(target, A) # 解析成功说明子域名存在 ips [str(rdata) for rdata in answers] found.append((target, ips)) print(f[] Found: {target} - {ips}) except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout): # 解析失败子域名可能不存在 continue return found策略二搜索引擎公开信息利用公开的搜索引擎语法。例如使用requests模拟访问Google搜索site:*.example.com然后从返回的HTML中提取子域名。这种方法受搜索引擎反爬限制需要谨慎使用并添加合理的延时和User-Agent轮换。策略三证书透明度日志查询这是近年来非常有效的一种被动信息收集方式。证书颁发机构CA会将颁发的SSL/TLS证书记录到公开的CT日志中。通过查询这些日志例如使用crt.sh的API可以获取到所有为该域名及其子域名颁发过证书的记录。import requests import re def query_crt_sh(domain): subdomains set() url fhttps://crt.sh/?q%.{domain}outputjson try: resp requests.get(url, timeout10) if resp.status_code 200: data resp.json() for item in data: name_value item.get(name_value, ) # 处理返回的字符串可能包含换行分隔的多个域名 for name in name_value.split(\n): name name.strip().lower() # 清理通配符和无关字符 if * in name: continue if name.endswith(f.{domain}) and name ! domain: subdomains.add(name) except Exception as e: print(f[-] 查询 crt.sh 失败: {e}) return list(subdomains)实操心得在实际使用中推荐优先使用“证书透明度日志查询”因为它速度快、覆盖面广且完全被动不会对目标产生直接流量。字典爆破作为补充但要注意控制并发和频率避免对目标DNS服务器造成压力或触发防护。搜索引擎接口不稳定可作为最后的备选方案。3.2 端口扫描从简单到可配置端口扫描实现了最基础的TCP Connect扫描。为了提高速度我们使用concurrent.futures线程池。import socket from concurrent.futures import ThreadPoolExecutor, as_completed def tcp_connect_scan(target_ip, port_list, max_workers100, timeout1): open_ports [] def scan_port(port): sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) result sock.connect_ex((target_ip, port)) sock.close() return port, result 0 # 返回端口号和是否开放 with ThreadPoolExecutor(max_workersmax_workers) as executor: future_to_port {executor.submit(scan_port, port): port for port in port_list} for future in as_completed(future_to_port): port, is_open future.result() if is_open: open_ports.append(port) print(f[] {target_ip}:{port} is OPEN) return sorted(open_ports)在GUI中我提供了常用端口列表如Web服务、数据库、远程管理端口的预设也允许用户自定义端口范围。扫描完成后会尝试对开放端口进行简单的横幅Banner抓取以初步识别服务。def grab_banner(target_ip, port, timeout2): try: sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((target_ip, port)) # 发送一个简单的探测数据如HTTP GET请求或换行符 sock.send(b\n) banner sock.recv(1024).decode(utf-8, errorsignore).strip() sock.close() return banner if banner else No banner grabbed except Exception as e: return fError: {e}注意事项未经授权的端口扫描可能违反法律或目标的使用政策务必仅在你有权限测试的环境中进行。此外高并发扫描容易被防火墙或IDS检测并屏蔽。在实际工具中我增加了扫描速度调节滑块控制max_workers和随机延时功能以降低“噪音”。3.3 Web目录扫描与指纹识别目录扫描基于一个字典文件使用多线程发起HTTP HEAD或GET请求根据状态码判断目录或文件是否存在。这里的关键在于字典的质量和请求头的伪装。import requests from urllib.parse import urljoin def dir_scan(base_url, wordlist_path, extensions[, .php, .html, .bak], threads20): found [] with open(wordlist_path, r, encodingutf-8, errorsignore) as f: words [line.strip() for line in f if line.strip() and not line.startswith(#)] session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, }) def check_path(path): for ext in extensions: test_url urljoin(base_url, path ext) try: resp session.head(test_url, timeout3, allow_redirectsTrue) # 通常认为 200, 403, 301, 302 是存在的迹象 if resp.status_code in [200, 403, 301, 302]: return (test_url, resp.status_code, resp.headers.get(Content-Length, N/A)) except requests.RequestException: continue return None # 使用线程池并发检查代码略 return found指纹识别则通过多个特征进行综合判断检查特定文件如/wp-admin/(WordPress),/administrator/(Joomla),/robots.txt中的特征字符。检查HTTP响应头如X-Powered-By、Server字段。检查首页HTML内容查找特定的Meta标签、注释、CSS/JS文件路径中的关键字。 我将这些特征规则写在一个JSON配置文件中便于管理和更新。// fingerprints.json 片段 { WordPress: { checks: [ {type: keyword, location: body, content: wp-content, certainty: 0.7}, {type: path, path: /wp-login.php, status_code: 200, certainty: 0.9}, {type: header, header: X-Powered-By, content: PHP, certainty: 0.3} ] }, ThinkPHP: { checks: [ {type: keyword, location: body, content: ThinkPHP, certainty: 0.8}, {type: header, header: X-Powered-By, content: ThinkPHP, certainty: 0.9} ] } }4. 图形界面GUI的设计与交互实现4.1 界面布局与组件选择使用Tkinter的ttk组件构建主窗口。界面主要分为以下几个区域顶部控制区目标输入框支持单IP/域名或文件导入、功能选择复选框子域名、端口扫描、目录扫描等、开始/停止按钮、进度条。中间结果展示区使用ttk.Notebook组件创建标签页每个标签页对应一个功能模块的结果内部使用Treeview组件以表格形式展示详细信息如子域名、IP、状态码、横幅等。底部日志输出区使用ScrolledText组件实时显示扫描过程中的日志信息方便调试和观察运行状态。为了保持界面响应所有耗时的扫描任务都必须放在后台线程中执行否则会阻塞GUI主循环导致界面“卡死”。我使用Python的threading模块来创建后台线程。4.2 多线程与GUI的通信这是GUI开发中最关键也最容易出错的部分。Tkinter不是线程安全的这意味着你不能直接从后台线程更新GUI组件。正确的做法是使用线程安全队列queue.Queue或者Tkinter的after方法。我的实现方案是为每个扫描任务创建一个Thread对象。这个线程在执行过程中将需要更新的日志或结果数据放入一个共享的queue.Queue中。GUI主线程定期使用root.after(100, check_queue)检查这个队列一旦有数据就在主线程中安全地更新Text或Treeview组件。import tkinter as tk from tkinter import ttk, scrolledtext import threading import queue import time class ScannerGUI: def __init__(self, root): self.root root self.log_queue queue.Queue() self.result_queue queue.Queue() self.setup_ui() self.running False # 启动队列检查定时器 self.root.after(100, self.process_queues) def setup_ui(self): # ... 创建各种界面组件 ... self.log_area scrolledtext.ScrolledText(self.root, statedisabled, height10) self.log_area.grid(row3, column0, columnspan2, stickynsew, padx5, pady5) self.start_btn ttk.Button(self.root, text开始扫描, commandself.start_scan) self.start_btn.grid(row0, column2, padx5, pady5) def start_scan(self): if self.running: return target self.target_entry.get().strip() if not target: self.log([-] 请输入目标) return self.running True self.start_btn.config(statedisabled) # 创建并启动后台扫描线程 scan_thread threading.Thread(targetself.scan_worker, args(target,), daemonTrue) scan_thread.start() def scan_worker(self, target): 后台扫描线程函数 # 模拟一个耗时的任务 for i in range(5): time.sleep(1) # 将日志放入队列而不是直接操作GUI self.log_queue.put(f[] 扫描进度: {i1}/5) # 扫描完成信号 self.log_queue.put([INFO] 扫描完成) self.result_queue.put((完成, target, 所有任务结束)) self.running False # 通过队列通知GUI恢复按钮状态 self.log_queue.put(_ENABLE_BUTTON_) def process_queues(self): 在主线程中处理队列中的消息 # 处理日志队列 while not self.log_queue.empty(): msg self.log_queue.get_nowait() if msg _ENABLE_BUTTON_: self.start_btn.config(statenormal) else: self.log(msg) # 处理结果队列更新Treeview等代码类似 # ... # 每隔100ms再次检查 self.root.after(100, self.process_queues) def log(self, message): 安全地在主线程中更新日志区域 self.log_area.config(statenormal) self.log_area.insert(tk.END, message \n) self.log_area.see(tk.END) self.log_area.config(statedisabled)踩坑记录初期我直接在线程中调用self.log_area.insert()在Windows上运行时界面经常随机崩溃而在macOS或Linux上可能看似正常但这是未定义行为。务必牢记所有直接操作Tkinter组件的代码都必须发生在主线程中。使用队列是解决此问题的标准模式。4.3 结果的展示与导出Treeview组件非常适合展示结构化的扫描结果。我为每种扫描类型子域名、端口、目录创建了对应的列。双击某一行可以查看更详细的信息例如对于端口弹窗显示完整的Banner信息对于URL用默认浏览器打开。此外工具还提供了结果导出功能支持将当前标签页的结果导出为文本文件.txt或逗号分隔值文件.csv方便后续用Excel或脚本进行深入分析。实现起来很简单就是遍历Treeview的get_children()方法获取所有行数据然后写入文件。5. 项目打包与部署实践开发完成后如何将代码打包成一个独立的、用户可以双击运行的.exeWindows或可执行文件macOS/Linux是另一个关键步骤。我选择使用PyInstaller因为它简单易用支持跨平台。基本打包命令pyinstaller --onefile --windowed --nameMySecurityTool gui_main.py--onefile: 将所有依赖打包成单个可执行文件。--windowed: 运行时不显示控制台窗口对于GUI程序很重要。--name: 指定生成的可执行文件名称。遇到的挑战与解决方案动态加载的资源文件丢失我的工具包含字典文件wordlists/、指纹配置文件fingerprints.json和图标文件。PyInstaller默认不会打包这些。需要在打包时通过--add-data参数指定。pyinstaller --onefile --windowed --nameMySecurityTool \ --add-data wordlists;wordlists \ --add-data fingerprints.json;. \ --add-data icon.ico;. \ gui_main.py在Windows上用分号;分隔源路径和目标路径在macOS/Linux上用冒号:代码中如何访问打包后的资源打包后程序的运行路径会改变。不能再用相对路径wordlists/subdomains.txt直接访问。需要使用PyInstaller提供的sys._MEIPASS属性来获取临时解压资源的路径。import sys import os def get_resource_path(relative_path): 获取资源的绝对路径。兼容开发环境和PyInstaller打包后环境 if hasattr(sys, _MEIPASS): # PyInstaller创建的临时文件夹 base_path sys._MEIPASS else: # 开发环境中的当前目录 base_path os.path.abspath(.) return os.path.join(base_path, relative_path) # 使用方式 wordlist_path get_resource_path(os.path.join(wordlists, subdomains.txt))杀毒软件误报这是使用PyInstaller打包Python程序尤其是安全相关工具时一个非常常见且头疼的问题。因为PyInstaller打包的单文件exe行为类似加壳程序且安全工具的行为特征如端口扫描容易被启发式检测判定为恶意。缓解措施包括使用--key参数进行加密效果有限。向各大杀毒软件厂商提交你的程序进行白名单认证过程漫长。最务实的建议在项目README中明确说明此情况并提供源代码让用户自行用pyinstaller打包或者提供免安装的Python脚本运行方式。6. 安全、法律与伦理的边界开发和使用此类工具必须时刻绷紧安全、法律和伦理这根弦。1. 法律合规性仅用于授权测试该工具设计初衷是用于安全人员对自身拥有或已获得明确书面授权的资产进行安全评估。任何未经授权的扫描、探测、渗透行为都可能违反《网络安全法》、《刑法》等相关法律法规构成“非法侵入计算机信息系统罪”等。知晓你的边界明确告知用户工具的用途和风险。我在GUI启动时和帮助文档中加入了显著的免责声明。2. 工具本身的安全避免硬编码敏感信息不要在源代码或配置文件中写入任何API密钥、默认密码等。应通过配置文件.ini,.yaml或环境变量读取并提醒用户自行配置。谨慎处理用户输入对用户输入的目标地址进行基本的格式校验防止命令注入或路径遍历等漏洞影响到工具本身。网络请求的安全性建议支持HTTPS并验证SSL证书可在设置中提供关闭验证的选项但需提示风险。避免使用不安全的协议或默认凭证。3. 使用伦理最小化影响原则在工具设计中默认设置应是“保守的”。例如端口扫描的并发数不宜过高扫描间隔应可配置避免对目标系统造成拒绝服务DoS影响。尊重隐私收集到的信息仅用于安全评估目的不得泄露或用于其他非法用途。负责任的披露如果在授权测试中发现严重漏洞应遵循负责任的漏洞披露流程首先通知资产所有者给予其合理的修复时间。个人体会技术本身是中立的但使用技术的人需要为其后果负责。这个工具就像一把螺丝刀在工程师手里是维修设备的好帮手但也能成为破坏工具。我在代码仓库的显著位置和工具启动界面都强调了“仅用于合法授权测试”这是每个安全从业者必须坚守的底线。7. 常见问题排查与优化技巧在实际使用和开发过程中会遇到各种各样的问题。这里记录一些典型问题的解决方法。问题1扫描速度慢界面卡顿无响应。原因虽然使用了多线程但线程数量过多或任务粒度太细导致线程创建/切换开销巨大且GUI主线程忙于处理队列更新。解决方案使用线程池控制并发数如前文所用ThreadPoolExecutor将max_workers设置为一个合理值如50-200取决于网络和系统性能避免创建数千个线程。批量处理队列更新不要每产生一条日志或一个结果就立即更新GUI。可以改为在后台线程中先缓存一批结果例如每10个或每隔0.5秒再将这批数据一次性放入队列。GUI处理队列时也一次性插入多条数据而不是逐条插入。这能大幅减少GUI事件循环的压力。考虑使用异步IO对于I/O密集型任务网络请求使用asyncioaiohttp比多线程更高效、资源占用更少。但这需要重构代码为异步模式并且Tkinter与asyncio的集成需要额外处理例如使用asyncio的run_in_executor或在单独的线程中运行事件循环。问题2某些网站无法扫描返回403或触发WAFWeb应用防火墙。原因工具默认的HTTP头如User-Agent被识别为脚本或爬虫。解决方案随机化请求头维护一个常见的浏览器User-Agent列表每次请求随机选择。同时可以随机添加一些常见的HTTP头如Accept-Language,Referer设置为目标网站首页等。请求速率限制在扫描逻辑中加入随机延时time.sleep(random.uniform(0.5, 2))模拟人类操作避免请求过快。使用代理池如果单个IP被封锁可以通过代理池轮换IP。在工具中集成代理设置功能支持从文件或API接口读取代理列表。问题3子域名爆破时DNS解析超时导致整体速度极慢。原因默认的DNS服务器可能响应慢或者对大量查询进行了限制。解决方案配置多个DNS解析器使用dns.resolver.Resolver()并为其配置多个公共DNS服务器地址如8.8.8.8,114.114.114.114,1.1.1.1。设置合理的超时和重试如前面代码所示设置resolver.timeout和resolver.lifetime。对于解析失败的可以不重试或仅重试一次快速失败以进行下一个查询。使用操作系统缓存需要注意Python的dns.resolver默认可能不利用系统缓存。对于大规模扫描可以考虑自建一个小的DNS缓存机制或者使用dnspython的缓存功能。问题4打包后的程序在别的电脑上运行报错提示缺少DLL或模块。原因PyInstaller可能没有正确打包所有依赖特别是涉及C扩展的模块如cryptography,lxml在某些Windows系统上。解决方案在“干净”的虚拟环境中打包使用venv或conda创建一个新的虚拟环境只安装项目必需的包然后在这个环境中运行PyInstaller。这可以避免打包进开发环境中不必要的、可能冲突的包。使用--hidden-import如果运行时提示找不到某个模块可能是PyInstaller没有自动分析到该依赖。可以通过--hidden-importmodule_name参数显式指定。在目标系统测试尽可能在“纯净”的Windows虚拟机如全新安装的Windows 10/11中测试打包好的程序这是发现依赖问题最直接的方法。开发这样一个工具的过程远不止是代码的堆砌。它涉及需求分析、架构设计、编码实现、用户体验、打包部署乃至法律伦理的全面思考。每一次故障排查和性能优化都加深了对Python及其相关生态、网络协议、系统交互的理解。最终这个工具的价值不仅在于它提供的扫描结果更在于它成为了一个可扩展、可学习的代码基底。你可以基于它轻松地集成新的漏洞检测插件、对接更多的外部API、或者优化其并发模型。希望我的这些分享能为你开启自己的安全工具开发之路提供一块坚实的垫脚石。本文还有配套的精品资源点击获取