package core import ( "fmt" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/webscan/lib" "strconv" "strings" "sync" "sync/atomic" "time" ) // ScanTask 表示单个扫描任务 type ScanTask struct { pluginName string // 插件名称 target common.HostInfo // 目标信息 } // ScanStrategy 定义扫描策略接口 type ScanStrategy interface { // 名称和描述 Name() string Description() string // 执行扫描的主要方法 Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) // 插件管理方法 GetPlugins() ([]string, bool) LogPluginInfo() // 任务准备方法 PrepareTargets(info common.HostInfo) []common.HostInfo IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool } // Scanner 扫描器结构体 type Scanner struct { strategy ScanStrategy } // NewScanner 创建新的扫描器并选择合适的策略 func NewScanner(info common.HostInfo) *Scanner { scanner := &Scanner{} scanner.selectStrategy(info) return scanner } // selectStrategy 根据扫描配置选择适当的扫描策略 func (s *Scanner) selectStrategy(info common.HostInfo) { switch { case common.LocalMode: s.strategy = NewLocalScanStrategy() common.LogBase("已选择本地扫描模式") case len(common.URLs) > 0: s.strategy = NewWebScanStrategy() common.LogBase("已选择Web扫描模式") default: s.strategy = NewServiceScanStrategy() common.LogBase(i18n.GetText("scan_mode_service_selected")) } } // Scan 执行整体扫描流程 func (s *Scanner) Scan(info common.HostInfo) { common.LogBase(i18n.GetText("scan_info_start")) lib.Inithttp() // 并发控制初始化 ch := make(chan struct{}, common.ThreadNum) wg := sync.WaitGroup{} // 执行策略 s.strategy.Execute(info, &ch, &wg) // 等待所有扫描完成 wg.Wait() s.finishScan() } // finishScan 完成扫描并输出结果 func (s *Scanner) finishScan() { // 确保进度条正确完成 if common.IsProgressActive() { common.FinishProgressBar() } // 输出扫描完成信息 common.LogBase(i18n.GetText("scan_task_complete", common.End, common.Num)) } // 任务执行通用框架 func ExecuteScanTasks(targets []common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) { // 获取要执行的插件 pluginsToRun, isCustomMode := strategy.GetPlugins() // 准备扫描任务 tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode, strategy) // 输出扫描计划 if common.ShowScanPlan && len(tasks) > 0 { logScanPlan(tasks) } // 初始化进度条 if len(tasks) > 0 && common.ShowProgress { description := i18n.GetText("progress_scanning_description") common.InitProgressBar(int64(len(tasks)), description) } // 执行所有任务 for _, task := range tasks { scheduleScanTask(task.pluginName, task.target, ch, wg) } } // 准备扫描任务列表 func prepareScanTasks(targets []common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) []ScanTask { var tasks []ScanTask for _, target := range targets { targetPort := 0 if target.Ports != "" { targetPort, _ = strconv.Atoi(target.Ports) } for _, pluginName := range pluginsToRun { plugin, exists := common.PluginManager[pluginName] if !exists { continue } // 检查插件是否适用于当前目标 (通过策略判断) if strategy.IsPluginApplicable(plugin, targetPort, isCustomMode) { tasks = append(tasks, ScanTask{ pluginName: pluginName, target: target, }) } } } return tasks } // logScanPlan 输出扫描计划信息 func logScanPlan(tasks []ScanTask) { // 统计每个插件的目标数量 pluginCounts := make(map[string]int) for _, task := range tasks { pluginCounts[task.pluginName]++ } // 构建扫描计划信息 var planInfo strings.Builder planInfo.WriteString("扫描计划:\n") for plugin, count := range pluginCounts { planInfo.WriteString(fmt.Sprintf(" - %s: %d 个目标\n", plugin, count)) } common.LogBase(planInfo.String()) } // 调度单个扫描任务 func scheduleScanTask(pluginName string, target common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { wg.Add(1) *ch <- struct{}{} // 获取并发槽位 go func() { startTime := time.Now() defer func() { // 捕获并记录任何可能的panic if r := recover(); r != nil { common.LogError(fmt.Sprintf("[PANIC] 插件 %s 扫描 %s:%s 时崩溃: %v", pluginName, target.Host, target.Ports, r)) } // 完成任务,释放资源 duration := time.Since(startTime) if common.ShowScanPlan { common.LogBase(fmt.Sprintf("完成 %s 扫描 %s:%s (耗时: %.2fs)", pluginName, target.Host, target.Ports, duration.Seconds())) } wg.Done() <-*ch // 释放并发槽位 }() atomic.AddInt64(&common.Num, 1) executeSingleScan(pluginName, target) common.UpdateProgressBar(1) }() } // 执行单个扫描 func executeSingleScan(pluginName string, info common.HostInfo) { plugin, exists := common.PluginManager[pluginName] if !exists { common.LogBase(fmt.Sprintf("扫描类型 %v 无对应插件,已跳过", pluginName)) return } if err := plugin.ScanFunc(&info); err != nil { common.LogError(fmt.Sprintf("扫描错误 %v:%v - %v", info.Host, info.Ports, err)) } } // 入口函数,向后兼容旧的调用方式 func Scan(info common.HostInfo) { scanner := NewScanner(info) scanner.Scan(info) }