Files
2026-07-10 23:44:46 -05:00

301 lines
7.2 KiB
Go

//go:build windows
package handlers
import (
"syscall"
"unsafe"
"overlord-client/cmd/agent/privacy"
)
var (
user32 = syscall.NewLazyDLL("user32.dll")
procSetCursorPos = user32.NewProc("SetCursorPos")
procSendInput = user32.NewProc("SendInput")
procMapVirtualKeyW = user32.NewProc("MapVirtualKeyW")
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
)
const (
MOUSEEVENTF_MOVE = 0x0001
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010
MOUSEEVENTF_MIDDLEDOWN = 0x0020
MOUSEEVENTF_MIDDLEUP = 0x0040
MOUSEEVENTF_WHEEL = 0x0800
MOUSEEVENTF_ABSOLUTE = 0x8000
MOUSEEVENTF_VIRTUALDESK = 0x4000
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP = 0x0002
KEYEVENTF_UNICODE = 0x0004
KEYEVENTF_SCANCODE = 0x0008
SM_XVIRTUALSCREEN = 76
SM_YVIRTUALSCREEN = 77
SM_CXVIRTUALSCREEN = 78
SM_CYVIRTUALSCREEN = 79
)
type mouseInput struct {
dx int32
dy int32
mouseData uint32
dwFlags uint32
time uint32
dwExtraInfo uintptr
}
type keybdInput struct {
wVk uint16
wScan uint16
dwFlags uint32
time uint32
dwExtraInfo uintptr
}
type hardwareInput struct {
uMsg uint32
wParamL uint16
wParamH uint16
}
type input struct {
inputType uint32
_ uint32
union [32]byte
}
func setCursorPos(x, y int32) {
if !sendMouseMoveAbsolute(x, y) {
_, _, _ = procSetCursorPos.Call(uintptr(x), uintptr(y))
}
}
func sendMouseDown(button int) {
switch button {
case 0:
sendMouseInput(MOUSEEVENTF_LEFTDOWN, 0)
case 2:
sendMouseInput(MOUSEEVENTF_RIGHTDOWN, 0)
case 1:
sendMouseInput(MOUSEEVENTF_MIDDLEDOWN, 0)
}
}
func sendMouseUp(button int) {
switch button {
case 0:
sendMouseInput(MOUSEEVENTF_LEFTUP, 0)
case 2:
sendMouseInput(MOUSEEVENTF_RIGHTUP, 0)
case 1:
sendMouseInput(MOUSEEVENTF_MIDDLEUP, 0)
}
}
func sendMouseWheel(delta int32) {
sendMouseInput(MOUSEEVENTF_WHEEL, uint32(delta))
}
func sendMouseInput(flags uint32, mouseData uint32) {
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
var inp input
inp.inputType = INPUT_MOUSE
mi := (*mouseInput)(unsafe.Pointer(&inp.union[0]))
mi.dx = 0
mi.dy = 0
mi.mouseData = mouseData
mi.dwFlags = flags
mi.time = 0
mi.dwExtraInfo = inputExtraInfo()
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
}
func sendMouseMoveAbsolute(x, y int32) bool {
left := getSystemMetrics(SM_XVIRTUALSCREEN)
top := getSystemMetrics(SM_YVIRTUALSCREEN)
width := getSystemMetrics(SM_CXVIRTUALSCREEN)
height := getSystemMetrics(SM_CYVIRTUALSCREEN)
if width <= 1 || height <= 1 {
return false
}
const maxCoord = 65535
relX := int64(x) - int64(left)
relY := int64(y) - int64(top)
if relX < 0 {
relX = 0
}
if relY < 0 {
relY = 0
}
if relX > int64(width-1) {
relX = int64(width - 1)
}
if relY > int64(height-1) {
relY = int64(height - 1)
}
normX := int32(relX * maxCoord / int64(width-1))
normY := int32(relY * maxCoord / int64(height-1))
var inp input
inp.inputType = INPUT_MOUSE
mi := (*mouseInput)(unsafe.Pointer(&inp.union[0]))
mi.dx = normX
mi.dy = normY
mi.mouseData = 0
mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK
mi.time = 0
mi.dwExtraInfo = inputExtraInfo()
r, _, _ := procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
return r != 0
}
func getSystemMetrics(index int32) int32 {
r, _, _ := procGetSystemMetrics.Call(uintptr(index))
return int32(r)
}
func sendKeyDown(vk uint16) {
sendKeyInput(vk, false)
}
func sendKeyUp(vk uint16) {
sendKeyInput(vk, true)
}
func sendKeyInput(vk uint16, keyUp bool) {
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
var inp input
inp.inputType = INPUT_KEYBOARD
ki := (*keybdInput)(unsafe.Pointer(&inp.union[0]))
ki.wVk = vk
ki.wScan = uint16(mapVirtualKey(vk))
flags := uint32(0)
if keyUp {
flags |= KEYEVENTF_KEYUP
}
if isExtendedKey(vk) {
flags |= KEYEVENTF_EXTENDEDKEY
}
ki.dwFlags = flags
ki.time = 0
ki.dwExtraInfo = inputExtraInfo()
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
}
func isExtendedKey(vk uint16) bool {
switch vk {
case 0xA3, // VK_RCONTROL
0xA5, // VK_RMENU
0x21, // VK_PRIOR
0x22, // VK_NEXT
0x23, // VK_END
0x24, // VK_HOME
0x25, // VK_LEFT
0x26, // VK_UP
0x27, // VK_RIGHT
0x28, // VK_DOWN
0x2D, // VK_INSERT
0x2E, // VK_DELETE
0x6F, // VK_DIVIDE
0x90: // VK_NUMLOCK
return true
default:
return false
}
}
func mapVirtualKey(vk uint16) uint32 {
ret, _, _ := procMapVirtualKeyW.Call(uintptr(vk), 0)
return uint32(ret)
}
func sendTextInput(text string) {
for _, r := range text {
switch r {
case '\r', '\n':
sendKeyDown(0x0D)
sendKeyUp(0x0D)
case '\t':
sendKeyDown(0x09)
sendKeyUp(0x09)
case '\b':
sendKeyDown(0x08)
sendKeyUp(0x08)
default:
sendUnicodeRune(r)
}
}
}
func sendUnicodeRune(r rune) {
var inp input
inp.inputType = INPUT_KEYBOARD
ki := (*keybdInput)(unsafe.Pointer(&inp.union[0]))
ki.wVk = 0
ki.wScan = uint16(r)
ki.dwFlags = KEYEVENTF_UNICODE
ki.dwExtraInfo = inputExtraInfo()
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
var inpUp input
inpUp.inputType = INPUT_KEYBOARD
kiUp := (*keybdInput)(unsafe.Pointer(&inpUp.union[0]))
kiUp.wVk = 0
kiUp.wScan = uint16(r)
kiUp.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP
kiUp.dwExtraInfo = inputExtraInfo()
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inpUp)), unsafe.Sizeof(inpUp))
}
func keyCodeToVK(code string) uint16 {
vkMap := map[string]uint16{
"KeyA": 0x41, "KeyB": 0x42, "KeyC": 0x43, "KeyD": 0x44, "KeyE": 0x45,
"KeyF": 0x46, "KeyG": 0x47, "KeyH": 0x48, "KeyI": 0x49, "KeyJ": 0x4A,
"KeyK": 0x4B, "KeyL": 0x4C, "KeyM": 0x4D, "KeyN": 0x4E, "KeyO": 0x4F,
"KeyP": 0x50, "KeyQ": 0x51, "KeyR": 0x52, "KeyS": 0x53, "KeyT": 0x54,
"KeyU": 0x55, "KeyV": 0x56, "KeyW": 0x57, "KeyX": 0x58, "KeyY": 0x59, "KeyZ": 0x5A,
"Digit0": 0x30, "Digit1": 0x31, "Digit2": 0x32, "Digit3": 0x33, "Digit4": 0x34,
"Digit5": 0x35, "Digit6": 0x36, "Digit7": 0x37, "Digit8": 0x38, "Digit9": 0x39,
"Enter": 0x0D, "Space": 0x20, "Backspace": 0x08, "Tab": 0x09, "Escape": 0x1B,
"ShiftLeft": 0xA0, "ShiftRight": 0xA1, "ControlLeft": 0xA2, "ControlRight": 0xA3,
"AltLeft": 0xA4, "AltRight": 0xA5, "MetaLeft": 0x5B, "MetaRight": 0x5C,
"ArrowLeft": 0x25, "ArrowUp": 0x26, "ArrowRight": 0x27, "ArrowDown": 0x28,
"Delete": 0x2E, "Home": 0x24, "End": 0x23, "PageUp": 0x21, "PageDown": 0x22,
"F1": 0x70, "F2": 0x71, "F3": 0x72, "F4": 0x73, "F5": 0x74, "F6": 0x75,
"F7": 0x76, "F8": 0x77, "F9": 0x78, "F10": 0x79, "F11": 0x7A, "F12": 0x7B,
"Slash": 0xBF,
"Backquote": 0xC0,
"Minus": 0xBD,
"Equal": 0xBB,
"BracketLeft": 0xDB,
"BracketRight": 0xDD,
"Backslash": 0xDC,
"IntlBackslash": 0xDC,
"Semicolon": 0xBA,
"Quote": 0xDE,
"Comma": 0xBC,
"Period": 0xBE,
}
if vk, ok := vkMap[code]; ok {
return vk
}
return 0
}
func inputExtraInfo() uintptr {
if privacy.IsEnabled() {
return privacy.InputMarker()
}
return 0
}