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

- 扩展Web端口识别范围至40+个常见端口 - 新增HTTP/HTTPS协议智能探测机制 - 实现并发协议检测提升性能 - 添加精确错误分析避免误判 - 完善插件过滤确保Web插件仅在检测到Web服务时执行 - 支持端口模式匹配和响应头分析
191 lines
5.5 KiB
Go
191 lines
5.5 KiB
Go
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, 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 {
|
||
if !GlobalPluginAdapter.PluginExists(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 {
|
||
if GlobalPluginAdapter.PluginExists(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)
|
||
|
||
// 执行扫描(使用新插件系统)
|
||
if err := GlobalPluginAdapter.ScanWithPlugin(pluginName, &target); err != nil {
|
||
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_error"), target.Host, target.Ports, err))
|
||
}
|
||
}()
|
||
}
|
||
|
||
|
||
// 已移除未使用的 Scan 方法
|