fscan/core/BaseScanStrategy.go
ZacharyZcR d981f0100f perf: 优化插件系统性能,消除重复实例化问题
- 添加plugins.Exists()函数,避免不必要的插件实例创建
- 合并PluginInfo数据结构,统一插件工厂和端口信息存储
- 修复Scanner中重复调用plugins.Get()的性能问题
- 优化BaseScanStrategy.pluginExists()实现效率

主要性能改进:
* 消除21×57=1197次不必要的本地插件实例化
* 提升插件存在性检查效率,从O(n)遍历优化为O(1)查找
* 改善数据内聚性,插件元数据集中管理
* 保持所有现有插件控制逻辑和功能完整性

测试验证:
* 无-local参数时不再创建本地插件实例
* 端口匹配、Web检测、互斥验证等功能正常
* 插件注册和执行逻辑保持向后兼容
2025-08-26 19:53:57 +08:00

278 lines
7.8 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/plugins"
"strings"
)
// PluginFilterType 插件过滤类型
type PluginFilterType int
const (
FilterNone PluginFilterType = iota // 不过滤
FilterLocal // 仅本地插件
FilterService // 仅服务插件(排除本地)
FilterWeb // 仅Web插件
)
// BaseScanStrategy 扫描策略基础类
type BaseScanStrategy struct {
strategyName string
filterType PluginFilterType
}
// NewBaseScanStrategy 创建基础扫描策略
func NewBaseScanStrategy(name string, filterType PluginFilterType) *BaseScanStrategy {
return &BaseScanStrategy{
strategyName: name,
filterType: filterType,
}
}
// GetPlugins 获取插件列表(简化版)
func (b *BaseScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了特定插件且不是"all"
if common.ScanMode != "" && common.ScanMode != "all" {
requestedPlugins := parsePluginList(common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{common.ScanMode}
}
// 验证插件是否存在
var validPlugins []string
for _, name := range requestedPlugins {
if b.pluginExists(name) {
validPlugins = append(validPlugins, name)
}
}
return validPlugins, true
}
// 未指定或使用"all":获取所有插件
return plugins.All(), false
}
// IsPluginApplicable 判断插件是否适用(传统接口兼容)
func (b *BaseScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 检查插件类型过滤
switch b.filterType {
case FilterLocal:
return plugin.HasType(common.PluginTypeLocal)
case FilterService:
// 服务扫描排除本地插件允许服务和Web插件
return !plugin.HasType(common.PluginTypeLocal)
case FilterWeb:
return plugin.HasType(common.PluginTypeWeb)
default:
return true
}
}
// IsPluginApplicableByName 根据插件名称判断是否适用(新接口)
func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHost string, targetPort int, isCustomMode bool) bool {
// 首先检查插件是否存在,但不创建实例
if !b.pluginExists(pluginName) {
return false
}
// 自定义模式下强制运行所有明确指定的插件n*m调用
if isCustomMode {
return true
}
// 本地插件特殊处理:优先检查,避免不必要的端口获取
if b.isLocalPlugin(pluginName) {
result := b.isLocalPluginExplicitlySpecified(pluginName)
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
return result
}
// 检查插件端口匹配(特殊端口自动调用具体插件)
pluginPorts := b.getPluginPorts(pluginName)
// Web插件特殊处理使用智能HTTP检测
if len(pluginPorts) == 0 && b.isWebPlugin(pluginName) {
return b.isWebServicePort(targetHost, targetPort)
}
// 无端口限制的其他插件适用于所有端口
if len(pluginPorts) == 0 {
return true
}
// 有端口限制的插件:检查端口匹配
if targetPort > 0 {
for _, port := range pluginPorts {
if port == targetPort {
return true
}
}
}
return false
}
// pluginExists 检查插件是否存在,不创建实例
func (b *BaseScanStrategy) pluginExists(pluginName string) bool {
// 使用统一插件系统的Exists方法避免创建实例和遍历
return plugins.Exists(pluginName)
}
// getPluginPorts 获取插件端口列表
func (b *BaseScanStrategy) getPluginPorts(pluginName string) []int {
// 使用统一插件系统获取端口信息
return plugins.GetPluginPorts(pluginName)
}
// isWebPlugin 判断是否为Web插件
func (b *BaseScanStrategy) isWebPlugin(pluginName string) bool {
// 已知的Web插件列表
webPlugins := []string{"webtitle", "webpoc"}
for _, webPlugin := range webPlugins {
if pluginName == webPlugin {
return true
}
}
return false
}
// isLocalPlugin 判断是否为本地插件
func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool {
// 已知的本地插件列表从RegisterLocalPlugin调用中获取
localPlugins := []string{
"avdetect", "crontask", "cleaner", "dcinfo", "envinfo", "forwardshell",
"minidump", "socks5proxy", "shellenv", "downloader", "reverseshell",
"systemdservice", "fileinfo", "keylogger", "winwmi", "winstartup",
"winschtask", "systeminfo", "winregistry", "ldpreload", "winservice",
}
for _, localPlugin := range localPlugins {
if pluginName == localPlugin {
return true
}
}
return false
}
// isWebServicePort 使用智能检测判断端口是否运行Web服务
func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool {
// 创建Web端口检测器实例
detector := NewWebPortDetector()
return detector.IsWebService(host, port)
}
// isLocalPluginExplicitlySpecified 检查本地插件是否明确通过-local参数指定
func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string) bool {
// 只有通过-local参数明确指定的单个插件才能调用
return common.LocalPlugin == pluginName
}
// LogPluginInfo 输出插件信息简化版将被各Strategy重写
func (b *BaseScanStrategy) LogPluginInfo() {
// 基础实现:显示所有插件(无端口过滤)
// 各个具体Strategy应该重写这个方法以提供更精确的显示
allPlugins, isCustomMode := b.GetPlugins()
var prefix string
switch b.filterType {
case FilterLocal:
prefix = "本地插件"
case FilterService:
prefix = "服务插件"
case FilterWeb:
prefix = "Web插件"
default:
prefix = "插件"
}
if len(allPlugins) > 0 {
if isCustomMode {
common.LogBase(fmt.Sprintf("%s: 自定义指定 (%s)", prefix, strings.Join(allPlugins, ", ")))
} else {
common.LogBase(fmt.Sprintf("%s: %s", prefix, strings.Join(allPlugins, ", ")))
}
} else {
common.LogBase(fmt.Sprintf("%s: 无可用插件", prefix))
}
}
// LogPluginInfoWithPort 带端口信息的插件显示(供子类使用)
func (b *BaseScanStrategy) LogPluginInfoWithPort(targetPort int) {
allPlugins, isCustomMode := b.GetPlugins()
var prefix string
switch b.filterType {
case FilterLocal:
prefix = "本地插件"
case FilterService:
prefix = "服务插件"
case FilterWeb:
prefix = "Web插件"
default:
prefix = "插件"
}
// 过滤适用的插件
var applicablePlugins []string
for _, pluginName := range allPlugins {
if b.pluginExists(pluginName) {
if b.IsPluginApplicableByName(pluginName, "127.0.0.1", targetPort, isCustomMode) {
applicablePlugins = append(applicablePlugins, pluginName)
}
}
}
if len(applicablePlugins) > 0 {
if isCustomMode {
common.LogBase(fmt.Sprintf("%s: 自定义指定 (%s)", prefix, strings.Join(applicablePlugins, ", ")))
} else {
common.LogBase(fmt.Sprintf("%s: %s", prefix, strings.Join(applicablePlugins, ", ")))
}
} else {
common.LogBase(fmt.Sprintf("%s: 无可用插件", prefix))
}
}
// ValidateConfiguration 验证扫描配置
func (b *BaseScanStrategy) ValidateConfiguration() error {
return nil
}
// LogScanStart 输出扫描开始信息
func (b *BaseScanStrategy) LogScanStart() {
switch b.filterType {
case FilterLocal:
common.LogBase("开始本地扫描")
case FilterService:
common.LogBase("开始服务扫描")
case FilterWeb:
common.LogBase("开始Web扫描")
default:
common.LogBase("开始扫描")
}
}
// parsePluginList 解析插件列表字符串
func parsePluginList(pluginStr string) []string {
if pluginStr == "" {
return []string{}
}
// 支持逗号分隔的插件列表
plugins := strings.Split(pluginStr, ",")
var result []string
for _, plugin := range plugins {
plugin = strings.TrimSpace(plugin)
if plugin != "" {
result = append(result, plugin)
}
}
return result
}