fscan/core/ServiceScanner.go
ZacharyZcR 0cc29afbeb fix: 修复服务扫描插件列表显示本地插件的问题
修复了服务扫描模式下插件列表错误包含本地插件的显示问题:

- 在ServiceScanner的插件过滤逻辑中正确排除本地插件
- 统一新插件架构和传统插件系统的过滤规则
- 保留Web插件的智能检测功能(当检测到Web端口时自动包含)

修复前显示包含:cleaner, keylogger, winschtask, minidump 等本地插件
修复后只显示:ssh, mysql, redis, vnc 等真正的服务插件和适用的Web插件

提升了用户体验的准确性和扫描日志的可读性。
2025-08-12 13:51:26 +08:00

195 lines
5.2 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 (
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
"strconv"
"strings"
"sync"
)
// ServiceScanStrategy 服务扫描策略
type ServiceScanStrategy struct {
*BaseScanStrategy
portDiscovery *PortDiscoveryService
}
// NewServiceScanStrategy 创建新的服务扫描策略
func NewServiceScanStrategy() *ServiceScanStrategy {
return &ServiceScanStrategy{
BaseScanStrategy: NewBaseScanStrategy("服务扫描", FilterService),
portDiscovery: NewPortDiscoveryService(),
}
}
// Name 返回策略名称
func (s *ServiceScanStrategy) Name() string {
return i18n.GetText("scan_strategy_service_name")
}
// Description 返回策略描述
func (s *ServiceScanStrategy) Description() string {
return i18n.GetText("scan_strategy_service_desc")
}
// Execute 执行服务扫描策略
func (s *ServiceScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 验证扫描目标
if info.Host == "" {
common.LogError(i18n.GetText("parse_error_target_empty"))
return
}
// 输出扫描开始信息
s.LogScanStart()
// 验证插件配置
if err := s.ValidateConfiguration(); err != nil {
common.LogError(err.Error())
return
}
common.LogBase(i18n.GetText("scan_host_start"))
// 输出插件信息
s.LogPluginInfo()
// 执行主机扫描流程
s.performHostScan(info, ch, wg)
}
// performHostScan 执行主机扫描的完整流程
func (s *ServiceScanStrategy) performHostScan(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 使用端口发现服务发现目标
targetInfos, err := s.portDiscovery.DiscoverTargets(info.Host, info)
if err != nil {
common.LogError(err.Error())
return
}
// 执行漏洞扫描
if len(targetInfos) > 0 {
common.LogBase(i18n.GetText("scan_vulnerability_start"))
// 显示即将使用的漏洞扫描插件
s.LogVulnerabilityPluginInfo(targetInfos)
ExecuteScanTasks(targetInfos, s, ch, wg)
}
}
// PrepareTargets 准备目标信息
func (s *ServiceScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 使用端口发现服务发现目标
targetInfos, err := s.portDiscovery.DiscoverTargets(info.Host, info)
if err != nil {
common.LogError(err.Error())
return nil
}
return targetInfos
}
// LogVulnerabilityPluginInfo 输出服务扫描插件信息
func (s *ServiceScanStrategy) LogVulnerabilityPluginInfo(targets []common.HostInfo) {
allPlugins, isCustomMode := s.GetPlugins()
// 收集所有目标端口用于插件适用性检查
portSet := make(map[int]bool)
for _, target := range targets {
if target.Ports != "" {
if port, err := strconv.Atoi(target.Ports); err == nil {
portSet[port] = true
}
}
}
// 获取实际会被使用的插件列表(包括新插件架构和传统插件)
var servicePlugins []string
// 检查新插件架构
for _, pluginName := range allPlugins {
// 首先检查新插件架构
if factory := base.GlobalPluginRegistry.GetFactory(pluginName); factory != nil {
// 获取插件元数据检查端口匹配
metadata := factory.GetMetadata()
if s.isNewPluginApplicableToAnyPort(metadata, portSet, isCustomMode) {
servicePlugins = append(servicePlugins, pluginName)
}
continue
}
// 然后检查传统插件系统
plugin, exists := common.PluginManager[pluginName]
if !exists {
continue
}
// 检查传统插件是否对任何目标端口适用
if s.isPluginApplicableToAnyPort(plugin, portSet, isCustomMode) {
servicePlugins = append(servicePlugins, pluginName)
}
}
// 输出插件信息
if len(servicePlugins) > 0 {
common.LogBase(i18n.GetText("scan_service_plugins", strings.Join(servicePlugins, ", ")))
} else {
common.LogBase(i18n.GetText("scan_no_service_plugins"))
}
}
// isPluginApplicableToAnyPort 检查插件是否对任何端口适用(性能优化)
func (s *ServiceScanStrategy) isPluginApplicableToAnyPort(plugin common.ScanPlugin, portSet map[int]bool, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 服务扫描排除本地插件但保留Web插件有智能检测
if plugin.HasType(common.PluginTypeLocal) {
return false
}
// 无端口限制的插件适用于所有端口
if len(plugin.Ports) == 0 {
return true
}
// 有端口限制的插件:检查是否匹配任何目标端口
for port := range portSet {
if plugin.HasPort(port) {
return true
}
}
return false
}
// isNewPluginApplicableToAnyPort 检查新插件架构的插件是否对任何端口适用
func (s *ServiceScanStrategy) isNewPluginApplicableToAnyPort(metadata *base.PluginMetadata, portSet map[int]bool, isCustomMode bool) bool {
// 服务扫描排除本地插件但保留service和web类型web有智能检测
if metadata.Category == "local" {
return false
}
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 无端口限制的插件适用于所有端口
if len(metadata.Ports) == 0 {
return true
}
// 有端口限制的插件:检查是否匹配任何目标端口
for port := range portSet {
for _, pluginPort := range metadata.Ports {
if pluginPort == port {
return true
}
}
}
return false
}