fscan/plugins/local/keylogger.go
ZacharyZcR 8f54702c02 refactor: 精准修复插件系统三个设计问题
经Linus式架构审计,发现并修复插件系统中的具体问题:

## 核心修复

### 1. 消除local插件GetPorts()方法冗余
- 删除21个local插件中无意义的GetPorts()方法
- 简化local.Plugin接口:移除端口概念
- 理由:本地插件不涉及网络,端口概念完全多余

### 2. 消除web插件GetPorts()方法冗余
- 删除2个web插件中无用的GetPorts()方法
- 简化web.WebPlugin接口:专注智能HTTP检测
- 理由:Web插件使用动态HTTP检测,预定义端口无价值

### 3. 统一插件命名规范
- 统一所有插件接口使用Name()方法(符合Go惯例)
- 消除GetName()与Name()不一致问题
- 简化适配器:不再需要方法名转换

## 技术改进

接口精简:
- local插件:GetName() + GetPorts() → Name()
- web插件:GetName() + GetPorts() → Name()
- services插件:GetName() → Name()(保留GetPorts(),业务必需)

代码减少:
- 删除23个无用GetPorts()方法
- 重命名52个Name()方法
- 简化3个插件接口定义

## 影响范围

修改文件:55个插件文件
代码变更:-155行 +61行(净减少94行)
功能影响:零破坏性,保持所有业务逻辑不变

这是基于业务需求分析的精准重构,消除真正多余的部分,
保持系统架构合理性和向后兼容性。
2025-08-26 20:38:39 +08:00

277 lines
6.7 KiB
Go

package local
import (
"context"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/common"
)
// KeyloggerPlugin 键盘记录插件 - Linus式简化版本
//
// 设计哲学:直接实现,删除过度设计
// - 删除复杂的继承体系
// - 直接实现键盘记录功能
// - 保持原有功能逻辑
type KeyloggerPlugin struct {
name string
outputFile string
isRunning bool
stopChan chan struct{}
keyBuffer []string
bufferMutex sync.RWMutex
}
// NewKeyloggerPlugin 创建键盘记录插件
func NewKeyloggerPlugin() *KeyloggerPlugin {
outputFile := common.KeyloggerOutputFile
if outputFile == "" {
outputFile = "keylog.txt"
}
return &KeyloggerPlugin{
name: "keylogger",
outputFile: outputFile,
stopChan: make(chan struct{}),
keyBuffer: make([]string, 0),
}
}
// GetName 实现Plugin接口
func (p *KeyloggerPlugin) Name() string {
return p.name
}
// Scan 执行键盘记录 - 直接实现
func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
var output strings.Builder
output.WriteString("=== 键盘记录 ===\n")
output.WriteString(fmt.Sprintf("输出文件: %s\n", p.outputFile))
output.WriteString(fmt.Sprintf("平台: %s\n\n", runtime.GOOS))
// 检查输出文件权限
if err := p.checkOutputFilePermissions(); err != nil {
output.WriteString(fmt.Sprintf("输出文件权限检查失败: %v\n", err))
return &ScanResult{
Success: false,
Output: output.String(),
Error: err,
}
}
// 检查平台要求
if err := p.checkPlatformRequirements(); err != nil {
output.WriteString(fmt.Sprintf("平台要求检查失败: %v\n", err))
return &ScanResult{
Success: false,
Output: output.String(),
Error: err,
}
}
// 启动键盘记录
err := p.startKeylogging(ctx)
if err != nil {
output.WriteString(fmt.Sprintf("键盘记录失败: %v\n", err))
return &ScanResult{
Success: false,
Output: output.String(),
Error: err,
}
}
// 输出结果
output.WriteString("✓ 键盘记录已完成\n")
output.WriteString(fmt.Sprintf("捕获事件数: %d\n", len(p.keyBuffer)))
output.WriteString(fmt.Sprintf("日志文件: %s\n", p.outputFile))
common.LogSuccess(fmt.Sprintf("键盘记录完成,捕获了 %d 个键盘事件", len(p.keyBuffer)))
return &ScanResult{
Success: true,
Output: output.String(),
Error: nil,
}
}
// startKeylogging 启动键盘记录
func (p *KeyloggerPlugin) startKeylogging(ctx context.Context) error {
p.isRunning = true
defer func() {
p.isRunning = false
}()
// 根据平台启动相应的键盘记录
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)
}
// 保存到文件
if err := p.saveKeysToFile(); err != nil {
common.LogError(fmt.Sprintf("保存键盘记录失败: %v", err))
}
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
}
// 平台特定的键盘记录实现 - 简化版本,仅做演示
func (p *KeyloggerPlugin) startWindowsKeylogging(ctx context.Context) error {
// Windows平台键盘记录实现
// 在实际实现中需要使用Windows API
p.addKeyToBuffer("演示键盘记录 - Windows平台")
// 模拟记录一段时间
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(5 * time.Second):
// 模拟结束
}
return nil
}
func (p *KeyloggerPlugin) startLinuxKeylogging(ctx context.Context) error {
// Linux平台键盘记录实现
// 在实际实现中需要访问/dev/input/event*设备
p.addKeyToBuffer("演示键盘记录 - Linux平台")
// 模拟记录一段时间
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(5 * time.Second):
// 模拟结束
}
return nil
}
func (p *KeyloggerPlugin) startDarwinKeylogging(ctx context.Context) error {
// macOS平台键盘记录实现
// 在实际实现中需要使用Core Graphics框架
p.addKeyToBuffer("演示键盘记录 - macOS平台")
// 模拟记录一段时间
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(5 * time.Second):
// 模拟结束
}
return nil
}
// 平台特定的要求检查 - 简化版本
func (p *KeyloggerPlugin) checkWindowsRequirements() error {
// Windows平台要求检查
return nil
}
func (p *KeyloggerPlugin) checkLinuxRequirements() error {
// Linux平台要求检查
return nil
}
func (p *KeyloggerPlugin) checkDarwinRequirements() error {
// macOS平台要求检查
return nil
}
// 注册插件
func init() {
RegisterLocalPlugin("keylogger", func() Plugin {
return NewKeyloggerPlugin()
})
}