fix: 修复扫描逻辑

This commit is contained in:
ZacharyZcR
2025-04-26 06:18:01 +08:00
parent a8bd8ca508
commit e58a48ba9b
8 changed files with 672 additions and 476 deletions
+88 -467
View File
@@ -5,7 +5,6 @@ import (
"github.com/schollz/progressbar/v3"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan/lib"
"sort"
"strconv"
"strings"
"sync"
@@ -13,31 +12,59 @@ import (
"time"
)
// 全局状态
var (
LocalScan bool // 本地扫描模式标识
WebScan bool // Web扫描模式标识
)
// ScanTask 表示单个扫描任务
type ScanTask struct {
pluginName string // 插件名称
target Common.HostInfo // 目标信息
}
// 添加一个本地插件集合,用于识别哪些插件是本地信息收集插件
var localPlugins = map[string]bool{
"localinfo": true,
"dcinfo": true,
"minidump": true,
// 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
}
// Scan 执行整体扫描流程的入口函数
func Scan(info Common.HostInfo) {
// 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.LogInfo("已选择本地扫描模式")
case len(Common.URLs) > 0:
s.strategy = NewWebScanStrategy()
Common.LogInfo("已选择Web扫描模式")
default:
s.strategy = NewServiceScanStrategy()
Common.LogInfo("已选择服务扫描模式")
}
}
// Scan 执行整体扫描流程
func (s *Scanner) Scan(info Common.HostInfo) {
Common.LogInfo("开始信息扫描")
lib.Inithttp()
@@ -45,30 +72,16 @@ func Scan(info Common.HostInfo) {
ch := make(chan struct{}, Common.ThreadNum)
wg := sync.WaitGroup{}
// 选择并执行扫描模式
selectScanMode(info, &ch, &wg)
// 执行策略
s.strategy.Execute(info, &ch, &wg)
// 等待所有扫描完成
wg.Wait()
finishScan()
s.finishScan()
}
// 根据配置选择扫描模式
func selectScanMode(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
switch {
case Common.LocalMode:
LocalScan = true
executeLocalScan(info, ch, wg)
case len(Common.URLs) > 0:
WebScan = true
executeWebScan(info, ch, wg)
default:
executeHostScan(info, ch, wg)
}
}
// 完成扫描并输出结果
func finishScan() {
// finishScan 完成扫描并输出结果
func (s *Scanner) finishScan() {
if Common.ProgressBar != nil {
Common.ProgressBar.Finish()
fmt.Println()
@@ -76,391 +89,13 @@ func finishScan() {
Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num))
}
// -----------------------------------------------------------------------------
// 三种扫描模式实现
// -----------------------------------------------------------------------------
// 执行本地信息收集
func executeLocalScan(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("执行本地信息收集")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 输出插件信息
logPluginInfo()
// 执行扫描任务
executeScanTasks([]Common.HostInfo{info}, ch, wg)
}
// 执行Web扫描
func executeWebScan(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("开始Web扫描")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 准备URL目标
targetInfos := prepareURLTargets(info)
// 输出插件信息
logPluginInfo()
// 执行扫描任务
executeScanTasks(targetInfos, ch, wg)
}
// 执行主机扫描
func executeHostScan(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 验证扫描目标
if info.Host == "" {
Common.LogError("未指定扫描目标")
return
}
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 解析目标主机
hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return
}
Common.LogInfo("开始主机扫描")
// 输出插件信息
logPluginInfo()
// 执行主机扫描
performHostScan(hosts, info, ch, wg)
}
// -----------------------------------------------------------------------------
// 主机扫描流程详细实现
// -----------------------------------------------------------------------------
// 执行主机扫描的完整流程
func performHostScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
var targetInfos []Common.HostInfo
// 主机存活性检测和端口扫描
if len(hosts) > 0 || len(Common.HostPort) > 0 {
// 主机存活检测
if shouldPerformLivenessCheck(hosts) {
hosts = CheckLive(hosts, Common.UsePing)
Common.LogInfo(fmt.Sprintf("存活主机数量: %d", len(hosts)))
}
// 端口扫描
targetInfos = scanPortsAndPrepareTargets(hosts, info)
}
// 添加URL目标
targetInfos = appendURLTargets(targetInfos, info)
// 执行漏洞扫描
if len(targetInfos) > 0 {
Common.LogInfo("开始漏洞扫描")
executeScanTasks(targetInfos, ch, wg)
}
}
// 判断是否需要执行存活性检测
func shouldPerformLivenessCheck(hosts []string) bool {
return Common.DisablePing == false && len(hosts) > 1
}
// 扫描端口并准备目标信息
func scanPortsAndPrepareTargets(hosts []string, info Common.HostInfo) []Common.HostInfo {
// 扫描存活端口
alivePorts := discoverAlivePorts(hosts)
if len(alivePorts) == 0 {
return nil
}
// 转换为目标信息
return convertToTargetInfos(alivePorts, info)
}
// 发现存活的端口
func discoverAlivePorts(hosts []string) []string {
var alivePorts []string
// 根据扫描模式选择端口扫描方式
if WebScan || len(Common.URLs) > 0 {
alivePorts = NoPortScan(hosts, Common.Ports)
} else if len(hosts) > 0 {
alivePorts = PortScan(hosts, Common.Ports, Common.Timeout)
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
// 合并额外指定的端口
if len(Common.HostPort) > 0 {
alivePorts = append(alivePorts, Common.HostPort...)
alivePorts = Common.RemoveDuplicate(alivePorts)
Common.HostPort = nil
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
return alivePorts
}
// -----------------------------------------------------------------------------
// 插件管理和解析
// -----------------------------------------------------------------------------
// getAllLocalPlugins 返回所有本地插件的名称列表
func getAllLocalPlugins() []string {
var localPluginList []string
for plugin := range localPlugins {
localPluginList = append(localPluginList, plugin)
}
sort.Strings(localPluginList)
return localPluginList
}
// parsePluginList 解析逗号分隔的插件列表
// pluginStr: 逗号分隔的插件字符串,如 "ssh,ftp,telnet"
// 返回: 插件名称的字符串切片
func parsePluginList(pluginStr string) []string {
if pluginStr == "" {
return nil
}
// 按逗号分割并去除每个插件名称两端的空白
plugins := strings.Split(pluginStr, ",")
for i, p := range plugins {
plugins[i] = strings.TrimSpace(p)
}
// 过滤空字符串
var result []string
for _, p := range plugins {
if p != "" {
result = append(result, p)
}
}
return result
}
// validateScanPlugins 验证扫描插件的有效性
// 返回: 错误信息
func validateScanPlugins() error {
// 如果未指定扫描模式或使用All模式,则无需验证
if Common.ScanMode == "" || Common.ScanMode == "All" {
return nil
}
// 解析插件列表
plugins := parsePluginList(Common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
}
// 验证每个插件是否有效
var invalidPlugins []string
for _, plugin := range plugins {
if _, exists := Common.PluginManager[plugin]; !exists {
invalidPlugins = append(invalidPlugins, plugin)
}
}
if len(invalidPlugins) > 0 {
return fmt.Errorf("无效的插件: %s", strings.Join(invalidPlugins, ", "))
}
// 如果是本地模式,验证是否包含非本地插件
if Common.LocalMode {
var nonLocalPlugins []string
for _, plugin := range plugins {
if !isLocalPlugin(plugin) {
nonLocalPlugins = append(nonLocalPlugins, plugin)
}
}
if len(nonLocalPlugins) > 0 {
Common.LogInfo(fmt.Sprintf("本地模式下,以下非本地插件将被忽略: %s", strings.Join(nonLocalPlugins, ", ")))
}
}
return nil
}
// isLocalPlugin 判断插件是否为本地信息收集插件
func isLocalPlugin(pluginName string) bool {
return localPlugins[pluginName]
}
// getPluginsToRun 获取要执行的插件列表
// 返回: 插件列表和是否为自定义插件模式
func getPluginsToRun() ([]string, bool) {
// 本地模式处理
if Common.LocalMode {
// 在本地模式下只执行本地插件
// 如果指定了特定插件(单个或多个)
if Common.ScanMode != "" && Common.ScanMode != "All" {
requestedPlugins := parsePluginList(Common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
}
// 过滤出本地插件
var localPluginsToRun []string
for _, plugin := range requestedPlugins {
if isLocalPlugin(plugin) {
localPluginsToRun = append(localPluginsToRun, plugin)
}
}
return localPluginsToRun, true
}
// 如果是All模式或未指定,则返回所有本地插件
return getAllLocalPlugins(), true
}
// 非本地模式处理(保持原有行为)
// 如果指定了插件列表(逗号分隔)
if Common.ScanMode != "" && Common.ScanMode != "All" {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) > 0 {
return plugins, true
}
return []string{Common.ScanMode}, true
}
// 默认情况:使用所有非本地插件
allPlugins := GetAllPlugins()
filteredPlugins := make([]string, 0, len(allPlugins))
for _, plugin := range allPlugins {
if !isLocalPlugin(plugin) {
filteredPlugins = append(filteredPlugins, plugin)
}
}
return filteredPlugins, false
}
// logPluginInfo 输出插件信息
func logPluginInfo() {
if Common.LocalMode {
if Common.ScanMode == "" || Common.ScanMode == "All" {
Common.LogInfo("本地模式: 使用所有本地信息收集插件")
} else {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
}
// 过滤出本地插件
var localPluginsToRun []string
for _, plugin := range plugins {
if isLocalPlugin(plugin) {
localPluginsToRun = append(localPluginsToRun, plugin)
}
}
if len(localPluginsToRun) > 1 {
Common.LogInfo(fmt.Sprintf("本地模式: 使用本地插件: %s", strings.Join(localPluginsToRun, ", ")))
} else if len(localPluginsToRun) == 1 {
Common.LogInfo(fmt.Sprintf("本地模式: 使用本地插件: %s", localPluginsToRun[0]))
} else {
Common.LogInfo("本地模式: 未指定有效的本地插件,将不执行任何扫描")
}
}
return
}
// 非本地模式的原有逻辑
if Common.ScanMode == "" || Common.ScanMode == "All" {
Common.LogInfo("使用所有可用插件(已排除本地敏感插件)")
} else {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) > 1 {
Common.LogInfo(fmt.Sprintf("使用插件: %s", strings.Join(plugins, ", ")))
} else {
Common.LogInfo(fmt.Sprintf("使用插件: %s", Common.ScanMode))
}
}
}
// -----------------------------------------------------------------------------
// 目标准备
// -----------------------------------------------------------------------------
// 准备URL目标列表
func prepareURLTargets(baseInfo Common.HostInfo) []Common.HostInfo {
var targetInfos []Common.HostInfo
for _, url := range Common.URLs {
urlInfo := baseInfo
// 确保URL包含协议头
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "http://" + url
}
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
return targetInfos
}
// 将端口列表转换为目标信息
func convertToTargetInfos(ports []string, baseInfo Common.HostInfo) []Common.HostInfo {
var infos []Common.HostInfo
for _, targetIP := range ports {
hostParts := strings.Split(targetIP, ":")
if len(hostParts) != 2 {
Common.LogError(fmt.Sprintf("无效的目标地址格式: %s", targetIP))
continue
}
info := baseInfo
info.Host = hostParts[0]
info.Ports = hostParts[1]
infos = append(infos, info)
}
return infos
}
// 添加URL扫描目标
func appendURLTargets(targetInfos []Common.HostInfo, baseInfo Common.HostInfo) []Common.HostInfo {
for _, url := range Common.URLs {
urlInfo := baseInfo
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
return targetInfos
}
// -----------------------------------------------------------------------------
// 任务执行
// -----------------------------------------------------------------------------
// 执行扫描任务集合
func executeScanTasks(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 任务执行通用框架
func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
// 获取要执行的插件
pluginsToRun, isCustomMode := getPluginsToRun()
pluginsToRun, isCustomMode := strategy.GetPlugins()
// 准备扫描任务
tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode)
tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode, strategy)
// 输出扫描计划
if Common.ShowScanPlan && len(tasks) > 0 {
@@ -478,6 +113,35 @@ func executeScanTasks(targets []Common.HostInfo, ch *chan struct{}, wg *sync.Wai
}
}
// 准备扫描任务列表
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) {
// 统计每个插件的目标数量
@@ -497,55 +161,6 @@ func logScanPlan(tasks []ScanTask) {
Common.LogInfo(planInfo.String())
}
// 准备扫描任务列表
func prepareScanTasks(targets []Common.HostInfo, pluginsToRun []string, isCustomMode bool) []ScanTask {
var tasks []ScanTask
for _, target := range targets {
targetPort, _ := strconv.Atoi(target.Ports)
for _, pluginName := range pluginsToRun {
plugin, exists := Common.PluginManager[pluginName]
if !exists {
continue
}
// 检查插件是否适用于当前目标
if isPluginApplicable(plugin, targetPort, isCustomMode, pluginName) {
tasks = append(tasks, ScanTask{
pluginName: pluginName,
target: target,
})
}
}
}
return tasks
}
// isPluginApplicable 判断插件是否适用于目标
func isPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool, pluginName string) bool {
// 本地模式下,只执行本地插件
if LocalScan {
return isLocalPlugin(pluginName)
}
// 非本地模式下,本地插件特殊处理
if isLocalPlugin(pluginName) {
// 只有在自定义模式下明确指定时才执行本地插件
return isCustomMode
}
// 特殊扫描模式下的处理
if WebScan || isCustomMode {
return true
}
// 端口匹配检查
// 无端口限制的插件或端口匹配的插件
return len(plugin.Ports) == 0 || plugin.HasPort(targetPort)
}
// 初始化进度条
func initProgressBar(totalTasks int) {
Common.ProgressBar = progressbar.NewOptions(totalTasks,
@@ -623,3 +238,9 @@ func updateProgress() {
Common.ProgressBar.Add(1)
}
}
// 入口函数,向后兼容旧的调用方式
func Scan(info Common.HostInfo) {
scanner := NewScanner(info)
scanner.Scan(info)
}