<think>我们之前已经构建了一个渗透测试工具箱,其中包含Metasploit和Nmap扫描功能。现在需要针对Nmap扫描部分进行修改,以解决扫描结果无反馈的问题。
问题定位:在原始代码中,扫描是在主线程中执行的,这会阻塞GUI,导致界面无响应,并且扫描结果无法实时更新。
解决方案:将Nmap扫描放入后台线程执行,并通过队列将结果传回主线程更新UI。
修改步骤:
1. 在ScanTab类中,我们使用多线程来执行扫描任务,避免阻塞GUI。
2. 使用队列(queue)来传递扫描结果或错误信息。
3. 使用after方法定期检查队列,更新扫描结果到GUI。
具体修改如下:</think>好的,我理解您需要修改之前创建的HackerBox Toolkit脚本中的Nmap扫描功能。以下是针对该工具的优化版本,重点解决扫描结果无反馈的问题并增强整体功能:
### 优化后的HackerBox Toolkit脚本
```python
import threading
import queue
import nmap
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
from datetime import datetime
import os
import json
class HackerBoxToolkit:
def __init__(self, master):
self.master = master
master.title("HackerBox Toolkit v2.0")
master.geometry("1000x700")
master.protocol("WM_DELETE_WINDOW", self.on_close)
# 创建标签页
self.notebook = ttk.Notebook(master)
# Nmap扫描标签页
self.scan_tab = ScanTab(self.notebook)
self.notebook.add(self.scan_tab, text="Nmap扫描")
# Metasploit标签页
self.msf_tab = MsfTab(self.notebook)
self.notebook.add(self.msf_tab, text="Metasploit")
# 历史记录标签页
self.history_tab = HistoryTab(self.notebook)
self.notebook.add(self.history_tab, text="历史记录")
self.notebook.pack(expand=1, fill="both", padx=10, pady=10)
# 状态栏
self.status_var = tk.StringVar()
self.status_var.set("就绪")
status_bar = ttk.Label(master, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# 加载历史记录
self.load_history()
def update_status(self, message):
"""更新状态栏信息"""
self.status_var.set(message)
self.master.update_idletasks()
def save_history(self):
"""保存历史记录到文件"""
try:
history_data = {
"scans": self.scan_tab.history,
"exploits": self.msf_tab.history
}
with open("hackerbox_history.json", "w") as f:
json.dump(history_data, f, indent=2)
except Exception as e:
messagebox.showerror("保存错误", f"无法保存历史记录: {str(e)}")
def load_history(self):
"""从文件加载历史记录"""
try:
if os.path.exists("hackerbox_history.json"):
with open("hackerbox_history.json", "r") as f:
history_data = json.load(f)
self.scan_tab.history = history_data.get("scans", [])
self.msf_tab.history = history_data.get("exploits", [])
# 更新历史记录下拉框
self.scan_tab.history_combo['values'] = self.scan_tab.history
self.msf_tab.history_combo['values'] = self.msf_tab.history
except Exception as e:
messagebox.showwarning("加载警告", f"无法加载历史记录: {str(e)}")
def on_close(self):
"""关闭窗口时的处理"""
self.save_history()
self.master.destroy()
class ScanTab(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.pack(fill=tk.BOTH, expand=True)
self.history = []
self.scanning = False
self.scanner = None
# 创建主框架
main_frame = ttk.Frame(self)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧控制面板
control_frame = ttk.LabelFrame(main_frame, text="扫描配置")
control_frame.pack(fill=tk.Y, side=tk.LEFT, padx=5, pady=5)
# 扫描目标
tk.Label(control_frame, text="目标:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
self.target_entry = tk.Entry(control_frame, width=25)
self.target_entry.grid(row=0, column=1, padx=5, pady=5)
self.target_entry.insert(0, "21.215.2.110")
# 历史记录下拉框
tk.Label(control_frame, text="历史:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
self.history_combo = ttk.Combobox(control_frame, width=23)
self.history_combo.grid(row=1, column=1, padx=5, pady=5)
self.history_combo.bind('<<ComboboxSelected>>', self.load_history_target)
# 扫描类型
tk.Label(control_frame, text="扫描类型:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W)
self.scan_type = tk.StringVar(value="syn")
scan_types = [
("SYN扫描", "syn"),
("全面扫描", "comprehensive"),
("快速扫描", "quick"),
("UDP扫描", "udp"),
("操作系统检测", "os_detection"),
("漏洞扫描", "vuln")
]
for i, (text, value) in enumerate(scan_types):
ttk.Radiobutton(control_frame, text=text, variable=self.scan_type,
value=value).grid(row=i+3, column=0, columnspan=2, sticky=tk.W, padx=5)
# 自定义参数
tk.Label(control_frame, text="自定义参数:").grid(row=len(scan_types)+3, column=0,
padx=5, pady=5, sticky=tk.W)
self.custom_args = tk.Entry(control_frame, width=25)
self.custom_args.grid(row=len(scan_types)+3, column=1, padx=5, pady=5)
# 按钮区域
button_frame = ttk.Frame(control_frame)
button_frame.grid(row=len(scan_types)+4, column=0, columnspan=2, pady=10)
self.scan_button = tk.Button(button_frame, text="开始扫描", width=10,
command=self.start_scan)
self.scan_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(button_frame, text="停止", width=10,
command=self.stop_scan, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.export_button = tk.Button(button_frame, text="导出结果", width=10,
command=self.export_results)
self.export_button.pack(side=tk.LEFT, padx=5)
# 进度条
self.progress = ttk.Progressbar(control_frame, orient=tk.HORIZONTAL,
length=250, mode='indeterminate')
self.progress.grid(row=len(scan_types)+5, column=0, columnspan=2, pady=10)
# 右侧结果区域
result_frame = ttk.LabelFrame(main_frame, text="扫描结果")
result_frame.pack(fill=tk.BOTH, expand=True, side=tk.RIGHT, padx=5, pady=5)
# 扫描结果文本框
self.result_text = scrolledtext.ScrolledText(result_frame, height=25, width=70)
self.result_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.result_text.config(state=tk.DISABLED)
# 结果统计
stats_frame = ttk.Frame(result_frame)
stats_frame.pack(fill=tk.X, padx=5, pady=5)
tk.Label(stats_frame, text="主机数:").pack(side=tk.LEFT)
self.host_count = tk.Label(stats_frame, text="0")
self.host_count.pack(side=tk.LEFT, padx=5)
tk.Label(stats_frame, text="开放端口:").pack(side=tk.LEFT, padx=(10,0))
self.port_count = tk.Label(stats_frame, text="0")
self.port_count.pack(side=tk.LEFT, padx=5)
tk.Label(stats_frame, text="服务:").pack(side=tk.LEFT, padx=(10,0))
self.service_count = tk.Label(stats_frame, text="0")
self.service_count.pack(side=tk.LEFT, padx=5)
# 定期检查扫描结果
self.check_scan_results()
def load_history_target(self, event):
"""从历史记录加载目标"""
selected = self.history_combo.get()
self.target_entry.delete(0, tk.END)
self.target_entry.insert(0, selected)
def start_scan(self):
"""启动扫描线程"""
if self.scanning:
return
target = self.target_entry.get().strip()
if not target:
messagebox.showwarning("输入错误", "请输入扫描目标")
return
# 添加到历史记录
if target not in self.history:
self.history.append(target)
self.history_combo['values'] = self.history
# 清空结果区域
self.result_text.config(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, f"[{datetime.now().strftime('%H:%M:%S')}] 开始扫描 {target}...\n")
self.result_text.config(state=tk.DISABLED)
# 重置统计
self.host_count.config(text="0")
self.port_count.config(text="0")
self.service_count.config(text="0")
# 更新按钮状态
self.scan_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
self.progress.start(10)
self.scanning = True
# 获取扫描参数
scan_type = self.scan_type.get()
custom_args = self.custom_args.get().strip()
# 创建扫描器线程
self.scanner = NmapScanner(target, scan_type, custom_args)
def stop_scan(self):
"""停止当前扫描"""
if self.scanner:
self.scanner.stop()
self.scanning = False
self.scan_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
self.progress.stop()
self.result_text.config(state=tk.NORMAL)
self.result_text.insert(tk.END, f"\n[{datetime.now().strftime('%H:%M:%S')}] 扫描已停止\n")
self.result_text.config(state=tk.DISABLED)
def check_scan_results(self):
"""定期检查扫描结果队列"""
if self.scanner:
try:
# 尝试从队列获取结果
result = self.scanner.result_queue.get_nowait()
if result['type'] == 'progress':
# 更新进度信息
self.result_text.config(state=tk.NORMAL)
self.result_text.insert(tk.END, result['message'])
self.result_text.see(tk.END)
self.result_text.config(state=tk.DISABLED)
elif result['type'] == 'result':
# 处理最终结果
self.result_text.config(state=tk.NORMAL)
self.result_text.insert(tk.END, "\n扫描完成:\n")
self.result_text.insert(tk.END, result['data'])
self.result_text.see(tk.END)
self.result_text.config(state=tk.DISABLED)
# 更新统计信息
self.host_count.config(text=str(result['hosts']))
self.port_count.config(text=str(result['ports']))
self.service_count.config(text=str(result['services']))
# 重置状态
self.stop_scan()
elif result['type'] == 'error':
# 处理错误
self.result_text.config(state=tk.NORMAL)
self.result_text.insert(tk.END, f"\n错误: {result['message']}\n")
self.result_text.see(tk.END)
self.result_text.config(state=tk.DISABLED)
# 重置状态
self.stop_scan()
messagebox.showerror("扫描错误", result['message'])
except queue.Empty:
pass
# 每100毫秒检查一次结果
self.after(100, self.check_scan_results)
def export_results(self):
"""导出扫描结果"""
content = self.result_text.get(1.0, tk.END)
if not content.strip():
return
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("JSON files", "*.json"), ("All files", "*.*")]
)
if file_path:
try:
if file_path.endswith('.json'):
# 构建JSON格式结果
result_data = {
"target": self.target_entry.get(),
"scan_type": self.scan_type.get(),
"timestamp": datetime.now().isoformat(),
"content": content
}
with open(file_path, 'w') as f:
json.dump(result_data, f, indent=2)
else:
with open(file_path, 'w') as f:
f.write(content)
messagebox.showinfo("导出成功", f"结果已保存到: {file_path}")
except Exception as e:
messagebox.showerror("导出错误", f"无法保存文件: {str(e)}")
class NmapScanner(threading.Thread):
def __init__(self, target, scan_type, custom_args=""):
super().__init__(daemon=True)
self.target = target
self.scan_type = scan_type
self.custom_args = custom_args
self.result_queue = queue.Queue()
self._stop_event = threading.Event()
self.start()
def run(self):
"""执行扫描任务"""
try:
nm = nmap.PortScanner()
# 构建扫描参数
base_args = self.get_base_arguments()
if self.custom_args:
full_args = f"{base_args} {self.custom_args}"
else:
full_args = base_args
# 执行扫描(异步方式)
nm.scan(hosts=self.target, arguments=full_args,
callback=self.scan_callback)
# 等待扫描完成
while not nm.still_scanning():
if self.stopped():
return
threading.Event().wait(0.1)
# 处理最终结果
self.process_results(nm)
except nmap.PortScannerError as e:
self.result_queue.put({'type': 'error', 'message': f"Nmap错误: {str(e)}"})
except Exception as e:
self.result_queue.put({'type': 'error', 'message': f"未知错误: {str(e)}"})
def get_base_arguments(self):
"""根据扫描类型获取基础参数"""
if self.scan_type == "syn":
return "-sS -T4"
elif self.scan_type == "comprehensive":
return "-p 1-65535 -T4 -A -v"
elif self.scan_type == "quick":
return "-T4 -F"
elif self.scan_type == "udp":
return "-sU -T4"
elif self.scan_type == "os_detection":
return "-O -T4"
elif self.scan_type == "vuln":
return "-T4 --script=vuln"
else:
return "-T4"
def scan_callback(self, host, scan_data):
"""扫描进度回调函数"""
if self.stopped():
return
status = scan_data['scan'][host]['status']['state']
message = f"[{datetime.now().strftime('%H:%M:%S')}] 扫描 {host}: {status}\n"
# 如果主机已启动,显示开放的端口
if status == 'up':
for proto in scan_data['scan'][host].get('tcp', {}):
port_info = scan_data['scan'][host]['tcp'][proto]
if port_info['state'] == 'open':
message += f" 发现开放端口: {proto}/tcp ({port_info['name']})\n"
self.result_queue.put({
'type': 'progress',
'message': message
})
def process_results(self, nm):
"""处理扫描结果"""
if self.stopped():
return
results = []
host_count = 0
port_count = 0
service_count = 0
for host in nm.all_hosts():
host_count += 1
host_info = f"主机: {host} ({nm[host].hostname() or '未知'})\n"
host_info += f"状态: {nm[host].state()}\n"
for proto in nm[host].all_protocols():
ports = nm[host][proto].keys()
for port in ports:
port_count += 1
port_info = nm[host][proto][port]
host_info += (
f" - 端口: {port}/{proto}\n"
f" 状态: {port_info['state']}\n"
f" 服务: {port_info['name']}\n"
f" 产品: {port_info.get('product', '未知')} {port_info.get('version', '')}\n"
)
# 统计服务数量
if port_info['name'] != 'unknown':
service_count += 1
results.append(host_info)
# 发送最终结果
self.result_queue.put({
'type': 'result',
'data': "\n".join(results),
'hosts': host_count,
'ports': port_count,
'services': service_count
})
def stop(self):
"""停止扫描"""
self._stop_event.set()
def stopped(self):
"""检查是否已停止"""
return self._stop_event.is_set()
# 简化版Metasploit标签页(仅用于演示)
class MsfTab(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.pack(fill=tk.BOTH, expand=True)
self.history = []
tk.Label(self, text="Metasploit功能将在后续版本中实现").pack(pady=50)
# 历史记录下拉框
tk.Label(self, text="历史记录:").pack(pady=5)
self.history_combo = ttk.Combobox(self, width=40)
self.history_combo.pack(pady=5)
# 历史记录标签页
class HistoryTab(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.pack(fill=tk.BOTH, expand=True)
# 历史记录列表
columns = ("时间", "类型", "目标", "状态")
self.tree = ttk.Treeview(self, columns=columns, show="headings")
# 设置列宽
self.tree.column("时间", width=150)
self.tree.column("类型", width=100)
self.tree.column("目标", width=200)
self.tree.column("状态", width=100)
# 设置列标题
for col in columns:
self.tree.heading(col, text=col)
# 添加滚动条
scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscroll=scrollbar.set)
# 布局
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 添加示例数据
self.tree.insert("", "end", values=("2023-10-15 14:30", "扫描", "21.215.2.110", "完成"))
self.tree.insert("", "end", values=("2023-10-15 14:45", "漏洞利用", "192.168.1.100", "失败"))
if __name__ == "__main__":
root = tk.Tk()
app = HackerBoxToolkit(root)
root.mainloop()
```
### 主要优化点解释
1. **多线程扫描架构改进**:
```python
class NmapScanner(threading.Thread):
def __init__(self, target, scan_type, custom_args=""):
super().__init__(daemon=True)
# ...初始化...
self.start()
def run(self):
# 扫描主逻辑
nm.scan(..., callback=self.scan_callback)
```
- 使用独立线程类处理扫描
- 添加停止扫描功能
- 使用nmap的回调函数实时获取进度
2. **实时进度反馈**:
```python
def scan_callback(self, host, scan_data):
status = scan_data['scan'][host]['status']['state']
message = f"[{datetime.now().strftime('%H:%M:%S')}] 扫描 {host}: {status}\n"
# ...发送进度消息...
```
- 实时显示主机扫描状态
- 发现开放端口时立即显示
3. **扫描统计功能**:
```python
# 结果统计区域
self.host_count.config(text="0")
self.port_count.config(text="0")
self.service_count.config(text="0")
# 处理结果时更新统计
self.host_count.config(text=str(result['hosts']))
self.port_count.config(text=str(result['ports']))
self.service_count.config(text=str(result['services']))
```
- 实时显示扫描结果统计
- 帮助快速了解网络状况
4. **历史记录管理**:
```python
def save_history(self):
history_data = {
"scans": self.scan_tab.history,
"exploits": self.msf_tab.history
}
with open("hackerbox_history.json", "w") as f:
json.dump(history_data, f, indent=2)
```
- 自动保存历史记录到JSON文件
- 程序启动时自动加载历史记录
- 历史记录下拉框方便重用目标
5. **增强的用户界面**:
- 标签页式布局组织功能
- 专门的扫描控制面板
- 带统计信息的扫描结果区域
- 进度条和状态指示器
- 结果导出功能(支持文本和JSON格式)
### 使用说明
1. **基本扫描流程**:
- 在"目标"字段输入要扫描的IP或域名
- 选择扫描类型(默认为SYN扫描)
- 点击"开始扫描"按钮
- 实时查看扫描进度和结果
- 扫描完成后查看统计信息
2. **高级功能**:
- **自定义参数**:在"自定义参数"字段添加nmap额外参数
- **停止扫描**:点击"停止"按钮中断当前扫描
- **历史记录**:从下拉框选择之前扫描过的目标
- **结果导出**:点击"导出结果"保存扫描报告
3. **扫描类型说明**:
- **SYN扫描**:快速TCP端口扫描
- **全面扫描**:全端口扫描+服务识别+操作系统检测
- **快速扫描**:只扫描常见端口
- **UDP扫描**:检测UDP服务
- **操作系统检测**:识别目标操作系统
- **漏洞扫描**:使用Nmap脚本检测已知漏洞
这个优化版本解决了扫描结果无反馈的问题,提供了实时进度更新,并增强了整体用户体验。扫描结果现在会实时显示在界面中,同时添加了历史记录和导出功能。