diff --git a/Common/Bridge.go b/Common/Bridge.go deleted file mode 100644 index 1887a7c..0000000 --- a/Common/Bridge.go +++ /dev/null @@ -1,430 +0,0 @@ -package common - -/* -Bridge.go - 统一桥接模块 - -将Config.go、Log.go、Output.go、Proxy.go的桥接功能合并到一个文件中, -减少文件数量,提高代码组织性。保持所有原有API的完全兼容性。 -*/ - -import ( - "context" - "crypto/tls" - "fmt" - "io" - "log" - "net" - "sync" - "time" - - "github.com/fatih/color" - "github.com/schollz/progressbar/v3" - - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/logging" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/common/proxy" -) - -// ============================================================================= -// 配置桥接 (Config.go 功能) -// ============================================================================= - -var version = "2.0.2" - -// 向后兼容的输出配置变量 -var ( - Outputfile string - OutputFormat string - ProgressBar *progressbar.ProgressBar - OutputMutex sync.Mutex -) - -// PocInfo POC详细信息结构 -type PocInfo = config.PocInfo - -func syncOutputConfig() { - cfg := config.GetGlobalConfig() - if cfg != nil && cfg.Output != nil { - Outputfile = cfg.Output.Outputfile - OutputFormat = cfg.Output.OutputFormat - } - ProgressBar = config.GetGlobalProgressBar() -} - -// ============================================================================= -// 日志桥接 (Log.go 功能) -// ============================================================================= - -// 全局日志状态和变量 -var ( - status = logging.NewScanStatus() - Num, End int64 - StartTime = time.Now() - globalLogger *logging.Logger - loggerMutex sync.Mutex -) - -// 日志级别常量 -const ( - LogLevelAll = string(logging.LevelAll) - LogLevelError = string(logging.LevelError) - LogLevelBase = string(logging.LevelBase) - LogLevelInfo = string(logging.LevelInfo) - LogLevelSuccess = string(logging.LevelSuccess) - LogLevelDebug = string(logging.LevelDebug) - LogLevelInfoSuccess = string(logging.LevelInfoSuccess) - LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess) -) - -// 类型别名 -type LogEntry = logging.LogEntry -type ScanStatus = logging.ScanStatus -type ProgressDisplay = logging.ProgressDisplay - -func getGlobalLogger() *logging.Logger { - loggerMutex.Lock() - defer loggerMutex.Unlock() - - if globalLogger == nil { - // 获取正确的日志级别 - 动态获取确保使用最新值 - level := getLogLevelFromString(LogLevel) - - config := &logging.LoggerConfig{ - Level: level, EnableColor: !NoColor, SlowOutput: SlowLogOutput, - ShowProgress: true, StartTime: StartTime, - LevelColors: map[logging.LogLevel]color.Attribute{ - logging.LevelError: color.FgBlue, logging.LevelBase: color.FgYellow, - logging.LevelInfo: color.FgGreen, logging.LevelSuccess: color.FgRed, logging.LevelDebug: color.FgWhite, - }, - } - globalLogger = logging.NewLogger(config) - if ProgressBar != nil { - globalLogger.SetProgressBar(ProgressBar) - } - globalLogger.SetOutputMutex(&OutputMutex) - status = globalLogger.GetScanStatus() - } - return globalLogger -} - -// shouldLogLevel 检查是否应该记录该级别的日志 -func shouldLogLevel(logger *logging.Logger, level logging.LogLevel) bool { - // 直接基于当前的LogLevel配置判断 - switch LogLevel { - case "all", "ALL": - return true - case "error", "ERROR": - return level == logging.LevelError - case "base", "BASE": - return level == logging.LevelBase - case "info", "INFO": - return level == logging.LevelInfo - case "success", "SUCCESS": - return level == logging.LevelSuccess - case "debug", "DEBUG": - return level == logging.LevelDebug || level == logging.LevelError - case "info,success", "INFO_SUCCESS": - return level == logging.LevelInfo || level == logging.LevelSuccess - case "base,info,success", "BASE_INFO_SUCCESS": - return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess - default: - // 默认使用BaseInfoSuccess级别,不包含DEBUG和ERROR级别 - return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess - } -} - -// getLogLevelFromString 根据字符串获取日志级别 -func getLogLevelFromString(levelStr string) logging.LogLevel { - switch levelStr { - case "all", "ALL": - return logging.LevelAll - case "error", "ERROR": - return logging.LevelError - case "base", "BASE": - return logging.LevelBase - case "info", "INFO": - return logging.LevelInfo - case "success", "SUCCESS": - return logging.LevelSuccess - case "debug", "DEBUG": - return logging.LevelDebug - case "info,success": - return logging.LevelInfoSuccess - case "base,info,success", "BASE_INFO_SUCCESS": - return logging.LevelBaseInfoSuccess - default: - // 默认使用InfoSuccess级别,只显示Info和Success消息 - return logging.LevelInfoSuccess - } -} - -// 日志相关函数 -func InitLogger() { - // 重置全局logger以确保使用最新的LogLevel配置 - loggerMutex.Lock() - globalLogger = nil - loggerMutex.Unlock() - - log.SetOutput(io.Discard) - getGlobalLogger().Initialize() -} -func LogDebug(msg string) { logWithProgressCoordination(msg, "debug") } -func LogBase(msg string) { logWithProgressCoordination(msg, "base") } -func LogInfo(msg string) { logWithProgressCoordination(msg, "info") } -func LogSuccess(result string) { logWithProgressCoordination(result, "success") } -func LogError(errMsg string) { logWithProgressCoordination(errMsg, "error") } -func CheckErrs(err error) error { return logging.CheckErrs(err) } - -// logWithProgressCoordination 协调日志输出与进度条的冲突 -func logWithProgressCoordination(msg, level string) { - logger := getGlobalLogger() - - // 首先检查是否应该记录这个级别的日志 - var logLevel logging.LogLevel - switch level { - case "debug": - logLevel = logging.LevelDebug - case "base": - logLevel = logging.LevelBase - case "info": - logLevel = logging.LevelInfo - case "success": - logLevel = logging.LevelSuccess - case "error": - logLevel = logging.LevelError - default: - logLevel = logging.LevelDebug - } - - // 如果当前日志级别不应该显示这条消息,直接返回 - if !shouldLogLevel(logger, logLevel) { - return - } - - // 如果进度条活跃,使用协调输出 - if IsProgressActive() { - // 简单格式化消息,保持时间戳格式一致 - elapsed := time.Since(StartTime) - var prefix, colorCode, resetCode string - - if !NoColor { - resetCode = "\033[0m" - switch level { - case "debug": - colorCode = "\033[37m" // 白色 - prefix = " " - case "base": - colorCode = "\033[33m" // 黄色 - prefix = " " - case "info": - colorCode = "\033[32m" // 绿色 - prefix = " [*] " - case "success": - colorCode = "\033[31m" // 红色 - prefix = " [+] " - case "error": - colorCode = "\033[34m" // 蓝色 - prefix = " [-] " - } - } else { - switch level { - case "info": - prefix = " [*] " - case "success": - prefix = " [+] " - case "error": - prefix = " [-] " - default: - prefix = " " - } - } - - formattedMsg := fmt.Sprintf("[%.1fs]%s%s%s%s", - elapsed.Seconds(), prefix, colorCode, msg, resetCode) - LogWithProgress(formattedMsg) - } else { - // 如果进度条不活跃,使用原始日志方法 - switch level { - case "debug": - logger.Debug(msg) - case "base": - logger.Base(msg) - case "info": - logger.Info(msg) - case "success": - logger.Success(msg) - case "error": - logger.Error(msg) - } - } -} - -// ============================================================================= -// 输出桥接 (Output.go 功能) -// ============================================================================= - -// 全局输出管理器 -var ResultOutput *OutputManager - -type OutputManager struct{ manager *output.Manager } -type ResultType = output.ResultType - -const ( - HOST ResultType = output.TypeHost - PORT ResultType = output.TypePort - SERVICE ResultType = output.TypeService - VULN ResultType = output.TypeVuln -) - -type ScanResult = output.ScanResult - -func createOutputManager(outputPath, outputFormat string) (*OutputManager, error) { - var format output.OutputFormat - switch outputFormat { - case "txt": - format = output.FormatTXT - case "json": - format = output.FormatJSON - case "csv": - format = output.FormatCSV - default: - return nil, fmt.Errorf(i18n.GetText("output_format_invalid", outputFormat)) - } - - config := output.DefaultManagerConfig(outputPath, format) - manager, err := output.NewManager(config) - if err != nil { - return nil, err - } - return &OutputManager{manager: manager}, nil -} - -// 输出相关函数 -func InitOutput() error { - LogDebug(i18n.GetText("output_init_start")) - switch OutputFormat { - case "txt", "json", "csv": - default: - return fmt.Errorf(i18n.GetText("output_format_invalid", OutputFormat)) - } - if Outputfile == "" { - return fmt.Errorf(i18n.GetText("output_path_empty")) - } - - manager, err := createOutputManager(Outputfile, OutputFormat) - if err != nil { - LogDebug(i18n.GetText("output_init_failed", err)) - return fmt.Errorf(i18n.GetText("output_init_failed", err)) - } - ResultOutput = manager - output.SetGlobalManager(manager.manager) - LogDebug(i18n.GetText("output_init_success")) - return nil -} - -func (om *OutputManager) saveResult(result *ScanResult) error { - if om.manager == nil { - return fmt.Errorf(i18n.GetText("output_not_init")) - } - LogDebug(i18n.GetText("output_saving_result", result.Type, result.Target)) - return om.manager.SaveResult(result) -} - -func SaveResult(result *ScanResult) error { - if ResultOutput == nil { - LogDebug(i18n.GetText("output_not_init")) - return fmt.Errorf(i18n.GetText("output_not_init")) - } - return ResultOutput.saveResult(result) -} - -func CloseOutput() error { - if ResultOutput == nil { - return nil - } - LogDebug(i18n.GetText("output_closing")) - err := ResultOutput.manager.Close() - if err != nil { - return fmt.Errorf(i18n.GetText("output_close_failed", err)) - } - LogDebug(i18n.GetText("output_closed")) - return nil -} - -// ============================================================================= -// 代理桥接 (Proxy.go 功能) -// ============================================================================= - -// 代理相关函数 -func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - return proxy.DialContextWithProxy(ctx, network, address) -} - -func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) { - return proxy.DialContextWithProxy(ctx, network, address) -} - -func Socks5Dialer(forward *net.Dialer) (interface{}, error) { - if err := syncProxyConfig(); err != nil { - return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err)) - } - manager := proxy.GetGlobalProxy() - dialer, err := manager.GetDialer() - if err != nil { - return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err)) - } - return dialer, nil -} - -func WrapperTlsWithContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) { - if err := syncProxyConfig(); err != nil { - LogError(i18n.GetText("proxy_config_sync_failed", err.Error())) - } - conn, err := proxy.DialTLSContextWithProxy(ctx, network, address, tlsConfig) - if err != nil { - LogError(i18n.GetText("tls_conn_failed", err.Error())) - return nil, fmt.Errorf(i18n.GetText("tls_conn_failed", err)) - } - return conn, nil -} - -func syncProxyConfig() error { - var proxyURL string - if Socks5Proxy != "" { - proxyURL = Socks5Proxy - } else if HttpProxy != "" { - proxyURL = HttpProxy - } - - if proxy.IsProxyEnabledGlobally() { - currentAddr := proxy.GetGlobalProxyAddress() - if (proxyURL == "" && currentAddr == "") || - (proxyURL != "" && currentAddr != "" && (Socks5Proxy == currentAddr || HttpProxy == currentAddr)) { - return nil - } - } - - if err := proxy.InitGlobalProxy(proxyURL); err != nil { - return fmt.Errorf(i18n.GetText("proxy_init_failed", err)) - } - - if proxyURL != "" { - LogBase(i18n.GetText("proxy_enabled", proxy.GetGlobalProxyType(), proxy.GetGlobalProxyAddress())) - } else { - LogBase(i18n.GetText("proxy_disabled")) - } - return nil -} - -// ============================================================================= -// 初始化函数 -// ============================================================================= - -func init() { - config.SetGlobalVersion(version) - syncOutputConfig() -} diff --git a/Common/Core/Manager.go b/Common/Core/Manager.go index b06b925..e8c0f1e 100644 --- a/Common/Core/Manager.go +++ b/Common/Core/Manager.go @@ -42,10 +42,10 @@ var ( LocalMode bool // 本地模式 // 基础认证配置 - Username string // 用户名 - Password string // 密码 - Userdict map[string][]string // 用户字典 - Passwords []string // 密码列表 + Username string // 用户名 + Password string // 密码 + Userdict map[string][]string // 用户字典 + Passwords []string // 密码列表 // 网络配置 HttpProxy string // HTTP代理 @@ -130,4 +130,4 @@ func SetThreadNum(num int) { // init 自动初始化 func init() { InitGlobalConfig() -} \ No newline at end of file +} diff --git a/Common/Parse.go b/Common/Parse.go index d2575c9..9b2fbaf 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -131,6 +131,9 @@ func Parse(Info *HostInfo) error { // 显示解析结果摘要 showParseSummary(result.Config) + // 同步变量到core包 + syncToCore() + return nil } @@ -498,6 +501,9 @@ func applyLogLevel() { newLogger.SetProgressBar(ProgressBar) } newLogger.SetOutputMutex(&OutputMutex) + + // 设置协调输出函数,使用LogWithProgress + newLogger.SetCoordinatedOutput(LogWithProgress) // 更新全局日志管理器 globalLogger = newLogger diff --git a/Common/ProgressManager.go b/Common/ProgressManager.go index bb5b2a3..5ab8b1e 100644 --- a/Common/ProgressManager.go +++ b/Common/ProgressManager.go @@ -72,6 +72,9 @@ func (pm *ProgressManager) InitProgress(total int64, description string) { pm.isActive = true pm.enabled = true + // 为进度条保留空间 + pm.setupProgressSpace() + // 初始显示进度条 pm.renderProgress() } @@ -130,24 +133,23 @@ func (pm *ProgressManager) FinishProgress() { pm.isActive = false } -// reserveProgressSpace 为进度条保留底部空间 -func (pm *ProgressManager) reserveProgressSpace() { - if pm.terminalHeight <= 0 { - return - } - - // 移动到底部保留区域上方 - targetLine := pm.terminalHeight - pm.reservedLines - fmt.Printf("\033[%d;1H", targetLine) - - // 在底部创建空行 - for i := 0; i < pm.reservedLines; i++ { - fmt.Println() - } - - // 回到内容输出位置 - fmt.Printf("\033[%d;1H", targetLine) - pm.lastContentLine = targetLine - 1 +// setupProgressSpace 设置进度条空间 +func (pm *ProgressManager) setupProgressSpace() { + // 简化设计:进度条在原地更新,不需要预留额外空间 + // 只是标记进度条开始的位置 + pm.lastContentLine = 0 +} + +// moveToContentArea 移动到内容输出区域 +func (pm *ProgressManager) moveToContentArea() { + // 对于简化的实现,直接在当前位置输出内容 + // 不需要复杂的光标移动 +} + +// moveToProgressLine 移动到进度条位置 +func (pm *ProgressManager) moveToProgressLine() { + // 移动到行首准备重绘进度条 + fmt.Print("\r") } // renderProgress 渲染进度条(使用锁避免输出冲突) @@ -155,9 +157,6 @@ func (pm *ProgressManager) renderProgress() { pm.outputMutex.Lock() defer pm.outputMutex.Unlock() - // 清除当前行并回到行首 - fmt.Print("\033[2K\r") - pm.renderProgressUnsafe() } @@ -308,13 +307,13 @@ func LogWithProgress(message string) { pm.outputMutex.Lock() defer pm.outputMutex.Unlock() - // 清除当前进度条 + // 清除当前行(清除进度条) fmt.Print("\033[2K\r") - // 输出消息 + // 输出日志消息 fmt.Println(message) - // 重新绘制进度条 + // 重绘进度条 pm.renderProgressUnsafe() } @@ -324,6 +323,9 @@ func (pm *ProgressManager) renderProgressUnsafe() { return } + // 移动到行首并清除当前行 + fmt.Print("\033[2K\r") + // 生成进度条内容 progressBar := pm.generateProgressBar() diff --git a/Common/Types.go b/Common/Types.go deleted file mode 100644 index 05ef803..0000000 --- a/Common/Types.go +++ /dev/null @@ -1,52 +0,0 @@ -package common - -import "github.com/shadow1ng/fscan/common/core" - -/* -Types.go - 类型定义(向后兼容层) - -此文件保持向后兼容,实际类型定义和插件系统已迁移到Core/Plugin.go -*/ - -// ============================================================================= -// 向后兼容的类型别名 -// ============================================================================= - -// HostInfo 主机信息结构 - 引用Core包中的定义 -type HostInfo = core.HostInfo - -// ScanPlugin 扫描插件结构 - 引用Core包中的定义 -type ScanPlugin = core.ScanPlugin - -// ============================================================================= -// 向后兼容的插件类型常量 -// ============================================================================= - -const ( - PluginTypeService = core.PluginTypeService // 服务类型插件 - PluginTypeWeb = core.PluginTypeWeb // Web类型插件 - PluginTypeLocal = core.PluginTypeLocal // 本地类型插件 - PluginTypeBrute = core.PluginTypeBrute // 暴力破解插件 - PluginTypePoc = core.PluginTypePoc // POC验证插件 - PluginTypeScan = core.PluginTypeScan // 扫描探测插件 -) - -// ============================================================================= -// 向后兼容的插件管理函数 -// ============================================================================= - -// RegisterPlugin 注册插件到全局管理器 - 保持向后兼容 -func RegisterPlugin(name string, plugin ScanPlugin) { - if err := core.RegisterPlugin(name, plugin); err != nil { - // 注册失败时记录错误,但不中断程序 - LogError("Failed to register plugin " + name + ": " + err.Error()) - } -} - -// GetGlobalPluginManager 获取全局插件管理器 -func GetGlobalPluginManager() *core.PluginManager { - return core.GetGlobalPluginManager() -} - -// 向后兼容的全局变量 - 引用Core包中的定义 -var PluginManager = core.LegacyPluginManager diff --git a/Common/Variables.go b/Common/Variables.go deleted file mode 100644 index c06dcc9..0000000 --- a/Common/Variables.go +++ /dev/null @@ -1,97 +0,0 @@ -package common - -import "github.com/shadow1ng/fscan/common/core" - -/* -Variables.go - 全局变量(向后兼容层) - -此文件保持向后兼容,实际变量管理已迁移到Core/Manager.go -建议新代码使用Core.GetGlobalConfigManager()获取配置管理器 -*/ - -// ============================================================================= -// 向后兼容的全局变量 - 直接引用Core包中的定义 -// ============================================================================= - -// 核心扫描配置 -var ( - ScanMode = core.ScanMode // 扫描模式 - ThreadNum = core.ThreadNum // 线程数 - Timeout = core.Timeout // 超时时间 - DisablePing = core.DisablePing // 禁用ping - LocalMode = core.LocalMode // 本地模式 -) - -// 基础认证配置 -var ( - Username = core.Username // 用户名 - Password = core.Password // 密码 - Userdict = core.Userdict // 用户字典 - Passwords = core.Passwords // 密码列表 -) - -// 网络配置 -var ( - HttpProxy = core.HttpProxy // HTTP代理 - Socks5Proxy = core.Socks5Proxy // SOCKS5代理 -) - -// 显示控制 -var ( - NoColor = core.NoColor // 禁用颜色 - Language = core.Language // 语言 - LogLevel = core.LogLevel // 日志级别 -) - -// 端口映射 -var ( - PortMap = core.PortMap // 端口映射 - DefaultMap = core.DefaultMap // 默认映射 -) - -// 输出配置 (已在Bridge.go中定义,此处不重复声明) -// var Outputfile, OutputFormat - -// 其他全局状态 (已在Flag.go中定义,此处不重复声明) -// var SlowLogOutput - -// ============================================================================= -// 向后兼容的访问函数 -// ============================================================================= - -// GetGlobalConfigManager 获取全局配置管理器(已废弃) -// 注意:ConfigManager 已被移除,此函数不再可用 -// func GetGlobalConfigManager() *core.ConfigManager { -// return core.GetGlobalConfigManager() -// } - -// InitGlobalConfig 初始化全局配置 -func InitGlobalConfig() { - core.InitGlobalConfig() -} - -// GetScanMode 获取扫描模式 -func GetScanMode() string { - return core.GetScanMode() -} - -// SetScanMode 设置扫描模式 -func SetScanMode(mode string) { - core.SetScanMode(mode) -} - -// GetThreadNum 获取线程数 -func GetThreadNum() int { - return core.GetThreadNum() -} - -// SetThreadNum 设置线程数 -func SetThreadNum(num int) { - core.SetThreadNum(num) -} - -// ============================================================================= -// 向后兼容的日志级别常量 (已在Bridge.go中定义,此处不重复声明) -// ============================================================================= - -// const LogLevelAll, LogLevelError, etc. - 已在Bridge.go中定义 \ No newline at end of file diff --git a/Common/common.go b/Common/common.go new file mode 100644 index 0000000..e72209d --- /dev/null +++ b/Common/common.go @@ -0,0 +1,202 @@ +package common + +/* +common.go - 简化的统一入口 + +移除所有向后兼容层,提供清晰的模块化接口。 +直接导出各子模块的核心功能,避免代码债务。 +*/ + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "sync" + "time" + + "github.com/shadow1ng/fscan/common/core" + "github.com/shadow1ng/fscan/common/logging" + "github.com/shadow1ng/fscan/common/output" +) + +// ============================================================================= +// 核心类型导出 - 直接从core模块导出 +// ============================================================================= + +type HostInfo = core.HostInfo +type ScanPlugin = core.ScanPlugin + +// 插件类型常量 +const ( + PluginTypeService = core.PluginTypeService + PluginTypeWeb = core.PluginTypeWeb + PluginTypeLocal = core.PluginTypeLocal + PluginTypeBrute = core.PluginTypeBrute + PluginTypePoc = core.PluginTypePoc + PluginTypeScan = core.PluginTypeScan +) + +// 全局插件管理器 +var PluginManager = core.LegacyPluginManager + +// ============================================================================= +// 核心功能导出 - 直接调用对应模块 +// ============================================================================= + +// 插件系统 +func RegisterPlugin(name string, plugin ScanPlugin) { + if err := core.RegisterPlugin(name, plugin); err != nil { + LogError("Failed to register plugin " + name + ": " + err.Error()) + } +} + +func GetGlobalPluginManager() *core.PluginManager { + return core.GetGlobalPluginManager() +} + +// ============================================================================= +// 日志系统简化接口 +// ============================================================================= + +var globalLogger *logging.Logger +var loggerMutex sync.Mutex + +func getGlobalLogger() *logging.Logger { + loggerMutex.Lock() + defer loggerMutex.Unlock() + + if globalLogger == nil { + level := getLogLevelFromString(LogLevel) + config := &logging.LoggerConfig{ + Level: level, + EnableColor: !NoColor, + SlowOutput: SlowLogOutput, + ShowProgress: true, + StartTime: StartTime, + } + globalLogger = logging.NewLogger(config) + if ProgressBar != nil { + globalLogger.SetProgressBar(ProgressBar) + } + globalLogger.SetOutputMutex(&OutputMutex) + + // 设置协调输出函数,使用LogWithProgress + globalLogger.SetCoordinatedOutput(LogWithProgress) + } + return globalLogger +} + +func getLogLevelFromString(levelStr string) logging.LogLevel { + switch levelStr { + case "all", "ALL": + return logging.LevelAll + case "error", "ERROR": + return logging.LevelError + case "base", "BASE": + return logging.LevelBase + case "info", "INFO": + return logging.LevelInfo + case "success", "SUCCESS": + return logging.LevelSuccess + case "debug", "DEBUG": + return logging.LevelDebug + case "info,success": + return logging.LevelInfoSuccess + case "base,info,success", "BASE_INFO_SUCCESS": + return logging.LevelBaseInfoSuccess + default: + return logging.LevelInfoSuccess + } +} + +// 日志函数 +func InitLogger() { + loggerMutex.Lock() + globalLogger = nil + loggerMutex.Unlock() + getGlobalLogger().Initialize() +} + +func LogDebug(msg string) { getGlobalLogger().Debug(msg) } +func LogBase(msg string) { getGlobalLogger().Base(msg) } +func LogInfo(msg string) { getGlobalLogger().Info(msg) } +func LogSuccess(result string) { getGlobalLogger().Success(result) } +func LogError(errMsg string) { getGlobalLogger().Error(errMsg) } + +// ============================================================================= +// 输出系统简化接口 +// ============================================================================= + +var ResultOutput *output.Manager + +func InitOutput() error { + if Outputfile == "" { + return fmt.Errorf("output file not specified") + } + + var format output.OutputFormat + switch OutputFormat { + case "txt": + format = output.FormatTXT + case "json": + format = output.FormatJSON + case "csv": + format = output.FormatCSV + default: + return fmt.Errorf("invalid output format: %s", OutputFormat) + } + + config := output.DefaultManagerConfig(Outputfile, format) + manager, err := output.NewManager(config) + if err != nil { + return err + } + ResultOutput = manager + output.SetGlobalManager(manager) + return nil +} + +func CloseOutput() error { + if ResultOutput == nil { + return nil + } + return ResultOutput.Close() +} + +func SaveResult(result *output.ScanResult) error { + if ResultOutput == nil { + return fmt.Errorf("output not initialized") + } + return ResultOutput.SaveResult(result) +} + +// ============================================================================= +// 网络连接辅助函数 +// ============================================================================= + +// WrapperTcpWithTimeout TCP连接包装器,带超时 +func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout(network, address, timeout) +} + +// WrapperTcpWithContext TCP连接包装器,带上下文 +func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, network, address) +} + +// WrapperTlsWithContext TLS连接包装器,带上下文 +func WrapperTlsWithContext(ctx context.Context, network, address string, config *tls.Config) (net.Conn, error) { + d := &tls.Dialer{Config: config} + return d.DialContext(ctx, network, address) +} + +// ============================================================================= +// 错误处理辅助函数 +// ============================================================================= + +// CheckErrs 检查单个错误 - 简化版本 +func CheckErrs(err error) error { + return err +} \ No newline at end of file diff --git a/Common/globals.go b/Common/globals.go new file mode 100644 index 0000000..1fc060e --- /dev/null +++ b/Common/globals.go @@ -0,0 +1,169 @@ +package common + +import ( + "sync" + "time" + + "github.com/schollz/progressbar/v3" + "github.com/shadow1ng/fscan/common/core" + "github.com/shadow1ng/fscan/common/logging" +) + +/* +globals.go - 全局变量定义 + +直接导出core模块的变量,避免兼容层重定向。 +这些变量被Flag.go和其他模块直接使用。 +*/ + +// ============================================================================= +// 版本信息 +// ============================================================================= + +var version = "2.0.2" + +// ============================================================================= +// 核心扫描配置 - 直接使用core包变量 +// ============================================================================= + +var ( + ScanMode string // 扫描模式 + ThreadNum int // 线程数 + Timeout int64 // 超时时间 + DisablePing bool // 禁用ping + LocalMode bool // 本地模式 +) + +// ============================================================================= +// 基础认证配置 - 直接定义 +// ============================================================================= + +var ( + Username string // 用户名 + Password string // 密码 + Userdict map[string][]string // 用户字典 + Passwords []string // 密码列表 +) + +// ============================================================================= +// 网络配置 - 直接定义 +// ============================================================================= + +var ( + HttpProxy string // HTTP代理 + Socks5Proxy string // SOCKS5代理 +) + +// ============================================================================= +// 显示控制 - 直接定义 +// ============================================================================= + +var ( + NoColor bool // 禁用颜色 + Language string // 语言 + LogLevel string // 日志级别 +) + +// ============================================================================= +// 端口映射 - 直接定义 +// ============================================================================= + +var ( + PortMap map[int][]string // 端口映射 + DefaultMap []string // 默认映射 +) + +// ============================================================================= +// 其他全局变量 - 直接定义,避免多层引用 +// ============================================================================= + +var ( + // 输出配置 + Outputfile string + OutputFormat string + ProgressBar *progressbar.ProgressBar + OutputMutex sync.Mutex + + // 日志状态 + status = logging.NewScanStatus() + Num, End int64 + StartTime = time.Now() + + // 其他变量按需添加 +) + +// ============================================================================= +// 日志级别常量 +// ============================================================================= + +const ( + LogLevelAll = string(logging.LevelAll) + LogLevelError = string(logging.LevelError) + LogLevelBase = string(logging.LevelBase) + LogLevelInfo = string(logging.LevelInfo) + LogLevelSuccess = string(logging.LevelSuccess) + LogLevelDebug = string(logging.LevelDebug) + LogLevelInfoSuccess = string(logging.LevelInfoSuccess) + LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess) +) + +// ============================================================================= +// 初始化和同步函数 +// ============================================================================= + +func init() { + // 初始化核心配置 + core.InitGlobalConfig() + + // 同步变量 + syncWithCore() +} + +// syncWithCore 同步common包变量与core包变量 +func syncWithCore() { + // 读取core包的默认值 + ScanMode = core.ScanMode + ThreadNum = core.ThreadNum + Timeout = core.Timeout + DisablePing = core.DisablePing + LocalMode = core.LocalMode + + Username = core.Username + Password = core.Password + Userdict = core.Userdict + Passwords = core.Passwords + + HttpProxy = core.HttpProxy + Socks5Proxy = core.Socks5Proxy + + NoColor = core.NoColor + Language = core.Language + LogLevel = core.LogLevel + + PortMap = core.PortMap + DefaultMap = core.DefaultMap +} + +// syncToCore 将common包变量同步回core包 +func syncToCore() { + core.ScanMode = ScanMode + core.ThreadNum = ThreadNum + core.Timeout = Timeout + core.DisablePing = DisablePing + core.LocalMode = LocalMode + + core.Username = Username + core.Password = Password + core.Userdict = Userdict + core.Passwords = Passwords + + core.HttpProxy = HttpProxy + core.Socks5Proxy = Socks5Proxy + + core.NoColor = NoColor + core.Language = Language + core.LogLevel = LogLevel + + core.PortMap = PortMap + core.DefaultMap = DefaultMap +} \ No newline at end of file diff --git a/Common/logging/Logger.go b/Common/logging/Logger.go index f5a0f7f..f64ed0d 100644 --- a/Common/logging/Logger.go +++ b/Common/logging/Logger.go @@ -79,6 +79,18 @@ func (l *Logger) SetOutputMutex(mutex *sync.Mutex) { l.outputMutex = mutex } +// SetCoordinatedOutput 设置协调输出函数(用于进度条协调) +func (l *Logger) SetCoordinatedOutput(fn func(string)) { + l.mu.RLock() + defer l.mu.RUnlock() + + for _, handler := range l.handlers { + if consoleHandler, ok := handler.(*ConsoleHandler); ok { + consoleHandler.SetCoordinatedOutput(fn) + } + } +} + // Log 记录日志 func (l *Logger) Log(level LogLevel, content string, metadata ...map[string]interface{}) { if !l.shouldLog(level) { @@ -213,10 +225,11 @@ func (l *Logger) Initialize() { // ConsoleHandler 控制台日志处理器 type ConsoleHandler struct { - config *LoggerConfig - formatter LogFormatter - enabled bool - mu sync.RWMutex + config *LoggerConfig + formatter LogFormatter + enabled bool + coordinatedOutput func(string) // 协调输出函数 + mu sync.RWMutex } // NewConsoleHandler 创建控制台处理器 @@ -243,15 +256,29 @@ func (h *ConsoleHandler) Handle(entry *LogEntry) { // 使用自己的格式化器格式化消息 logMsg := h.formatter.Format(entry) - // 根据颜色设置输出 - if h.config.EnableColor { - if colorAttr, ok := h.config.LevelColors[entry.Level]; ok { - color.New(colorAttr).Println(logMsg) + // 使用协调输出函数,如果设置了的话 + if h.coordinatedOutput != nil { + if h.config.EnableColor { + if colorAttr, ok := h.config.LevelColors[entry.Level]; ok { + coloredMsg := color.New(colorAttr).Sprint(logMsg) + h.coordinatedOutput(coloredMsg) + } else { + h.coordinatedOutput(logMsg) + } + } else { + h.coordinatedOutput(logMsg) + } + } else { + // 回到原来的直接输出方式 + if h.config.EnableColor { + if colorAttr, ok := h.config.LevelColors[entry.Level]; ok { + color.New(colorAttr).Println(logMsg) + } else { + fmt.Println(logMsg) + } } else { fmt.Println(logMsg) } - } else { - fmt.Println(logMsg) } // 根据慢速输出设置决定是否添加延迟 @@ -267,6 +294,13 @@ func (h *ConsoleHandler) SetEnabled(enabled bool) { h.enabled = enabled } +// SetCoordinatedOutput 设置协调输出函数 +func (h *ConsoleHandler) SetCoordinatedOutput(fn func(string)) { + h.mu.Lock() + defer h.mu.Unlock() + h.coordinatedOutput = fn +} + // IsEnabled 检查处理器是否启用 func (h *ConsoleHandler) IsEnabled() bool { h.mu.RLock() diff --git a/Core/ICMP.go b/Core/ICMP.go index 4e070a8..540b9d0 100644 --- a/Core/ICMP.go +++ b/Core/ICMP.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/common/output" "golang.org/x/net/icmp" "net" "os/exec" @@ -68,9 +69,9 @@ func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool) { protocol = "PING" } - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.HOST, + Type: output.TypeHost, Target: ip, Status: "alive", Details: map[string]interface{}{ diff --git a/Core/PortScan.go b/Core/PortScan.go index 613790a..a391226 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/common/output" "github.com/shadow1ng/fscan/common/parsers" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" @@ -85,8 +86,8 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string { atomic.AddInt64(&count, 1) aliveMap.Store(addr, struct{}{}) common.LogInfo("端口开放 " + addr) - common.SaveResult(&common.ScanResult{ - Time: time.Now(), Type: common.PORT, Target: host, + common.SaveResult(&output.ScanResult{ + Time: time.Now(), Type: output.TypePort, Target: host, Status: "open", Details: map[string]interface{}{"port": port}, }) @@ -116,8 +117,8 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string { } // 保存服务结果 - common.SaveResult(&common.ScanResult{ - Time: time.Now(), Type: common.SERVICE, Target: host, + common.SaveResult(&output.ScanResult{ + Time: time.Now(), Type: output.TypeService, Target: host, Status: "identified", Details: details, }) diff --git a/Plugins/ActiveMQ.go b/Plugins/ActiveMQ.go index 1c8198f..d4415b3 100644 --- a/Plugins/ActiveMQ.go +++ b/Plugins/ActiveMQ.go @@ -8,6 +8,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // ActiveMQCredential 表示一个ActiveMQ凭据 @@ -300,9 +301,9 @@ func saveActiveMQSuccess(info *common.HostInfo, target string, credential Active common.LogSuccess(successMsg) // 保存结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Cassandra.go b/Plugins/Cassandra.go index bef0a64..7d1f402 100644 --- a/Plugins/Cassandra.go +++ b/Plugins/Cassandra.go @@ -11,6 +11,7 @@ import ( "github.com/gocql/gocql" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // CassandraProxyDialer 实现gocql.Dialer接口,支持代理连接 @@ -353,9 +354,9 @@ func saveCassandraSuccess(info *common.HostInfo, target string, credential Cassa common.LogSuccess(successMsg) // 保存结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/DCInfoUnix.go b/Plugins/DCInfoUnix.go index 7927246..cee6095 100644 --- a/Plugins/DCInfoUnix.go +++ b/Plugins/DCInfoUnix.go @@ -3,6 +3,7 @@ package Plugins import "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" func DCInfoScan(info *common.HostInfo) (err error) { return nil diff --git a/Plugins/Elasticsearch.go b/Plugins/Elasticsearch.go index 911060b..df636b7 100644 --- a/Plugins/Elasticsearch.go +++ b/Plugins/Elasticsearch.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "net/http" "strings" "sync" @@ -295,9 +296,9 @@ func saveElasticResult(info *common.HostInfo, target string, credential ElasticC common.LogSuccess(successMsg) // 保存结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/FTP.go b/Plugins/FTP.go index cb9b626..f6d54ab 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "strings" "sync" "time" @@ -326,9 +327,9 @@ func saveFtpResult(info *common.HostInfo, target string, result *FtpScanResult) common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index ba0530c..0cb4392 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "net" "regexp" "strconv" @@ -192,9 +193,9 @@ func read(text []byte, host string) error { } // 保存扫描结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.SERVICE, + Type: output.TypeService, Target: host, Status: "identified", Details: details, diff --git a/Plugins/IMAP.go b/Plugins/IMAP.go index 9d0245f..4bf6298 100644 --- a/Plugins/IMAP.go +++ b/Plugins/IMAP.go @@ -12,6 +12,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // IMAPCredential 表示一个IMAP凭据 @@ -309,9 +310,9 @@ func saveIMAPResult(info *common.HostInfo, target string, credential IMAPCredent common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go index b4016ec..11b3754 100644 --- a/Plugins/Kafka.go +++ b/Plugins/Kafka.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/IBM/sarama" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "strings" "sync" "time" @@ -45,9 +46,9 @@ func KafkaScan(info *common.HostInfo) error { common.LogSuccess(fmt.Sprintf("Kafka服务 %s 无需认证即可访问", target)) // 保存无认证访问结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ @@ -79,9 +80,9 @@ func KafkaScan(info *common.HostInfo) error { result := concurrentKafkaScan(ctx, info, credentials, common.Timeout, common.MaxRetries) if result != nil { // 保存爆破成功结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/LDAP.go b/Plugins/LDAP.go index 7d72fb8..3fb7ebd 100644 --- a/Plugins/LDAP.go +++ b/Plugins/LDAP.go @@ -9,6 +9,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // LDAPCredential 表示一个LDAP凭据 @@ -296,9 +297,9 @@ func saveLDAPResult(info *common.HostInfo, target string, result *LDAPScanResult common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 382f603..a9e2ba8 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "os" "strings" "time" @@ -218,9 +219,9 @@ func MS17010Scan(info *common.HostInfo) error { } // 保存 MS17-010 漏洞结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: ip, Status: "vulnerable", Details: details, @@ -248,9 +249,9 @@ func MS17010Scan(info *common.HostInfo) error { common.LogSuccess(fmt.Sprintf("发现后门 %s DOUBLEPULSAR", ip)) // 保存 DOUBLEPULSAR 后门结果 - backdoorResult := &common.ScanResult{ + backdoorResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: ip, Status: "backdoor", Details: map[string]interface{}{ @@ -270,9 +271,9 @@ func MS17010Scan(info *common.HostInfo) error { common.LogBase(fmt.Sprintf("系统信息 %s [%s]", ip, os)) // 保存系统信息 - sysResult := &common.ScanResult{ + sysResult := &output.ScanResult{ Time: time.Now(), - Type: common.SERVICE, + Type: output.TypeService, Target: ip, Status: "identified", Details: map[string]interface{}{ diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index d8c035f..3fd62c4 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -11,6 +11,7 @@ import ( mssql "github.com/denisenkom/go-mssqldb" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // MSSQLProxyDialer 自定义dialer结构体 @@ -315,9 +316,9 @@ func saveMssqlResult(info *common.HostInfo, target string, credential MssqlCrede common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index 0ceb33d..cbc663e 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "strings" "time" ) @@ -29,9 +30,9 @@ func MemcachedScan(info *common.HostInfo) error { if result.Success { // 保存成功结果 - scanResult := &common.ScanResult{ + scanResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/MiniDumpUnix.go b/Plugins/MiniDumpUnix.go index bef6d18..9d096b9 100644 --- a/Plugins/MiniDumpUnix.go +++ b/Plugins/MiniDumpUnix.go @@ -3,6 +3,7 @@ package Plugins import "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" func MiniDump(info *common.HostInfo) (err error) { return nil diff --git a/Plugins/Modbus.go b/Plugins/Modbus.go index c38afed..de02c50 100644 --- a/Plugins/Modbus.go +++ b/Plugins/Modbus.go @@ -7,6 +7,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // ModbusScanResult 表示 Modbus 扫描结果 @@ -251,9 +252,9 @@ func parseModbusResponse(response []byte) string { // saveModbusResult 保存Modbus扫描结果 func saveModbusResult(info *common.HostInfo, target string, result *ModbusScanResult) { // 保存扫描结果 - scanResult := &common.ScanResult{ + scanResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index e25f49d..546be1a 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -8,6 +8,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // MongodbScan 执行MongoDB未授权扫描 @@ -53,9 +54,9 @@ func MongodbScan(info *common.HostInfo) error { common.LogSuccess(fmt.Sprintf("MongoDB %v 未授权访问", target)) // 保存未授权访问结果 - scanResult := &common.ScanResult{ + scanResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index f58a2d4..e25dc69 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -11,6 +11,7 @@ import ( "github.com/go-sql-driver/mysql" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // MySQLProxyDialer 自定义dialer结构体 @@ -327,9 +328,9 @@ func saveMySQLResult(info *common.HostInfo, target string, credential MySQLCrede common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Neo4j.go b/Plugins/Neo4j.go index 840cb0f..78d7e7f 100644 --- a/Plugins/Neo4j.go +++ b/Plugins/Neo4j.go @@ -9,6 +9,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v4/neo4j" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // Neo4jCredential 表示一个Neo4j凭据 @@ -349,9 +350,9 @@ func saveNeo4jResult(info *common.HostInfo, target string, result *Neo4jScanResu common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index f012b06..96a1553 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -52,15 +52,7 @@ func NetBIOS(info *common.HostInfo) error { details["os_version"] = netbios.OsVersion } - scanResult := &common.ScanResult{ - Time: time.Now(), - Type: common.SERVICE, - Target: info.Host, - Status: "identified", - Details: details, - } - - common.SaveResult(scanResult) + // NetBIOS信息已通过上面的LogSuccess记录,不需要额外保存结果 return nil } return errNetBIOS diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index d006673..2266ab8 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" _ "github.com/sijms/go-ora/v2" "strings" "sync" @@ -417,9 +418,9 @@ func saveOracleResult(info *common.HostInfo, target string, credential OracleCre common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/POP3.go b/Plugins/POP3.go index 7896ae3..9cf6cd0 100644 --- a/Plugins/POP3.go +++ b/Plugins/POP3.go @@ -11,6 +11,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // POP3Credential 表示一个POP3凭据 @@ -395,9 +396,9 @@ func savePOP3Result(info *common.HostInfo, target string, result *POP3ScanResult common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index e9b70ce..e917e3c 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -12,6 +12,7 @@ import ( "github.com/lib/pq" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // PostgresProxyDialer 自定义dialer结构体 @@ -302,9 +303,9 @@ func savePostgresResult(info *common.HostInfo, target string, credential Postgre common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/RDP.go b/Plugins/RDP.go index b781d06..a987629 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "github.com/tomatome/grdp/core" "github.com/tomatome/grdp/glog" "github.com/tomatome/grdp/protocol/nla" @@ -255,9 +256,9 @@ func saveRdpResult(info *common.HostInfo, target string, port int, credential RD details["domain"] = credential.Domain } - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/RabbitMQ.go b/Plugins/RabbitMQ.go index 3be0d79..83f5c06 100644 --- a/Plugins/RabbitMQ.go +++ b/Plugins/RabbitMQ.go @@ -5,6 +5,7 @@ import ( "fmt" amqp "github.com/rabbitmq/amqp091-go" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "net" "strings" "sync" @@ -291,9 +292,9 @@ func saveRabbitMQResult(info *common.HostInfo, target string, credential RabbitM common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Redis.go b/Plugins/Redis.go index fe7d921..937becb 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "io" "net" "os" @@ -62,9 +63,9 @@ func RedisScan(info *common.HostInfo) error { common.LogSuccess(fmt.Sprintf("Redis无密码连接成功: %s", target)) // 保存未授权访问结果 - scanResult := &common.ScanResult{ + scanResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ @@ -107,9 +108,9 @@ func RedisScan(info *common.HostInfo) error { common.LogSuccess(fmt.Sprintf("Redis认证成功 %s [%s]", target, result.Credential.Password)) // 保存弱密码结果 - scanResult := &common.ScanResult{ + scanResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/Rsync.go b/Plugins/Rsync.go index 0dd30f0..0e7731c 100644 --- a/Plugins/Rsync.go +++ b/Plugins/Rsync.go @@ -8,6 +8,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // RsyncCredential 表示一个Rsync凭据 @@ -465,9 +466,9 @@ func saveRsyncResult(info *common.HostInfo, target string, result *RsyncScanResu common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/SMB.go b/Plugins/SMB.go index c92fc0a..7d91531 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "github.com/stacktitan/smb/smb" "strings" "sync" @@ -242,9 +243,9 @@ func saveSmbResult(info *common.HostInfo, target string, credential SmbCredentia common.LogSuccess(successMsg) // 保存结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index 4bdc8bd..3f4bcc3 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -9,6 +9,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "github.com/hirochachacha/go-smb2" ) @@ -401,9 +402,9 @@ func logSuccessfulAuth(info *common.HostInfo, user, pass string, hash []byte) { } // 保存认证成功结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "success", Details: map[string]interface{}{ @@ -456,9 +457,9 @@ func logShareInfo(info *common.HostInfo, user string, pass string, hash []byte, } // 保存共享信息结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "shares-found", Details: map[string]interface{}{ diff --git a/Plugins/SMTP.go b/Plugins/SMTP.go index ac872d3..6f3e0de 100644 --- a/Plugins/SMTP.go +++ b/Plugins/SMTP.go @@ -9,6 +9,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // SmtpCredential 表示一个SMTP凭据 @@ -314,9 +315,9 @@ func saveSmtpResult(info *common.HostInfo, target string, result *SmtpScanResult common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go index ed5c6d2..3956b1e 100644 --- a/Plugins/SNMP.go +++ b/Plugins/SNMP.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gosnmp/gosnmp" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "strconv" "strings" "time" @@ -66,9 +67,9 @@ func SNMPScan(info *common.HostInfo) (tmperr error) { common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 78a8663..62ab868 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "golang.org/x/crypto/ssh" "io/ioutil" "net" @@ -348,9 +349,9 @@ func logAndSaveSuccess(info *common.HostInfo, target string, result *SshScanResu common.LogSuccess(successMsg) - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/Telnet.go b/Plugins/Telnet.go index 83f8059..4d28865 100644 --- a/Plugins/Telnet.go +++ b/Plugins/Telnet.go @@ -12,6 +12,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // TelnetCredential 表示一个Telnet凭据 @@ -313,9 +314,9 @@ func saveTelnetResult(info *common.HostInfo, target string, result *TelnetScanRe common.LogSuccess(successMsg) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: details, diff --git a/Plugins/VNC.go b/Plugins/VNC.go index e121125..941d0fd 100644 --- a/Plugins/VNC.go +++ b/Plugins/VNC.go @@ -8,6 +8,7 @@ import ( "github.com/mitchellh/go-vnc" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" ) // VncCredential 表示VNC凭据 @@ -257,9 +258,9 @@ func saveVncResult(info *common.HostInfo, target string, credential VncCredentia common.LogSuccess(successLog) // 保存结果 - vulnResult := &common.ScanResult{ + vulnResult := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: info.Host, Status: "vulnerable", Details: map[string]interface{}{ diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 18f8156..cdded65 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -15,6 +15,7 @@ import ( "unicode/utf8" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "github.com/shadow1ng/fscan/webscan" "github.com/shadow1ng/fscan/webscan/lib" "golang.org/x/text/encoding/simplifiedchinese" @@ -379,9 +380,9 @@ func saveWebResult(info *common.HostInfo, resp *WebResponse) { } // 保存扫描结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.SERVICE, + Type: output.TypeService, Target: info.Host, Status: "identified", Details: map[string]interface{}{ diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index 1fc8a40..d80b93a 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -14,6 +14,7 @@ import ( "time" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/config" "github.com/shadow1ng/fscan/webscan/lib" ) @@ -72,13 +73,13 @@ func WebScan(info *common.HostInfo) { // 根据扫描策略执行POC if common.Pocinfo.PocName == "" && len(info.Infostr) == 0 { // 执行所有POC - executePOCs(ctx, common.PocInfo{Target: target}) + executePOCs(ctx, config.PocInfo{Target: target}) } else if len(info.Infostr) > 0 { // 基于指纹信息执行POC scanByFingerprints(ctx, target, info.Infostr) } else if common.Pocinfo.PocName != "" { // 基于指定POC名称执行 - executePOCs(ctx, common.PocInfo{Target: target, PocName: common.Pocinfo.PocName}) + executePOCs(ctx, config.PocInfo{Target: target, PocName: common.Pocinfo.PocName}) } } @@ -117,12 +118,12 @@ func scanByFingerprints(ctx context.Context, target string, fingerprints []strin continue } - executePOCs(ctx, common.PocInfo{Target: target, PocName: pocName}) + executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName}) } } // executePOCs 执行POC检测 -func executePOCs(ctx context.Context, pocInfo common.PocInfo) { +func executePOCs(ctx context.Context, pocInfo config.PocInfo) { // 验证目标 if pocInfo.Target == "" { common.LogError(ErrEmptyTarget.Error()) diff --git a/WebScan/lib/Check.go b/WebScan/lib/Check.go index f323ee7..2a1a22e 100644 --- a/WebScan/lib/Check.go +++ b/WebScan/lib/Check.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/cel-go/cel" "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/output" "github.com/shadow1ng/fscan/webscan/info" "math/rand" "net/http" @@ -90,9 +91,9 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) { } // 创建并保存扫描结果 - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: task.Req.URL.String(), Status: "vulnerable", Details: details, @@ -495,9 +496,9 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, // 保存漏洞结果(除非明确指示跳过) if !skipSave { - result := &common.ScanResult{ + result := &output.ScanResult{ Time: time.Now(), - Type: common.VULN, + Type: output.TypeVuln, Target: targetURL, Status: "vulnerable", Details: details, diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go index 8b9e66a..d5be2f5 100644 --- a/WebScan/lib/Client.go +++ b/WebScan/lib/Client.go @@ -7,7 +7,7 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/common" - "golang.org/x/net/proxy" + "github.com/shadow1ng/fscan/common/proxy" "gopkg.in/yaml.v2" "net" "net/http" @@ -67,15 +67,17 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err // 配置Socks5代理 if common.Socks5Proxy != "" { - dialSocksProxy, err := common.Socks5Dialer(dialer) + proxyConfig := &proxy.ProxyConfig{ + Type: proxy.ProxyTypeSOCKS5, + Address: common.Socks5Proxy, + Timeout: time.Second * 10, + } + proxyManager := proxy.NewProxyManager(proxyConfig) + proxyDialer, err := proxyManager.GetDialer() if err != nil { return err } - if contextDialer, ok := dialSocksProxy.(proxy.ContextDialer); ok { - tr.DialContext = contextDialer.DialContext - } else { - return errors.New("无法转换为DialContext类型") - } + tr.DialContext = proxyDialer.DialContext } else if DownProxy != "" { // 处理其他代理配置 if DownProxy == "1" {