package core import ( "fmt" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/common/parsers" "strings" "sync" ) // ServiceScanStrategy 服务扫描策略 type ServiceScanStrategy struct{} // NewServiceScanStrategy 创建新的服务扫描策略 func NewServiceScanStrategy() *ServiceScanStrategy { return &ServiceScanStrategy{} } // Name 返回策略名称 func (s *ServiceScanStrategy) Name() string { return "服务扫描" } // Description 返回策略描述 func (s *ServiceScanStrategy) Description() string { return "扫描主机服务和漏洞" } // Execute 执行服务扫描策略 func (s *ServiceScanStrategy) Execute(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 := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts) if err != nil { common.LogError(fmt.Sprintf("解析主机错误: %v", err)) return } common.LogBase(i18n.GetText("scan_host_start")) // 输出插件信息 s.LogPluginInfo() // 执行主机扫描流程 s.performHostScan(hosts, info, ch, wg) } // performHostScan 执行主机扫描的完整流程 func (s *ServiceScanStrategy) 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 s.shouldPerformLivenessCheck(hosts) { hosts = CheckLive(hosts, common.UsePing) common.LogBase(fmt.Sprintf("存活主机数量: %d", len(hosts))) } // 端口扫描 alivePorts := s.discoverAlivePorts(hosts) if len(alivePorts) > 0 { targetInfos = s.convertToTargetInfos(alivePorts, info) } } // 执行漏洞扫描 if len(targetInfos) > 0 { common.LogBase("开始漏洞扫描") ExecuteScanTasks(targetInfos, s, ch, wg) } } // shouldPerformLivenessCheck 判断是否需要执行存活性检测 func (s *ServiceScanStrategy) shouldPerformLivenessCheck(hosts []string) bool { return common.DisablePing == false && len(hosts) > 1 } // discoverAlivePorts 发现存活的端口 func (s *ServiceScanStrategy) discoverAlivePorts(hosts []string) []string { var alivePorts []string // 根据扫描模式选择端口扫描方式 if len(hosts) > 0 { alivePorts = EnhancedPortScan(hosts, common.Ports, common.Timeout) common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts))) } // 合并额外指定的端口 if len(common.HostPort) > 0 { alivePorts = append(alivePorts, common.HostPort...) alivePorts = common.RemoveDuplicate(alivePorts) common.HostPort = nil common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts))) } return alivePorts } // PrepareTargets 准备目标信息 func (s *ServiceScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo { // 解析目标主机 hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts) if err != nil { common.LogError(fmt.Sprintf("解析主机错误: %v", err)) return nil } var targetInfos []common.HostInfo // 主机存活性检测和端口扫描 if len(hosts) > 0 || len(common.HostPort) > 0 { // 主机存活检测 if s.shouldPerformLivenessCheck(hosts) { hosts = CheckLive(hosts, common.UsePing) } // 端口扫描 alivePorts := s.discoverAlivePorts(hosts) if len(alivePorts) > 0 { targetInfos = s.convertToTargetInfos(alivePorts, info) } } return targetInfos } // convertToTargetInfos 将端口列表转换为目标信息 func (s *ServiceScanStrategy) 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 } // GetPlugins 获取服务扫描插件列表 func (s *ServiceScanStrategy) GetPlugins() ([]string, bool) { // 如果指定了插件列表且不是"all" if common.ScanMode != "" && common.ScanMode != "all" { plugins := parsePluginList(common.ScanMode) if len(plugins) > 0 { return plugins, true } return []string{common.ScanMode}, true } // 未指定或使用"all":获取所有插件,由IsPluginApplicable做类型过滤 return GetAllPlugins(), false } // LogPluginInfo 输出服务扫描插件信息 func (s *ServiceScanStrategy) LogPluginInfo() { allPlugins, isCustomMode := s.GetPlugins() // 如果是自定义模式,直接显示用户指定的插件 if isCustomMode { common.LogBase(fmt.Sprintf("使用指定插件: %s", strings.Join(allPlugins, ", "))) return } // 在自动模式下,过滤掉本地插件,只显示服务类型插件 var applicablePlugins []string for _, pluginName := range allPlugins { plugin, exists := common.PluginManager[pluginName] if exists && !plugin.HasType(common.PluginTypeLocal) { applicablePlugins = append(applicablePlugins, pluginName) } } if len(applicablePlugins) > 0 { common.LogBase(fmt.Sprintf("使用服务插件: %s", strings.Join(applicablePlugins, ", "))) } else { common.LogBase(i18n.GetText("scan_no_service_plugins")) } } // IsPluginApplicable 判断插件是否适用于服务扫描 func (s *ServiceScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool { // 自定义模式下运行所有明确指定的插件 if isCustomMode { return true } // 非自定义模式下,排除本地插件 if plugin.HasType(common.PluginTypeLocal) { return false } // 检查端口是否匹配 if len(plugin.Ports) > 0 && targetPort > 0 { return plugin.HasPort(targetPort) } // 无端口限制的插件或适用于服务扫描的插件 return len(plugin.Ports) == 0 || plugin.HasType(common.PluginTypeService) }