Fix pipe ACL: switch from named pipe to TCP loopback
Named pipe created by SYSTEM service denied connection from user process. TCP loopback socket (127.0.0.1:random) has no cross-session ACL issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,25 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO.Pipes;
|
using System.Net.Sockets;
|
||||||
|
|
||||||
namespace ITNexusAgent;
|
namespace ITNexusAgent;
|
||||||
|
|
||||||
// Läuft als User-Prozess (via schtasks), nicht als SYSTEM-Service
|
// Läuft als User-Prozess (via schtasks), nicht als SYSTEM-Service
|
||||||
// Captured den Desktop und schickt JPEG-Frames via Named Pipe an den Service
|
// Captured den Desktop und schickt JPEG-Frames via TCP Loopback an den Service
|
||||||
public static class CaptureModeRunner
|
public static class CaptureModeRunner
|
||||||
{
|
{
|
||||||
private static readonly ImageCodecInfo JpegCodec =
|
private static readonly ImageCodecInfo JpegCodec =
|
||||||
ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid);
|
ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid);
|
||||||
|
|
||||||
public static void Run(string pipeName)
|
public static void Run(string portStr)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(pipeName)) return;
|
if (!int.TryParse(portStr, out var port) || port <= 0) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.Out);
|
using var tcp = new TcpClient();
|
||||||
pipe.Connect(5000); // 5s timeout
|
tcp.Connect("127.0.0.1", port);
|
||||||
|
var stream = tcp.GetStream();
|
||||||
|
|
||||||
var encParams = new EncoderParameters(1);
|
var encParams = new EncoderParameters(1);
|
||||||
encParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 60L);
|
encParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 60L);
|
||||||
@@ -32,7 +33,7 @@ public static class CaptureModeRunner
|
|||||||
int totalW = right - left;
|
int totalW = right - left;
|
||||||
int totalH = bottom - top;
|
int totalH = bottom - top;
|
||||||
|
|
||||||
while (pipe.IsConnected)
|
while (tcp.Connected)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -47,16 +48,16 @@ public static class CaptureModeRunner
|
|||||||
jpeg = ms.ToArray();
|
jpeg = ms.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Länge (4 Bytes LE) + JPEG-Daten
|
// 4-Byte Länge (LE) + JPEG-Daten
|
||||||
pipe.Write(BitConverter.GetBytes(jpeg.Length));
|
stream.Write(BitConverter.GetBytes(jpeg.Length));
|
||||||
pipe.Write(jpeg);
|
stream.Write(jpeg);
|
||||||
pipe.Flush();
|
stream.Flush();
|
||||||
}
|
}
|
||||||
catch { break; }
|
catch { break; }
|
||||||
|
|
||||||
Thread.Sleep(150); // ~6-7 fps
|
Thread.Sleep(150); // ~6-7 fps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { /* Pipe nicht verfügbar oder Timeout → Exit */ }
|
catch { /* Verbindung fehlgeschlagen → Exit */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO.Pipes;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -82,56 +83,61 @@ public class RtcService
|
|||||||
|
|
||||||
private async Task CapturePipeLoopAsync(ClientWebSocket ws, CancellationToken ct)
|
private async Task CapturePipeLoopAsync(ClientWebSocket ws, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var pipeName = $"it-nexus-rdp-{Guid.NewGuid():N}";
|
|
||||||
var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule!.FileName;
|
var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule!.FileName;
|
||||||
|
|
||||||
using var pipeServer = new NamedPipeServerStream(
|
// TCP Loopback: kein ACL-Problem zwischen SYSTEM-Service und User-Prozess
|
||||||
pipeName, PipeDirection.In, 1,
|
var listener = new TcpListener(IPAddress.Loopback, 0);
|
||||||
PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
listener.Start();
|
||||||
|
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||||
|
|
||||||
// Helper via schtasks in User-Session starten
|
if (!SpawnCaptureHelper(exePath, port.ToString()))
|
||||||
if (!SpawnCaptureHelper(exePath, pipeName))
|
|
||||||
{
|
{
|
||||||
|
listener.Stop();
|
||||||
AgentWorker.Log("RDP: Helper-Start fehlgeschlagen");
|
AgentWorker.Log("RDP: Helper-Start fehlgeschlagen");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TcpClient? tcp = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var connectCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
using var connectCts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||||
using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct, connectCts.Token);
|
using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct, connectCts.Token);
|
||||||
await pipeServer.WaitForConnectionAsync(linked.Token);
|
tcp = await listener.AcceptTcpClientAsync(linked.Token);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
listener.Stop();
|
||||||
AgentWorker.Log("RDP: Helper hat sich nicht verbunden");
|
AgentWorker.Log("RDP: Helper hat sich nicht verbunden");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
finally { listener.Stop(); }
|
||||||
|
|
||||||
AgentWorker.Log("RDP: Helper verbunden, sende Frames...");
|
AgentWorker.Log($"RDP: Helper verbunden (Port {port}), sende Frames...");
|
||||||
|
|
||||||
|
using (tcp)
|
||||||
|
{
|
||||||
|
var stream = tcp.GetStream();
|
||||||
var lenBuf = new byte[4];
|
var lenBuf = new byte[4];
|
||||||
while (!ct.IsCancellationRequested && pipeServer.IsConnected && ws.State == WebSocketState.Open)
|
|
||||||
|
while (!ct.IsCancellationRequested && tcp.Connected && ws.State == WebSocketState.Open)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 4-Byte Länge lesen
|
|
||||||
var read = 0;
|
var read = 0;
|
||||||
while (read < 4)
|
while (read < 4)
|
||||||
{
|
{
|
||||||
var n = await pipeServer.ReadAsync(lenBuf.AsMemory(read, 4 - read), ct);
|
var n = await stream.ReadAsync(lenBuf.AsMemory(read, 4 - read), ct);
|
||||||
if (n == 0) goto done;
|
if (n == 0) goto done;
|
||||||
read += n;
|
read += n;
|
||||||
}
|
}
|
||||||
var jpegLen = BitConverter.ToInt32(lenBuf);
|
var jpegLen = BitConverter.ToInt32(lenBuf);
|
||||||
if (jpegLen <= 0 || jpegLen > 5_000_000) continue;
|
if (jpegLen <= 0 || jpegLen > 5_000_000) continue;
|
||||||
|
|
||||||
// JPEG-Daten lesen
|
|
||||||
var jpeg = new byte[jpegLen];
|
var jpeg = new byte[jpegLen];
|
||||||
read = 0;
|
read = 0;
|
||||||
while (read < jpegLen)
|
while (read < jpegLen)
|
||||||
{
|
{
|
||||||
var n = await pipeServer.ReadAsync(jpeg.AsMemory(read, jpegLen - read), ct);
|
var n = await stream.ReadAsync(jpeg.AsMemory(read, jpegLen - read), ct);
|
||||||
if (n == 0) goto done;
|
if (n == 0) goto done;
|
||||||
read += n;
|
read += n;
|
||||||
}
|
}
|
||||||
@@ -146,7 +152,8 @@ public class RtcService
|
|||||||
await ws.SendAsync(new ArraySegment<byte>(payload), WebSocketMessageType.Text, true, CancellationToken.None);
|
await ws.SendAsync(new ArraySegment<byte>(payload), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { break; }
|
catch (OperationCanceledException) { break; }
|
||||||
catch (Exception ex) { AgentWorker.Log($"RDP: Pipe-Fehler: {ex.Message}"); break; }
|
catch (Exception ex) { AgentWorker.Log($"RDP: TCP-Fehler: {ex.Message}"); break; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|||||||
Reference in New Issue
Block a user