package cleaner import ( "context" "fmt" "os" "path/filepath" "runtime" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/local" ) // CleanerPlugin 系统痕迹清理插件 - 跨平台支持 type CleanerPlugin struct { *local.BaseLocalPlugin // 配置选项 targetFiles []string // 要清理的文件列表 cleanDirectories []string // 要清理的目录列表 currentExecutable string // 当前执行文件路径 workingDirectory string // 当前工作目录 cleanupStats map[string]int // 清理统计 } // NewCleanerPlugin 创建系统痕迹清理插件 func NewCleanerPlugin() *CleanerPlugin { metadata := &base.PluginMetadata{ Name: "cleaner", Version: "1.0.0", Author: "fscan-team", Description: "跨平台系统痕迹清理插件,清理扫描过程中产生的文件和系统痕迹", Category: "local", Tags: []string{"local", "cleaner", "forensics", "cross-platform"}, Protocols: []string{"local"}, } plugin := &CleanerPlugin{ BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), targetFiles: make([]string, 0), cleanDirectories: make([]string, 0), cleanupStats: make(map[string]int), } // 支持所有主要平台 plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) // 需要系统权限进行清理操作 plugin.SetRequiresPrivileges(false) // 根据当前用户权限进行清理 return plugin } // Initialize 初始化插件 func (p *CleanerPlugin) Initialize() error { common.LogInfo(fmt.Sprintf("初始化系统痕迹清理插件 - 平台: %s", runtime.GOOS)) // 获取当前执行文件路径 if exe, err := os.Executable(); err == nil { p.currentExecutable = exe common.LogDebug(fmt.Sprintf("当前执行文件: %s", exe)) } // 获取当前工作目录 if wd, err := os.Getwd(); err == nil { p.workingDirectory = wd common.LogDebug(fmt.Sprintf("当前工作目录: %s", wd)) } // 扫描要清理的文件 if err := p.scanCleanupTargets(); err != nil { return fmt.Errorf("扫描清理目标失败: %v", err) } return p.BaseLocalPlugin.Initialize() } // Scan 重写扫描方法以确保调用正确的ScanLocal实现 func (p *CleanerPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { return p.ScanLocal(ctx, info) } // ScanLocal 执行系统痕迹清理任务 func (p *CleanerPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { common.LogInfo("开始系统痕迹清理...") // 执行清理操作 cleanupReport, err := p.performCleanup(ctx) if err != nil { return &base.ScanResult{ Success: false, Error: err, }, nil } result := &base.ScanResult{ Success: true, Service: "SystemCleaner", Banner: fmt.Sprintf("痕迹清理完成: 清理了 %d 个文件, %d 个目录, %d 个系统条目", p.cleanupStats["files"], p.cleanupStats["directories"], p.cleanupStats["system_entries"]), Extra: map[string]interface{}{ "platform": runtime.GOOS, "files_cleaned": p.cleanupStats["files"], "directories_cleaned": p.cleanupStats["directories"], "system_entries_cleaned": p.cleanupStats["system_entries"], "cleanup_report": cleanupReport, }, } common.LogSuccess(fmt.Sprintf("系统痕迹清理完成: 文件(%d) 目录(%d) 系统条目(%d)", p.cleanupStats["files"], p.cleanupStats["directories"], p.cleanupStats["system_entries"])) return result, nil } // scanCleanupTargets 扫描要清理的目标 func (p *CleanerPlugin) scanCleanupTargets() error { common.LogInfo("扫描清理目标...") // 扫描当前目录下的fscan相关文件 if err := filepath.Walk(p.workingDirectory, func(path string, info os.FileInfo, err error) error { if err != nil { return nil // 忽略访问错误 } if info.IsDir() { return nil } filename := strings.ToLower(info.Name()) // 检查fscan相关文件 if p.isFscanRelatedFile(filename) { p.targetFiles = append(p.targetFiles, path) common.LogDebug(fmt.Sprintf("发现清理目标: %s", path)) } return nil }); err != nil { common.LogError(fmt.Sprintf("扫描文件失败: %v", err)) } common.LogInfo(fmt.Sprintf("发现 %d 个文件需要清理", len(p.targetFiles))) return nil } // isFscanRelatedFile 判断是否为fscan相关文件 - 使用保守的策略 func (p *CleanerPlugin) isFscanRelatedFile(filename string) bool { // 严格的项目文件排除列表 - 确保不误删项目文件 excludePatterns := []string{ ".go", ".mod", ".sum", ".md", ".yml", ".yaml", // 源码和配置 ".git", ".claude", ".idea", ".vscode", // 版本控制和IDE "dockerfile", "makefile", "license", "readme", // 项目文件 "plugins", "common", "core", "webscan", // 核心目录 "testdocker", // 测试配置 ".json", ".xml", // 配置文件 } // 检查是否为需要排除的文件 for _, exclude := range excludePatterns { if strings.Contains(filename, exclude) { return false } } // 只清理明确的输出和结果文件 - 非常保守的策略 cleanPatterns := []string{ "result.txt", // 默认扫描结果文件 "results.txt", // 可能的结果文件 "output.txt", // 输出文件 "scan_result.txt", // 扫描结果 "keylog.txt", // 键盘记录输出 "my_keylog.txt", // 自定义键盘记录 } // 排除当前执行文件(稍后单独处理) if p.currentExecutable != "" { currentExeName := strings.ToLower(filepath.Base(p.currentExecutable)) if filename == currentExeName { return false } } // 只清理明确匹配的输出文件 for _, pattern := range cleanPatterns { if filename == pattern { // 精确匹配,不使用 Contains return true } } // 清理明确的测试生成可执行文件(但保留源码) if strings.HasSuffix(filename, ".exe") { // 只清理包含特定测试标识的exe文件 testPatterns := []string{"_test.exe", "_debug.exe", "fscan_test", "fscan_debug"} for _, pattern := range testPatterns { if strings.Contains(filename, pattern) { return true } } } return false } // performCleanup 执行清理操作 func (p *CleanerPlugin) performCleanup(ctx context.Context) (map[string]interface{}, error) { report := make(map[string]interface{}) // 初始化统计 p.cleanupStats["files"] = 0 p.cleanupStats["directories"] = 0 p.cleanupStats["system_entries"] = 0 // 1. 清理文件 common.LogInfo("清理相关文件...") fileReport := p.cleanTargetFiles() report["file_cleanup"] = fileReport // 2. 清理系统痕迹(平台特定) common.LogInfo("清理系统痕迹...") systemReport := p.cleanSystemTraces() report["system_cleanup"] = systemReport // 3. 清理网络痕迹 common.LogInfo("清理网络痕迹...") networkReport := p.cleanNetworkTraces() report["network_cleanup"] = networkReport // 4. 最后清理自身(需要特殊处理) if p.currentExecutable != "" { common.LogInfo("准备清理自身...") selfReport := p.prepareSelfDestruction() report["self_cleanup"] = selfReport } return report, nil } // cleanTargetFiles 清理文件 func (p *CleanerPlugin) cleanTargetFiles() []string { var cleaned []string for _, file := range p.targetFiles { if err := p.secureDelete(file); err != nil { common.LogError(fmt.Sprintf("删除文件失败 %s: %v", file, err)) } else { cleaned = append(cleaned, file) p.cleanupStats["files"]++ common.LogSuccess(fmt.Sprintf("已删除: %s", file)) } } return cleaned } // secureDelete 安全删除文件(覆盖后删除) func (p *CleanerPlugin) secureDelete(filepath string) error { // 获取文件信息 info, err := os.Stat(filepath) if err != nil { return err } // 小文件进行覆盖删除 if info.Size() < 10*1024*1024 { // 10MB以下 if err := p.overwriteFile(filepath); err != nil { common.LogDebug(fmt.Sprintf("覆盖文件失败: %v", err)) } } // 删除文件 return os.Remove(filepath) } // overwriteFile 覆盖文件内容 func (p *CleanerPlugin) overwriteFile(filepath string) error { info, err := os.Stat(filepath) if err != nil { return err } file, err := os.OpenFile(filepath, os.O_WRONLY, 0) if err != nil { return err } defer file.Close() // 用随机数据覆盖 size := info.Size() buffer := make([]byte, 4096) for i := range buffer { buffer[i] = byte(time.Now().UnixNano() % 256) } for size > 0 { writeSize := int64(len(buffer)) if size < writeSize { writeSize = size } if _, err := file.Write(buffer[:writeSize]); err != nil { return err } size -= writeSize } return file.Sync() } // prepareSelfDestruction 准备自毁 - 平台特定实现在各自的文件中 // GetLocalData 获取清理器本地数据 func (p *CleanerPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) { data := make(map[string]interface{}) data["plugin_type"] = "cleaner" data["platform"] = runtime.GOOS data["current_executable"] = p.currentExecutable data["working_directory"] = p.workingDirectory data["cleanup_targets"] = len(p.targetFiles) if hostname, err := os.Hostname(); err == nil { data["hostname"] = hostname } return data, nil } // ExtractData 提取数据 func (p *CleanerPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) { return &base.ExploitResult{ Success: true, Output: fmt.Sprintf("系统痕迹清理完成,清理了 %d 个项目", p.cleanupStats["files"]+p.cleanupStats["directories"]+p.cleanupStats["system_entries"]), Data: data, Extra: map[string]interface{}{ "platform": runtime.GOOS, "cleanup_stats": p.cleanupStats, "status": "completed", }, }, nil } // GetInfo 获取插件信息 func (p *CleanerPlugin) GetInfo() string { var info strings.Builder info.WriteString("跨平台系统痕迹清理插件\n") info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) info.WriteString("功能:\n") info.WriteString(" - 清理fscan相关输出文件\n") info.WriteString(" - 清理系统日志痕迹\n") info.WriteString(" - 清理网络连接痕迹\n") info.WriteString(" - 清理Shell历史记录\n") info.WriteString(" - 安全删除敏感文件\n") info.WriteString(" - 自毁功能\n") info.WriteString("注意: 根据当前用户权限执行清理操作\n") return info.String() } // RegisterCleanerPlugin 注册系统痕迹清理插件 func RegisterCleanerPlugin() { factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "cleaner", Version: "1.0.0", Author: "fscan-team", Description: "跨平台系统痕迹清理插件,清理扫描过程中产生的文件和系统痕迹", Category: "local", Tags: []string{"cleaner", "local", "forensics", "cross-platform"}, Protocols: []string{"local"}, }, func() base.Plugin { return NewCleanerPlugin() }, ) base.GlobalPluginRegistry.Register("cleaner", factory) } // init 插件注册函数 func init() { RegisterCleanerPlugin() }