fscan/Core/Scanner.go
ZacharyZcR 8a2c9737f3 enhance: 增强进度条显示和扫描配置功能
- 在扫描开始时显示详细配置信息(-t, -time, -mt, -gt参数)
- 端口扫描进度条显示线程数配置
- 进度条实时显示插件和连接并发状态
- 添加ConcurrencyMonitor并发监控系统
- 提升mt参数默认值从10到50以提高扫描性能
- 移除端口范围限制,支持全端口扫描(1-65535)
- 完善中英文国际化支持
2025-08-07 14:48:32 +08:00

169 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package core
import (
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/webscan/lib"
"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, 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()
// 完成扫描
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 {
if !GlobalPluginAdapter.PluginExists(pluginName) {
continue
}
// 检查插件是否适用于当前目标
if strategy.IsPluginApplicableByName(pluginName, 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 {
if GlobalPluginAdapter.PluginExists(pluginName) &&
strategy.IsPluginApplicableByName(pluginName, 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)
// 执行扫描(使用新插件系统)
if err := GlobalPluginAdapter.ScanWithPlugin(pluginName, &target); err != nil {
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_error"), target.Host, target.Ports, err))
}
}()
}
// Scan 入口函数,向后兼容旧的调用方式
func Scan(info common.HostInfo) {
RunScan(info)
}