mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00

- 添加plugins.Exists()函数,避免不必要的插件实例创建 - 合并PluginInfo数据结构,统一插件工厂和端口信息存储 - 修复Scanner中重复调用plugins.Get()的性能问题 - 优化BaseScanStrategy.pluginExists()实现效率 主要性能改进: * 消除21×57=1197次不必要的本地插件实例化 * 提升插件存在性检查效率,从O(n)遍历优化为O(1)查找 * 改善数据内聚性,插件元数据集中管理 * 保持所有现有插件控制逻辑和功能完整性 测试验证: * 无-local参数时不再创建本地插件实例 * 端口匹配、Web检测、互斥验证等功能正常 * 插件注册和执行逻辑保持向后兼容
199 lines
5.7 KiB
Go
199 lines
5.7 KiB
Go
package core
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/common/i18n"
|
||
"github.com/shadow1ng/fscan/webscan/lib"
|
||
"github.com/shadow1ng/fscan/plugins"
|
||
"strconv"
|
||
"sync"
|
||
"sync/atomic"
|
||
)
|
||
|
||
|
||
// ScanStrategy 定义扫描策略接口(简化版)
|
||
type ScanStrategy interface {
|
||
// 执行扫描的主要方法
|
||
Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup)
|
||
|
||
// 插件管理方法
|
||
GetPlugins() ([]string, bool)
|
||
IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool
|
||
IsPluginApplicableByName(pluginName string, targetHost string, targetPort int, isCustomMode bool) bool
|
||
}
|
||
|
||
// selectStrategy 根据扫描配置选择适当的扫描策略
|
||
func selectStrategy(info common.HostInfo) ScanStrategy {
|
||
switch {
|
||
case common.AliveOnly || common.ScanMode == "icmp":
|
||
common.LogBase(i18n.GetText("scan_mode_alive_selected"))
|
||
return NewAliveScanStrategy()
|
||
case common.LocalMode:
|
||
common.LogBase(i18n.GetText("scan_mode_local_selected"))
|
||
return NewLocalScanStrategy()
|
||
case len(common.URLs) > 0:
|
||
common.LogBase(i18n.GetText("scan_mode_web_selected"))
|
||
return NewWebScanStrategy()
|
||
default:
|
||
common.LogBase(i18n.GetText("scan_mode_service_selected"))
|
||
return NewServiceScanStrategy()
|
||
}
|
||
}
|
||
|
||
// RunScan 执行整体扫描流程(简化版,移除不必要的包装)
|
||
func RunScan(info common.HostInfo) {
|
||
common.LogBase(i18n.GetText("scan_info_start"))
|
||
lib.Inithttp()
|
||
|
||
// 选择策略
|
||
strategy := selectStrategy(info)
|
||
|
||
// 并发控制初始化
|
||
ch := make(chan struct{}, common.ThreadNum)
|
||
wg := sync.WaitGroup{}
|
||
|
||
// 执行策略
|
||
strategy.Execute(info, &ch, &wg)
|
||
|
||
// 等待所有扫描完成
|
||
wg.Wait()
|
||
|
||
// 检查是否有活跃的反弹Shell或SOCKS5代理
|
||
if common.ReverseShellActive {
|
||
common.LogBase("检测到活跃的反弹Shell,保持程序运行...")
|
||
common.LogBase("按 Ctrl+C 退出程序")
|
||
|
||
// 进入无限等待,保持程序运行以维持反弹Shell连接
|
||
select {} // 阻塞等待,直到收到系统信号
|
||
}
|
||
|
||
if common.Socks5ProxyActive {
|
||
common.LogBase("检测到活跃的SOCKS5代理,保持程序运行...")
|
||
common.LogBase("按 Ctrl+C 退出程序")
|
||
|
||
// 进入无限等待,保持程序运行以维持SOCKS5代理
|
||
select {} // 阻塞等待,直到收到系统信号
|
||
}
|
||
|
||
if common.ForwardShellActive {
|
||
common.LogBase("检测到活跃的正向Shell,保持程序运行...")
|
||
common.LogBase("按 Ctrl+C 退出程序")
|
||
|
||
// 进入无限等待,保持程序运行以维持正向Shell
|
||
select {} // 阻塞等待,直到收到系统信号
|
||
}
|
||
|
||
// 完成扫描
|
||
finishScan()
|
||
}
|
||
|
||
// finishScan 完成扫描并输出结果
|
||
func finishScan() {
|
||
// 确保进度条正确完成
|
||
if common.IsProgressActive() {
|
||
common.FinishProgressBar()
|
||
}
|
||
|
||
// 输出扫描完成信息
|
||
common.LogBase(i18n.GetText("scan_task_complete", common.End, common.Num))
|
||
}
|
||
|
||
// ExecuteScanTasks 任务执行通用框架(流式处理,无预构建任务列表)
|
||
func ExecuteScanTasks(targets []common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
|
||
// 获取要执行的插件
|
||
pluginsToRun, isCustomMode := strategy.GetPlugins()
|
||
|
||
// 预计算任务数量用于进度条
|
||
taskCount := countApplicableTasks(targets, pluginsToRun, isCustomMode, strategy)
|
||
|
||
// 初始化进度条
|
||
if taskCount > 0 && common.ShowProgress {
|
||
description := i18n.GetText("progress_scanning_description")
|
||
common.InitProgressBar(int64(taskCount), description)
|
||
}
|
||
|
||
// 流式执行任务,避免预构建大量任务对象
|
||
for _, target := range targets {
|
||
targetPort := 0
|
||
if target.Ports != "" {
|
||
targetPort, _ = strconv.Atoi(target.Ports)
|
||
}
|
||
|
||
for _, pluginName := range pluginsToRun {
|
||
// 使用Exists检查避免不必要的插件实例创建
|
||
if !plugins.Exists(pluginName) {
|
||
continue
|
||
}
|
||
|
||
// 检查插件是否适用于当前目标
|
||
if strategy.IsPluginApplicableByName(pluginName, target.Host, targetPort, isCustomMode) {
|
||
executeScanTask(pluginName, target, ch, wg)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// countApplicableTasks 计算适用的任务数量(用于进度条初始化)
|
||
func countApplicableTasks(targets []common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) int {
|
||
count := 0
|
||
for _, target := range targets {
|
||
targetPort := 0
|
||
if target.Ports != "" {
|
||
targetPort, _ = strconv.Atoi(target.Ports)
|
||
}
|
||
|
||
for _, pluginName := range pluginsToRun {
|
||
// 使用Exists检查避免不必要的插件实例创建
|
||
if plugins.Exists(pluginName) &&
|
||
strategy.IsPluginApplicableByName(pluginName, target.Host, targetPort, isCustomMode) {
|
||
count++
|
||
}
|
||
}
|
||
}
|
||
return count
|
||
}
|
||
|
||
|
||
// executeScanTask 执行单个扫描任务(合并原 scheduleScanTask 和 executeSingleScan)
|
||
func executeScanTask(pluginName string, target common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
||
wg.Add(1)
|
||
*ch <- struct{}{} // 获取并发槽位
|
||
|
||
go func() {
|
||
// 开始监控插件任务
|
||
monitor := common.GetConcurrencyMonitor()
|
||
monitor.StartPluginTask()
|
||
|
||
defer func() {
|
||
// 捕获并记录任何可能的panic
|
||
if r := recover(); r != nil {
|
||
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_panic"),
|
||
pluginName, target.Host, target.Ports, r))
|
||
}
|
||
|
||
// 完成任务,释放资源
|
||
monitor.FinishPluginTask()
|
||
wg.Done()
|
||
<-*ch // 释放并发槽位
|
||
}()
|
||
|
||
// 更新统计和进度
|
||
atomic.AddInt64(&common.Num, 1)
|
||
common.UpdateProgressBar(1)
|
||
|
||
// 执行扫描(使用统一插件系统)
|
||
plugin := plugins.Get(pluginName)
|
||
if plugin != nil {
|
||
result := plugin.Scan(context.Background(), &target)
|
||
if result != nil && result.Error != nil {
|
||
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_error"), target.Host, target.Ports, result.Error))
|
||
}
|
||
}
|
||
}()
|
||
}
|
||
|
||
|
||
// 已移除未使用的 Scan 方法
|