試模塊)
簡(jiǎn)介這是一套面向網(wǎng)絡(luò)安全初學(xué)者與滲透測(cè)試從業(yè)者的Python信息收集工具源代碼基于PyQt5構(gòu)建圖形界面將端口掃描、敏感文件探測(cè)、子域名枚舉、WHOIS查詢、指紋識(shí)別與服務(wù)器信息收集等功能整合為可視化操作降低命令行使用門檻。資源包共39個(gè)文件以14個(gè)py源碼與12個(gè)pyc編譯文件為核心輔以6個(gè)txt字典、2個(gè)ini配置、1個(gè)ui界面文件及png圖標(biāo)等壓縮包約310KB結(jié)構(gòu)緊湊便于閱讀與二次開(kāi)發(fā)。其中字典文件支撐目錄爆破與子域名探測(cè)配置文件管理目標(biāo)與參數(shù)ui文件對(duì)應(yīng)主窗口布局源碼模塊劃分清晰適合逐模塊研讀實(shí)現(xiàn)思路。目前已有771人學(xué)習(xí)下載讀者可借此理解信息收集各環(huán)節(jié)的Python實(shí)現(xiàn)方式掌握從網(wǎng)絡(luò)請(qǐng)求到結(jié)果輸出的完整鏈路并在此基礎(chǔ)上擴(kuò)展自己的工具集適合作為滲透測(cè)試入門與代碼實(shí)踐的參考素材。1. 從一份帶 PyQt5 界面的信息收集工具源碼說(shuō)起很多做滲透測(cè)試的同行手里都攢著一堆零散的 Python 腳本端口掃描一個(gè)、目錄爆破一個(gè)、Whois 查詢又一個(gè)跑起來(lái)得來(lái)回切終端參數(shù)記不住還得翻歷史命令。這份「Python 信息收集、滲透測(cè)試工具源代碼」解決的正是這個(gè)痛點(diǎn)——它把常見(jiàn)的信息收集能力收進(jìn)一個(gè) PyQt5 圖形界面里點(diǎn)按鈕就能觸發(fā)掃描結(jié)果直接回顯在窗口里。它適合兩類人一是剛?cè)腴T、想通過(guò)讀源碼理解信息收集流程的新手二是想拿一套現(xiàn)成 GUI 框架改成自己內(nèi)部工具的老手。源碼本身不神秘價(jià)值在于把「掃描邏輯」和「界面交互」這兩件事拆得比較清楚改起來(lái)不費(fèi)勁。2. 拆開(kāi)源碼看結(jié)構(gòu)PyQt5 界面與掃描邏輯怎么分層拿到一份 GUI 工具源碼最怕的就是界面代碼和業(yè)務(wù)邏輯攪在一起改一個(gè)按鈕結(jié)果崩了三個(gè)功能。這份源碼在結(jié)構(gòu)上做了基本的分層值得先花十分鐘摸清楚再動(dòng)手。2.1 目錄結(jié)構(gòu)與模塊職責(zé)常見(jiàn)做法是把項(xiàng)目根目錄分成幾個(gè)部分入口文件負(fù)責(zé)啟動(dòng)應(yīng)用和加載主窗口ui目錄放 Qt Designer 生成的界面文件或手寫的界面類core或modules目錄放具體的掃描邏輯utils放公共函數(shù)比如 IP 校驗(yàn)、結(jié)果格式化。下面是一個(gè)典型的目錄布局你拿到源碼后可以對(duì)照著看project/ ├── main.py # 程序入口創(chuàng)建 QApplication 和主窗口 ├── ui/ │ ├── main_window.py # 主窗口類綁定按鈕信號(hào)與槽 │ └── main_window.ui # Qt Designer 界面描述文件可選 ├── core/ │ ├── port_scan.py # 端口掃描邏輯 │ ├── whois_query.py # Whois 查詢 │ └── subdomain.py # 子域名收集 ├── utils/ │ └── helpers.py # IP 校驗(yàn)、結(jié)果導(dǎo)出等 └── requirements.txt # 依賴清單這個(gè)分法的好處是界面層只負(fù)責(zé)「收集用戶輸入、調(diào)用 core 層函數(shù)、把返回值顯示到表格或文本框」core 層完全不依賴 PyQt5可以單獨(dú)在命令行里測(cè)試。我一般拿到新源碼第一件事就是打開(kāi)core目錄把每個(gè)模塊單獨(dú)跑一遍確認(rèn)掃描邏輯本身沒(méi)問(wèn)題再去調(diào)界面。2.2 信號(hào)與槽按鈕點(diǎn)擊后到底發(fā)生了什么PyQt5 的核心交互機(jī)制是信號(hào)signal和槽slot。按鈕被點(diǎn)擊是一個(gè)信號(hào)你寫的處理函數(shù)就是槽。很多新手讀源碼時(shí)卡在這里覺(jué)得「點(diǎn)一下按鈕怎么就跑到掃描函數(shù)去了」??聪旅孢@段典型代碼# ui/main_window.py from PyQt5.QtCore import QThread from core.port_scan import PortScanner class MainWindow(QMainWindow): def __init__(self): super().__init__() self.scan_btn.clicked.connect(self.on_scan_clicked) # 綁定信號(hào)與槽 def on_scan_clicked(self): target self.target_input.text().strip() # 取輸入框內(nèi)容 if not target: self.log_output.append(請(qǐng)先輸入目標(biāo)地址) return # 用獨(dú)立線程跑掃描避免阻塞界面 self.worker PortScanner(target) self.worker.finished.connect(self.on_scan_finished) self.worker.start() def on_scan_finished(self, results): for row in results: self.result_table.addRow(row) # 結(jié)果寫入表格邏輯說(shuō)明clicked.connect把按鈕點(diǎn)擊事件和on_scan_clicked方法綁在一起這是 PyQt5 的標(biāo)準(zhǔn)寫法。關(guān)鍵點(diǎn)在QThread——掃描是耗時(shí)操作如果直接在主線程里跑界面會(huì)卡死變成「無(wú)響應(yīng)」這是 GUI 工具最常見(jiàn)的翻車點(diǎn)。參數(shù)方面target來(lái)自輸入框記得strip()去掉首尾空格否則用戶復(fù)制粘貼時(shí)帶個(gè)空格就會(huì)導(dǎo)致掃描失敗。finished信號(hào)攜帶掃描結(jié)果通過(guò)connect回到主線程更新界面這樣線程安全。2.3 依賴安裝與環(huán)境準(zhǔn)備源碼要跑起來(lái)先把依賴裝齊。requirements.txt里通常包含PyQt5、requests、python-whois這類庫(kù)。安裝命令# 建議在虛擬環(huán)境里操作避免污染全局 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt如果pip install PyQt5卡住或報(bào)錯(cuò)常見(jiàn)原因是 Python 版本和 PyQt5 wheel 不匹配。Python 3.9 到 3.11 對(duì) PyQt5 的支持比較穩(wěn)3.12 以上有時(shí)需要換PyQt5-tools或改用PySide6。裝完后用python -c from PyQt5.QtWidgets import QApplication; print(ok)驗(yàn)證一下能打印ok就說(shuō)明環(huán)境沒(méi)問(wèn)題。這一步看著簡(jiǎn)單但我見(jiàn)過(guò)太多人卡在依賴上就放棄了其實(shí)換個(gè) Python 版本或者用國(guó)內(nèi)鏡像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt就能解決。3. 信息收集模塊逐個(gè)跑通端口、Whois、子域名界面能啟動(dòng)只是第一步真正要驗(yàn)證的是每個(gè)掃描模塊能不能出結(jié)果。這一章把常見(jiàn)的三個(gè)模塊拆開(kāi)講每個(gè)都給出可獨(dú)立運(yùn)行的代碼和參數(shù)說(shuō)明。3.1 端口掃描socket 連接與超時(shí)控制端口掃描的原理很樸素用 socket 去連目標(biāo)端口連上了就是開(kāi)放超時(shí)或拒絕就是關(guān)閉。源碼里大概率用的是socket.connect_ex因?yàn)樗粧伄惓V苯臃祷劐e(cuò)誤碼處理起來(lái)干凈。# core/port_scan.py import socket from concurrent.futures import ThreadPoolExecutor COMMON_PORTS [21, 22, 23, 25, 80, 110, 143, 443, 445, 3306, 3389, 8080] def scan_port(host, port, timeout1.0): 掃描單個(gè)端口返回 (port, is_open) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(timeout) # 超時(shí)設(shè)短一點(diǎn)否則掃 1000 個(gè)端口要等很久 result s.connect_ex((host, port)) # 返回 0 表示端口開(kāi)放 return port, result 0 def scan_ports(host, portsNone, max_workers50): ports ports or COMMON_PORTS open_ports [] with ThreadPoolExecutor(max_workersmax_workers) as executor: futures [executor.submit(scan_port, host, p) for p in ports] for f in futures: port, is_open f.result() if is_open: open_ports.append(port) return sorted(open_ports)邏輯說(shuō)明settimeout(1.0)是超時(shí)時(shí)間單位秒。設(shè)太長(zhǎng)掃描慢設(shè)太短可能漏掉響應(yīng)慢的端口1 秒是常見(jiàn)折中值。connect_ex返回 0 代表 TCP 三次握手成功端口開(kāi)放。ThreadPoolExecutor用多線程并發(fā)掃描max_workers50表示同時(shí)開(kāi) 50 個(gè)線程這個(gè)數(shù)字別調(diào)太大否則本機(jī)文件描述符可能不夠用報(bào)Too many open files。掃描結(jié)果排序后返回方便界面展示。參數(shù)怎么改如果你要掃全端口 1-65535把ports參數(shù)傳成range(1, 65536)但記得把max_workers降到 20 左右超時(shí)設(shè) 0.5 秒否則掃一輪要十幾分鐘。實(shí)際使用中先掃常見(jiàn)端口發(fā)現(xiàn)目標(biāo)存活后再針對(duì)性掃全端口效率更高。3.2 Whois 查詢域名注冊(cè)信息的獲取與解析Whois 查詢用來(lái)拿域名的注冊(cè)人、注冊(cè)時(shí)間、到期時(shí)間、DNS 服務(wù)器等信息。Python 里有python-whois庫(kù)可以直接用但要注意它返回的字段名在不同頂級(jí)域名下可能不一致。# core/whois_query.py import whois def query_whois(domain): 查詢域名 Whois 信息返回字典 try: w whois.whois(domain) return { domain: w.domain_name, registrar: w.registrar, creation_date: str(w.creation_date), expiration_date: str(w.expiration_date), name_servers: w.name_servers, } except Exception as e: return {error: str(e)}邏輯說(shuō)明whois.whois(domain)返回一個(gè)對(duì)象字段訪問(wèn)用屬性方式。creation_date有時(shí)是列表一個(gè)域名有多個(gè)注冊(cè)記錄所以用str()包一層避免界面顯示報(bào)錯(cuò)。異常處理不能省——有些域名沒(méi)有 Whois 記錄或者查詢被限流不捕獲的話整個(gè)程序會(huì)崩。參數(shù)方面?zhèn)魅氲膁omain要是純域名比如example.com不要帶http://或路徑否則查詢會(huì)失敗。3.3 子域名收集字典爆破與結(jié)果去重子域名收集常見(jiàn)兩種方式一是查證書透明度日志crt.sh 這類二是字典爆破。源碼里如果帶了字典爆破核心邏輯就是拼接subdomain.domain然后做 DNS 解析。# core/subdomain.py import socket def resolve_subdomain(sub, domain): 解析子域名返回 IP 或 None full f{sub}.{domain} try: return full, socket.gethostbyname(full) except socket.gaierror: return full, None def collect_subdomains(domain, wordlist): 遍歷字典收集子域名 found {} for sub in wordlist: full, ip resolve_subdomain(sub, domain) if ip: found[full] ip return found邏輯說(shuō)明socket.gethostbyname做 DNS 解析解析成功說(shuō)明子域名存在。socket.gaierror是解析失敗的異常捕獲后返回 None。found用字典存鍵是完整域名值是 IP天然去重。字典文件一般放在wordlist目錄下常見(jiàn)的有subdomains-top1million-5000.txt這類行數(shù)別太多5000 條左右跑一輪幾分鐘10 萬(wàn)條就要考慮加并發(fā)和 DNS 緩存了。提示DNS 解析受本地 DNS 服務(wù)器影響很大有時(shí)候解析失敗不是子域名不存在而是 DNS 超時(shí)??梢栽诖a里加重試或者換用dnspython庫(kù)指定公共 DNS 服務(wù)器。4. 避坑與排查GUI 卡死、依賴沖突、掃描被限這份源碼跑起來(lái)不難但實(shí)際用的時(shí)候有幾類問(wèn)題反復(fù)出現(xiàn)。下面按「現(xiàn)象 → 原因 → 解決」整理都是我自己踩過(guò)的。4.1 界面點(diǎn)擊后卡死無(wú)響應(yīng)現(xiàn)象點(diǎn)「開(kāi)始掃描」按鈕后窗口變白標(biāo)題欄顯示「無(wú)響應(yīng)」等十幾秒才恢復(fù)。原因掃描邏輯跑在主線程里PyQt5 的事件循環(huán)被阻塞界面無(wú)法重繪。這是 GUI 開(kāi)發(fā)最經(jīng)典的錯(cuò)誤。解決把所有耗時(shí)操作放進(jìn)QThread或QRunnable。源碼里如果已經(jīng)用了線程檢查是不是在槽函數(shù)里直接調(diào)了scan_ports()而不是通過(guò)worker.start()。另外注意子線程里不能直接操作界面控件必須通過(guò)信號(hào)回傳主線程更新。4.2 PyQt5 安裝報(bào)錯(cuò)或版本不兼容現(xiàn)象pip install PyQt5報(bào)ERROR: Could not build wheels或者裝完后import PyQt5提示 DLL 加載失敗。原因Python 版本太新比如 3.12沒(méi)有對(duì)應(yīng)的預(yù)編譯 wheel或者系統(tǒng)缺少 VC 運(yùn)行庫(kù)。解決優(yōu)先用 Python 3.93.11。Windows 上裝一下 VC Redistributable。如果還是不行改用PySide6API 幾乎一樣把from PyQt5改成from PySide6就能跑這是最省事的后悔藥。4.3 端口掃描結(jié)果不穩(wěn)定同一目標(biāo)兩次結(jié)果不同現(xiàn)象對(duì)同一臺(tái)主機(jī)掃兩次開(kāi)放的端口列表不一致有時(shí)多一個(gè)有時(shí)少一個(gè)。原因超時(shí)設(shè)太短網(wǎng)絡(luò)抖動(dòng)導(dǎo)致某些端口沒(méi)來(lái)得及響應(yīng)就被判定為關(guān)閉或者目標(biāo)主機(jī)有防護(hù)設(shè)備對(duì)高頻連接做了限流。解決把timeout從 0.5 秒調(diào)到 1.52 秒max_workers從 50 降到 20。如果目標(biāo)有防護(hù)掃描間隔加隨機(jī)延遲別用固定頻率猛打。掃描結(jié)果只作參考關(guān)鍵端口用telnet或nc手動(dòng)驗(yàn)證一遍。4.4 Whois 查詢返回空或字段缺失現(xiàn)象查詢某些域名時(shí)registrar是 None或者直接拋異常。原因不同頂級(jí)域名的 Whois 服務(wù)器返回格式不一樣python-whois的解析規(guī)則覆蓋不全部分域名開(kāi)啟了隱私保護(hù)注冊(cè)信息被隱藏。解決在代碼里對(duì)每個(gè)字段做空值判斷界面顯示「未獲取到」而不是直接崩。對(duì)于解析失敗的域名可以換用whois命令行工具手動(dòng)查一次確認(rèn)是庫(kù)的問(wèn)題還是域名本身沒(méi)有公開(kāi)信息。5. 二次開(kāi)發(fā)與驗(yàn)證把工具改成你自己的源碼能跑通之后真正的價(jià)值在于改成適合自己工作流的版本。這一章講兩個(gè)實(shí)用的改造方向和驗(yàn)證方法。5.1 增加掃描結(jié)果導(dǎo)出功能原始工具的結(jié)果只顯示在界面上關(guān)掉就沒(méi)了。加一個(gè)「導(dǎo)出 CSV」按鈕是很常見(jiàn)的需求。在MainWindow里加一個(gè)按鈕槽函數(shù)里把表格數(shù)據(jù)寫成 CSVimport csv from PyQt5.QtWidgets import QFileDialog def export_csv(self): path, _ QFileDialog.getSaveFileName(self, 保存結(jié)果, , CSV Files (*.csv)) if not path: return with open(path, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([端口, 狀態(tài), 服務(wù)]) # 表頭 for row in range(self.result_table.rowCount()): writer.writerow([ self.result_table.item(row, 0).text(), self.result_table.item(row, 1).text(), self.result_table.item(row, 2).text(), ])邏輯說(shuō)明QFileDialog.getSaveFileName彈出系統(tǒng)保存對(duì)話框返回路徑和過(guò)濾器。csv.writer逐行寫入encodingutf-8避免中文亂碼。rowCount()遍歷表格所有行item(row, col).text()取單元格內(nèi)容。參數(shù)方面表頭按你實(shí)際表格的列來(lái)改別照抄。5.2 用已知目標(biāo)驗(yàn)證掃描準(zhǔn)確性改完代碼怎么確認(rèn)沒(méi)改壞我一般用兩個(gè)目標(biāo)做回歸驗(yàn)證一是127.0.0.1本機(jī)開(kāi)放端口自己清楚掃出來(lái)對(duì)不上就是代碼有問(wèn)題二是scanme.nmap.org這是 Nmap 官方提供的測(cè)試目標(biāo)允許合法掃描用它驗(yàn)證外網(wǎng)掃描鏈路是否正常。驗(yàn)證步驟步驟操作預(yù)期結(jié)果1掃描 127.0.0.1 常見(jiàn)端口結(jié)果與本機(jī)netstat -tlnp一致2掃描 scanme.nmap.org 80/443兩個(gè)端口均顯示開(kāi)放3輸入不存在的域名做 Whois界面提示錯(cuò)誤而非崩潰4導(dǎo)出 CSV 后用 Excel 打開(kāi)中文表頭正常顯示無(wú)亂碼這四步走完基本能確認(rèn)工具的核心功能沒(méi)退化。從那以后我每次改完掃描邏輯都強(qiáng)制走一遍這個(gè)驗(yàn)證流程比憑感覺(jué)靠譜得多。5.3 關(guān)于合法使用的邊界這類工具的能力是雙刃劍用之前務(wù)必確認(rèn)目標(biāo)是你自己的資產(chǎn)或者拿到了書面授權(quán)。掃描公網(wǎng)陌生 IP 可能觸發(fā)對(duì)方的安全告警嚴(yán)重的話會(huì)惹上麻煩。源碼本身是學(xué)習(xí)材料拿它練手沒(méi)問(wèn)題但別拿去掃不屬于你的東西。希望這份源碼和上面的拆解能幫你把信息收集這塊的基本功練扎實(shí)需要的話按文中的結(jié)構(gòu)去對(duì)應(yīng)目錄里找文件就行。本文還有配套的精品資源點(diǎn)擊獲取