- CaptureModeRunner: Thread.Sleep(150) → 16ms (~60fps) + Skip-if-unchanged via Pixel-Hash - RtcService: rdp_switch_screen Handler — Screen wechseln ohne Consent-Dialog - RtcService: Fallback bei Consent-Fehler → rdp_denied statt silent capture - RdpConsentWindow: WindowStyle=ToolWindow + ShowInTaskbar=True (jetzt in Taskleiste sichtbar) - RemoteDesktopPanel: Screen-Dropdown auch während aktiver Verbindung nutzbar - Version: 2.2.0 → 2.3.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
208 lines
8.1 KiB
C#
208 lines
8.1 KiB
C#
using ITNexusAgent.Models;
|
|
using ITNexusAgent.Services;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace ITNexusAgent;
|
|
|
|
public class AgentWorker
|
|
{
|
|
private const string Version = "2.3.0";
|
|
private const string DataDir = @"C:\ProgramData\IT Nexus Agent";
|
|
private const string ConfigPath = @"C:\ProgramData\IT Nexus Agent\config.json";
|
|
private const string StatusPath = @"C:\ProgramData\IT Nexus Agent\status.json";
|
|
private const string LogPath = @"C:\ProgramData\IT Nexus Agent\agent.log";
|
|
|
|
private readonly CancellationToken _ct;
|
|
private AgentConfig? _config;
|
|
private ApiService? _api;
|
|
private NotificationService? _notifier;
|
|
private string _exePath = "";
|
|
|
|
public AgentWorker(CancellationToken ct) => _ct = ct;
|
|
|
|
public async Task RunAsync()
|
|
{
|
|
_exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule!.FileName;
|
|
Directory.CreateDirectory(DataDir);
|
|
|
|
if (!File.Exists(ConfigPath))
|
|
{
|
|
Log("ERROR: config.json nicht gefunden");
|
|
return;
|
|
}
|
|
|
|
SecureConfigFile(ConfigPath);
|
|
|
|
_config = AgentConfig.Load(ConfigPath);
|
|
_api = new ApiService(_config.ServerUrl, _config.AgentKey);
|
|
_notifier = new NotificationService(_exePath, DataDir);
|
|
|
|
Log($"Agent v{Version} gestartet");
|
|
|
|
// WebSocket Shell-Service im Hintergrund starten
|
|
var shellService = new ShellService(_config.ServerUrl, _config.AgentKey, SystemInfoService.GetHostname());
|
|
_ = shellService.RunAsync(_ct);
|
|
|
|
// WebRTC Remote Desktop Service im Hintergrund starten
|
|
var rtcService = new RtcService(_config.ServerUrl, _config.AgentKey, SystemInfoService.GetHostname());
|
|
_ = rtcService.RunAsync(_ct);
|
|
|
|
while (!_ct.IsCancellationRequested)
|
|
{
|
|
await RunCycleAsync();
|
|
await Task.Delay(TimeSpan.FromMinutes(1), _ct).ContinueWith(_ => { });
|
|
}
|
|
}
|
|
|
|
private async Task RunCycleAsync()
|
|
{
|
|
try
|
|
{
|
|
Log("Sammle Systemdaten...");
|
|
var payload = CollectData();
|
|
var response = await _api!.CheckinAsync(payload);
|
|
|
|
Log($"OK: Checkin erfolgreich (Hostname: {payload.Hostname})");
|
|
|
|
// Status-Cache für Dashboard schreiben
|
|
WriteStatusCache(payload, response, true);
|
|
|
|
// Auto-Update prüfen
|
|
if (!string.IsNullOrEmpty(response.AgentVersion) &&
|
|
System.Version.TryParse(response.AgentVersion, out var serverVer) &&
|
|
System.Version.TryParse(Version, out var localVer) &&
|
|
serverVer > localVer)
|
|
{
|
|
Log($"UPDATE: Neue Agent-Version verfügbar: {response.AgentVersion} (aktuell: {Version})");
|
|
await new CommandExecutor(_api, payload.Hostname, DataDir, _exePath)
|
|
.ExecuteAsync(new PatchCommand { Id = 0, Command = "update_agent" });
|
|
return;
|
|
}
|
|
|
|
// Patch-Commands ausführen
|
|
var executor = new CommandExecutor(_api, payload.Hostname, DataDir, _exePath);
|
|
foreach (var cmd in response.Commands)
|
|
{
|
|
Log($"PATCH: Command empfangen: {cmd.Command}");
|
|
await executor.ExecuteAsync(cmd);
|
|
}
|
|
|
|
// Ankündigungen anzeigen + sofort ACKen damit Server sie nicht mehr schickt
|
|
if (response.Announcements.Count > 0)
|
|
{
|
|
_notifier!.ShowAnnouncements(response.Announcements);
|
|
foreach (var ann in response.Announcements)
|
|
{
|
|
Log($"ANNOUNCEMENT: Dialog für {SystemInfoService.GetLastUser()} gestartet - ID {ann.Id}");
|
|
try { await _api!.AckAnnouncementAsync(ann.Id, payload.Hostname); }
|
|
catch (Exception ex) { Log($"ANNOUNCEMENT ACK Fehler: {ex.Message}"); }
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log($"ERROR: {ex.Message}");
|
|
WriteStatusCache(null, null, false);
|
|
}
|
|
}
|
|
|
|
private CheckinPayload CollectData()
|
|
{
|
|
var (cpuModel, cpuCores) = SystemInfoService.GetCpuInfo();
|
|
var (ramTotal, ramUsed) = SystemInfoService.GetRamInfo();
|
|
var (diskTotal, diskFree) = SystemInfoService.GetDiskInfo();
|
|
var (tpmPresent, tpmVersion, tpmV2) = SystemInfoService.GetTpmInfo();
|
|
var (defEnabled, defSigAge, _) = SecurityInfoService.GetDefenderStatus();
|
|
|
|
return new CheckinPayload
|
|
{
|
|
Hostname = SystemInfoService.GetHostname(),
|
|
AgentVersion = Version,
|
|
Os = SystemInfoService.GetOs(),
|
|
OsVersion = SystemInfoService.GetOsVersion(),
|
|
Domain = SystemInfoService.GetDomain(),
|
|
IpAddress = SystemInfoService.GetIpAddress(),
|
|
MacAddress = SystemInfoService.GetMacAddress(),
|
|
LastUser = SystemInfoService.GetLastUser(),
|
|
CpuModel = cpuModel,
|
|
CpuCores = cpuCores,
|
|
CpuUsage = SystemInfoService.GetCpuUsage(),
|
|
RamTotal = ramTotal,
|
|
RamUsed = ramUsed,
|
|
DiskTotal = diskTotal,
|
|
DiskFree = diskFree,
|
|
UptimeHours = SystemInfoService.GetUptimeHours(),
|
|
PendingUpdates = SystemInfoService.GetPendingUpdates(),
|
|
TpmPresent = tpmPresent,
|
|
TpmVersion = tpmVersion,
|
|
TpmV2 = tpmV2,
|
|
SecureBoot = SystemInfoService.GetSecureBoot(),
|
|
Win11Ready = SystemInfoService.GetWin11Readiness(tpmV2, SystemInfoService.GetSecureBoot(), cpuCores, ramTotal),
|
|
BitlockerStatus = SecurityInfoService.GetBitlockerStatus(),
|
|
DefenderEnabled = defEnabled,
|
|
DefenderSignaturesAge = defSigAge >= 0 ? defSigAge : null,
|
|
HardwareSerial = SystemInfoService.GetHardwareSerial(),
|
|
InstalledSoftware = SystemInfoService.GetInstalledSoftware(),
|
|
};
|
|
}
|
|
|
|
private void WriteStatusCache(CheckinPayload? p, CheckinResponse? r, bool online)
|
|
{
|
|
try
|
|
{
|
|
var status = new StatusCache
|
|
{
|
|
Hostname = p?.Hostname ?? SystemInfoService.GetHostname(),
|
|
LastCheckin = DateTime.Now,
|
|
AgentVersion = Version,
|
|
ServerUrl = _config?.ServerUrl ?? "",
|
|
Online = online,
|
|
CpuUsage = p?.CpuUsage,
|
|
RamTotal = p?.RamTotal,
|
|
RamUsed = p?.RamUsed,
|
|
DiskTotal = p?.DiskTotal,
|
|
DiskFree = p?.DiskFree,
|
|
PendingUpdates = p?.PendingUpdates ?? 0,
|
|
BitlockerStatus = p?.BitlockerStatus ?? "",
|
|
DefenderEnabled = p?.DefenderEnabled ?? false,
|
|
DefenderSignaturesAge = p?.DefenderSignaturesAge,
|
|
HardwareSerial = p?.HardwareSerial ?? "",
|
|
Os = p?.Os ?? "",
|
|
LastUser = p?.LastUser ?? "",
|
|
InstalledSoftware = p?.InstalledSoftware ?? [],
|
|
};
|
|
File.WriteAllText(StatusPath, JsonConvert.SerializeObject(status, Formatting.Indented));
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private static void SecureConfigFile(string path)
|
|
{
|
|
try
|
|
{
|
|
var fi = new System.IO.FileInfo(path);
|
|
var acl = fi.GetAccessControl();
|
|
acl.SetAccessRuleProtection(true, false); // Vererbung entfernen
|
|
// Nur SYSTEM und Administratoren
|
|
acl.AddAccessRule(new System.Security.AccessControl.FileSystemAccessRule(
|
|
"SYSTEM", System.Security.AccessControl.FileSystemRights.FullControl,
|
|
System.Security.AccessControl.AccessControlType.Allow));
|
|
acl.AddAccessRule(new System.Security.AccessControl.FileSystemAccessRule(
|
|
"Administrators", System.Security.AccessControl.FileSystemRights.FullControl,
|
|
System.Security.AccessControl.AccessControlType.Allow));
|
|
fi.SetAccessControl(acl);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
public static void Log(string msg)
|
|
{
|
|
try
|
|
{
|
|
var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {msg}";
|
|
File.AppendAllText(LogPath, line + Environment.NewLine, System.Text.Encoding.UTF8);
|
|
}
|
|
catch { }
|
|
}
|
|
}
|