# mIT-Viewer Legacy for Windows # Lekki klient audytowy dla starszych systemow Windows. # Generated by mIT-Desk. Empty ViewerToken is allowed. [CmdletBinding()] param( [switch]$InstallOnly, [switch]$RunOnce, [switch]$Service, [switch]$Tray, [switch]$UpdateSelf ) $ErrorActionPreference = "Stop" $ClientVersion = "0.1.1" $AgentChannel = "legacy" $ServerBaseUrl = 'https://helpdesk.mitgroup.pl' $ViewerToken = '' $ProgramFilesPath = if ($env:ProgramFiles) { $env:ProgramFiles } else { Join-Path $env:SystemDrive "Program Files" } $InstallDir = Join-Path $ProgramFilesPath "mIT-Desk Viewer Legacy" $AgentScript = Join-Path $InstallDir "mit-viewer-legacy.ps1" $LogDir = Join-Path $env:ProgramData "mIT-Desk Viewer" if (-not $LogDir -or $LogDir -eq "mIT-Desk Viewer") { $LogDir = Join-Path $env:ALLUSERSPROFILE "Application Data\mIT-Desk Viewer" } $SettingsFile = Join-Path $LogDir "viewer.settings.json" $StateFile = Join-Path $LogDir "legacy.state.json" $LogFile = Join-Path $LogDir "mit-viewer-legacy.log" $TaskName = "mIT-Desk Viewer Legacy" $RunKeyPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" $RunKeyName = "mIT-Viewer Legacy" function Write-MitLog { param([string]$Message) try { if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Add-Content -Path $LogFile -Value "$stamp $Message" } catch {} } function Set-MitTls { try { if ([enum]::GetNames([Net.SecurityProtocolType]) -contains "Tls12") { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } } catch {} } function ConvertTo-PlainObject { param($Value) if ($null -eq $Value) { return $null } if ($Value -is [string]) { return $Value } if ($Value -is [datetime]) { return $Value.ToUniversalTime().ToString("o") } if ($Value -is [bool] -or $Value -is [byte] -or $Value -is [int16] -or $Value -is [int] -or $Value -is [int64] -or $Value -is [single] -or $Value -is [double] -or $Value -is [decimal]) { return $Value } if ($Value -is [System.Collections.IDictionary]) { $hash = @{} foreach ($key in $Value.Keys) { $hash[[string]$key] = ConvertTo-PlainObject $Value[$key] } return $hash } if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { $items = @() foreach ($item in $Value) { $items += ,(ConvertTo-PlainObject $item) } return $items } $properties = @($Value.PSObject.Properties | Where-Object { $_.Name -and $_.Name -notmatch "^PS" }) if ($properties.Count -gt 0) { $hash = @{} foreach ($property in $properties) { try { $hash[$property.Name] = ConvertTo-PlainObject $property.Value } catch {} } return $hash } return [string]$Value } function ConvertTo-MitJsonString { param([string]$Text) if ($null -eq $Text) { return "null" } $escaped = $Text.Replace("\", "\\").Replace('"', '\"').Replace("`r", "\r").Replace("`n", "\n").Replace("`t", "\t") return '"' + $escaped + '"' } function ConvertTo-MitJsonValue { param($Value) if ($null -eq $Value) { return "null" } if ($Value -is [string]) { return ConvertTo-MitJsonString $Value } if ($Value -is [datetime]) { return ConvertTo-MitJsonString $Value.ToUniversalTime().ToString("o") } if ($Value -is [bool]) { if ($Value) { return "true" } else { return "false" } } if ($Value -is [byte] -or $Value -is [int16] -or $Value -is [int] -or $Value -is [int64] -or $Value -is [single] -or $Value -is [double] -or $Value -is [decimal]) { return [string]::Format([Globalization.CultureInfo]::InvariantCulture, "{0}", $Value) } if ($Value -is [System.Collections.IDictionary]) { $pairs = @() foreach ($key in $Value.Keys) { $pairs += (ConvertTo-MitJsonString ([string]$key)) + ":" + (ConvertTo-MitJsonValue $Value[$key]) } return "{" + ($pairs -join ",") + "}" } if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { $items = @() foreach ($item in $Value) { $items += ConvertTo-MitJsonValue $item } return "[" + ($items -join ",") + "]" } return ConvertTo-MitJsonString ([string]$Value) } function ConvertTo-MitJson { param($Value) $plain = ConvertTo-PlainObject $Value $convert = Get-Command ConvertTo-Json -ErrorAction SilentlyContinue if ($convert) { return ($plain | ConvertTo-Json -Depth 20 -Compress) } try { Add-Type -AssemblyName System.Web.Extensions -ErrorAction Stop $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer $serializer.MaxJsonLength = 104857600 return $serializer.Serialize($plain) } catch { return ConvertTo-MitJsonValue $plain } } function ConvertFrom-MitJson { param([string]$Json) if (-not $Json) { return $null } $convert = Get-Command ConvertFrom-Json -ErrorAction SilentlyContinue if ($convert) { return ($Json | ConvertFrom-Json) } try { Add-Type -AssemblyName System.Web.Extensions -ErrorAction Stop $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer return $serializer.DeserializeObject($Json) } catch { return $null } } function Get-JsonValue { param($Object, [string]$Name) if ($null -eq $Object) { return $null } if ($Object -is [System.Collections.IDictionary]) { if ($Object.Contains($Name)) { return $Object[$Name] } return $null } try { return $Object.$Name } catch { return $null } } function Read-MitSettings { if (-not (Test-Path $SettingsFile)) { return } try { $settingsText = [string]::Join("`n", @(Get-Content -Path $SettingsFile)) $settings = ConvertFrom-MitJson $settingsText $base = Get-JsonValue $settings "serverBaseUrl" $token = Get-JsonValue $settings "viewerToken" if ($base) { $script:ServerBaseUrl = ([string]$base).TrimEnd("/") } if ($null -ne $token) { $script:ViewerToken = [string]$token } } catch { Write-MitLog "Could not read settings: $($_.Exception.Message)" } } function Write-MitSettings { if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $settings = @{ serverBaseUrl = $ServerBaseUrl.TrimEnd("/") viewerToken = $ViewerToken channel = $AgentChannel version = $ClientVersion } Set-Content -Path $SettingsFile -Value (ConvertTo-MitJson $settings) -Encoding UTF8 } function Get-MitHeaders { $headers = @{} if ($ViewerToken -and $ViewerToken.Trim()) { $headers["Authorization"] = "Bearer $($ViewerToken.Trim())" $headers["X-mIT-Viewer-Token"] = $ViewerToken.Trim() } return $headers } function Invoke-MitHttpJson { param( [string]$Method, [string]$Uri, $Body = $null, [hashtable]$Headers = @{}, [int]$TimeoutSeconds = 120 ) Set-MitTls $json = $null if ($null -ne $Body) { if ($Body -is [string]) { $json = $Body } else { $json = ConvertTo-MitJson $Body } } $rest = Get-Command Invoke-RestMethod -ErrorAction SilentlyContinue if ($rest) { $params = @{ Method = $Method Uri = $Uri TimeoutSec = $TimeoutSeconds ErrorAction = "Stop" } if ($Headers -and $Headers.Count -gt 0) { $params.Headers = $Headers } if ($null -ne $json) { $params.Body = $json $params.ContentType = "application/json; charset=utf-8" } return Invoke-RestMethod @params } $client = New-Object Net.WebClient $client.Encoding = [Text.Encoding]::UTF8 $client.Headers.Add("Accept", "application/json") foreach ($key in $Headers.Keys) { $client.Headers.Add([string]$key, [string]$Headers[$key]) } if ($null -ne $json) { $client.Headers.Add("Content-Type", "application/json; charset=utf-8") $response = $client.UploadString($Uri, $Method.ToUpperInvariant(), $json) } else { $response = $client.DownloadString($Uri) } return ConvertFrom-MitJson $response } function Get-WmiSafe { param([string]$ClassName, [string]$Namespace = "root\cimv2") try { return @(Get-WmiObject -Namespace $Namespace -Class $ClassName -ErrorAction Stop) } catch { $cim = Get-Command Get-CimInstance -ErrorAction SilentlyContinue if ($cim) { try { return @(Get-CimInstance -Namespace $Namespace -ClassName $ClassName -ErrorAction Stop) } catch {} } return @() } } function Convert-WmiDate { param($Value) if (-not $Value) { return $null } try { return [Management.ManagementDateTimeConverter]::ToDateTime($Value).ToUniversalTime().ToString("o") } catch { return $null } } function Get-RegistrySoftware { $paths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" ) $items = @() foreach ($path in $paths) { try { $items += @(Get-ItemProperty $path -ErrorAction Stop | Where-Object { $_.DisplayName } | ForEach-Object { @{ name = [string]$_.DisplayName version = if ($_.DisplayVersion) { [string]$_.DisplayVersion } else { $null } publisher = if ($_.Publisher) { [string]$_.Publisher } else { $null } installed_at = if ($_.InstallDate) { [string]$_.InstallDate } else { $null } } }) } catch {} } return @($items | Sort-Object name, version -Unique) } function Get-IpAddresses { $ips = @() $configs = Get-WmiSafe Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled } foreach ($config in $configs) { foreach ($ip in @($config.IPAddress)) { if ($ip -and $ip -match "\." -and $ip -notlike "169.254.*") { $ips += [string]$ip } } } return @($ips | Select-Object -Unique) } function Get-MacAddresses { return @(Get-WmiSafe Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -and $_.MACAddress } | ForEach-Object { [string]$_.MACAddress } | Select-Object -Unique) } function Get-PublicIp { try { Set-MitTls $request = [Net.WebRequest]::Create("https://ifconfig.me/ip") $request.Timeout = 5000 $response = $request.GetResponse() $reader = New-Object IO.StreamReader($response.GetResponseStream()) $value = $reader.ReadToEnd().Trim() $reader.Close() $response.Close() if ($value -and $value.Length -lt 80) { return $value } } catch {} return $null } function Get-Disks { return @(Get-WmiSafe Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } | ForEach-Object { @{ name = [string]$_.DeviceID mount = [string]$_.DeviceID type = "Fixed" total_bytes = if ($_.Size) { [int64]$_.Size } else { $null } free_bytes = if ($_.FreeSpace) { [int64]$_.FreeSpace } else { $null } filesystem = if ($_.FileSystem) { [string]$_.FileSystem } else { $null } health = "unknown" } }) } function Convert-EdidString { param($Value) if (-not $Value) { return $null } $chars = @() foreach ($code in @($Value)) { try { $number = [int]$code if ($number -eq 0) { break } $chars += [char]$number } catch {} } $text = -join $chars if ($text) { return $text.Trim() } return $null } function Get-PrinterInventory { $ports = @{} foreach ($port in @(Get-WmiSafe Win32_TCPIPPrinterPort)) { if ($port.Name) { $ports[[string]$port.Name] = $port } } return @(Get-WmiSafe Win32_Printer | ForEach-Object { $portName = if ($_.PortName) { [string]$_.PortName } else { $null } $tcpPort = if ($portName -and $ports.ContainsKey($portName)) { $ports[$portName] } else { $null } $hostAddress = if ($tcpPort -and $tcpPort.HostAddress) { [string]$tcpPort.HostAddress } else { $null } if (-not $hostAddress -and $portName -match '(\d{1,3}(\.\d{1,3}){3})') { $hostAddress = $Matches[1] } $connection = "unknown" if ($hostAddress -or $_.Network) { $connection = "ip" } elseif ($portName -and ($portName.ToUpperInvariant().StartsWith("USB") -or $portName.ToUpperInvariant().StartsWith("DOT4"))) { $connection = "usb" } elseif ($_.Local) { $connection = "local" } @{ category = "printer" type = "printer" name = if ($_.Name) { [string]$_.Name } else { $null } model = if ($_.DriverName) { [string]$_.DriverName } else { $null } driver_name = if ($_.DriverName) { [string]$_.DriverName } else { $null } connection = $connection ip_address = $hostAddress host_address = $hostAddress port_name = $portName default = $_.Default network = $_.Network local = $_.Local status = if ($_.Status) { [string]$_.Status } else { $null } } }) } function Get-MonitorInventory { $edid = @(Get-WmiSafe WmiMonitorID "root\wmi") if (@($edid).Count -gt 0) { return @($edid | ForEach-Object { @{ category = "monitor" type = "display" name = (Convert-EdidString $_.UserFriendlyName) manufacturer = (Convert-EdidString $_.ManufacturerName) model = (Convert-EdidString $_.ProductCodeID) serial_number = (Convert-EdidString $_.SerialNumberID) connection = "display" pnp_device_id = if ($_.InstanceName) { [string]$_.InstanceName } else { $null } } }) } return @(Get-WmiSafe Win32_DesktopMonitor | ForEach-Object { $resolution = $null if ($_.ScreenWidth -and $_.ScreenHeight) { $resolution = "$($_.ScreenWidth)x$($_.ScreenHeight)" } @{ category = "monitor" type = "display" name = if ($_.Name) { [string]$_.Name } else { "Monitor" } connection = "display" resolution = $resolution pnp_device_id = if ($_.PNPDeviceID) { [string]$_.PNPDeviceID } else { $null } } }) } function Get-UsbSerialFromPnpId([string]$PnpDeviceId) { if (-not $PnpDeviceId) { return $null } $parts = @($PnpDeviceId -split '\\' | Where-Object { $_ -and $_.Trim() }) if ($parts.Count -lt 3) { return $null } $candidate = [uri]::UnescapeDataString([string]$parts[-1]).Trim() if (-not $candidate -or $candidate.Length -lt 3) { return $null } $upper = $candidate.ToUpperInvariant() if ($upper -in @("0000", "UNKNOWN", "NONE")) { return $null } if ($upper.Contains("&") -or $upper.Contains("VID_") -or $upper.Contains("PID_") -or $upper.StartsWith("MI_") -or $upper.StartsWith("COL") -or $upper.StartsWith("ROOT_HUB") -or $upper.StartsWith("HUB")) { return $null } return $candidate } function Get-UsbDeviceInventory { return @(Get-WmiSafe Win32_PnPEntity | Where-Object { $_.PNPDeviceID -and ($_.PNPDeviceID -like "USB*" -or $_.PNPDeviceID -like "HID*") } | Select-Object -First 300 | ForEach-Object { $pnp = [string]$_.PNPDeviceID $vid = $null $pid = $null if ($pnp -match 'VID_([0-9A-Fa-f]{4}).*PID_([0-9A-Fa-f]{4})') { $vid = $Matches[1].ToUpperInvariant() $pid = $Matches[2].ToUpperInvariant() } $text = ("{0} {1}" -f $_.PNPClass, $_.Name).ToLowerInvariant() $type = "usb" if ($text -match "keyboard") { $type = "keyboard" } elseif ($text -match "mouse|pointing") { $type = "mouse" } elseif ($text -match "camera|image") { $type = "camera" } elseif ($text -match "audio|media") { $type = "audio" } elseif ($text -match "disk|storage") { $type = "storage" } elseif ($text -match "controller|game|joystick") { $type = "controller" } elseif ($_.PNPClass) { $type = [string]$_.PNPClass } @{ category = "usb" type = $type name = if ($_.Name) { [string]$_.Name } else { $null } model = if ($_.Description) { [string]$_.Description } else { $null } manufacturer = if ($_.Manufacturer) { [string]$_.Manufacturer } else { $null } connection = if ($pnp -like "HID*") { "hid/usb" } else { "usb" } vendor_id = $vid product_id = $pid serial_number = Get-UsbSerialFromPnpId $pnp pnp_device_id = $pnp status = if ($_.Status) { [string]$_.Status } else { $null } } }) } function Get-PeripheralInventory { $items = @() $items += @(Get-PrinterInventory) $items += @(Get-MonitorInventory) $items += @(Get-UsbDeviceInventory) return @($items) } function Format-HyperVMac([string]$Value) { if (-not $Value) { return $null } $clean = ($Value -replace '[^0-9A-Fa-f]', '').ToUpperInvariant() if ($clean.Length -ne 12) { return $Value } return (($clean -split '(.{2})' | Where-Object { $_ }) -join '-') } function Get-HyperVInventory { $result = @{ installed = $false module_available = $false vm_count = 0 vms = @() collected_at = (Get-Date).ToUniversalTime().ToString("o") error = $null } try { $feature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -ErrorAction SilentlyContinue if ($feature -and [string]$feature.State -eq "Enabled") { $result.installed = $true } } catch {} $getVmCommand = Get-Command Get-VM -ErrorAction SilentlyContinue $result.module_available = [bool]$getVmCommand if (-not $result.module_available) { return $result } if (-not $result.installed) { $result.installed = $true } try { $vms = @(Get-VM -ErrorAction Stop | Select-Object -First 200 | ForEach-Object { $vm = $_ $adapters = @(Get-VMNetworkAdapter -VMName $vm.Name -ErrorAction SilentlyContinue | ForEach-Object { $ips = @($_.IPAddresses | Where-Object { $_ } | Select-Object -Unique) @{ name = if ($_.Name) { [string]$_.Name } else { $null } switch_name = if ($_.SwitchName) { [string]$_.SwitchName } else { $null } mac_address = Format-HyperVMac ([string]$_.MacAddress) ip_addresses = $ips status = if ($_.Status) { [string]$_.Status } else { $null } connected = [bool]$_.Connected is_management_os = [bool]$_.IsManagementOs } }) $disks = @(Get-VMHardDiskDrive -VMName $vm.Name -ErrorAction SilentlyContinue | ForEach-Object { $drive = $_ $vhd = $null if ($drive.Path) { $vhd = Get-VHD -Path $drive.Path -ErrorAction SilentlyContinue } @{ controller_type = if ($drive.ControllerType) { [string]$drive.ControllerType } else { $null } controller_number = $drive.ControllerNumber controller_location = $drive.ControllerLocation path = if ($drive.Path) { [string]$drive.Path } else { $null } size_bytes = if ($vhd) { [int64]$vhd.Size } else { $null } file_size_bytes = if ($vhd) { [int64]$vhd.FileSize } else { $null } vhd_type = if ($vhd) { [string]$vhd.VhdType } else { $null } format = if ($vhd) { [string]$vhd.VhdFormat } else { $null } } }) $checkpoints = @() try { $checkpoints = @(Get-VMSnapshot -VMName $vm.Name -ErrorAction SilentlyContinue) } catch {} @{ name = if ($vm.Name) { [string]$vm.Name } else { $null } id = if ($vm.Id) { [string]$vm.Id } else { $null } state = if ($vm.State) { [string]$vm.State } else { $null } status = if ($vm.Status) { [string]$vm.Status } else { $null } generation = $vm.Generation version = if ($vm.Version) { [string]$vm.Version } else { $null } processor_count = $vm.ProcessorCount memory_assigned_bytes = [int64]$vm.MemoryAssigned memory_startup_bytes = [int64]$vm.MemoryStartup dynamic_memory_enabled = [bool]$vm.DynamicMemoryEnabled uptime_seconds = if ($vm.Uptime) { [int64]$vm.Uptime.TotalSeconds } else { 0 } configuration_location = if ($vm.ConfigurationLocation) { [string]$vm.ConfigurationLocation } else { $null } smart_paging_file_path = if ($vm.SmartPagingFilePath) { [string]$vm.SmartPagingFilePath } else { $null } notes = if ($vm.Notes) { [string]$vm.Notes } else { $null } network_adapters = $adapters ip_addresses = @($adapters | ForEach-Object { $_.ip_addresses } | Where-Object { $_ } | Select-Object -Unique) disks = $disks checkpoint_count = @($checkpoints).Count } }) $result.vms = $vms $result.vm_count = @($vms).Count } catch { $result.error = $_.Exception.Message } return $result } function Get-WindowsUpdates { try { return @(Get-HotFix -ErrorAction Stop | Sort-Object InstalledOn -Descending | Select-Object -First 80 | ForEach-Object { @{ id = [string]$_.HotFixID description = [string]$_.Description installed_by = [string]$_.InstalledBy installed_on = if ($_.InstalledOn) { ([datetime]$_.InstalledOn).ToString("yyyy-MM-dd") } else { $null } } }) } catch { return @() } } function Get-SecuritySummary { $antivirus = @() try { $antivirus = @(Get-WmiSafe AntiVirusProduct "root\SecurityCenter2" | ForEach-Object { [string]$_.displayName }) } catch {} $firewall = "unknown" try { $fw = Get-WmiSafe FirewallProduct "root\SecurityCenter2" if (@($fw).Count -gt 0) { $firewall = "installed" } } catch {} return @{ firewall = $firewall antivirus = ($antivirus -join ", ") encryption = "unknown" legacy_limited = $true } } function Find-RustDeskExecutable { $pf86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)") $local = [Environment]::GetEnvironmentVariable("LOCALAPPDATA") $candidates = @( (Join-Path $ProgramFilesPath "RustDesk\RustDesk.exe"), (Join-Path $ProgramFilesPath "RustDesk\rustdesk.exe") ) if ($pf86) { $candidates += (Join-Path $pf86 "RustDesk\RustDesk.exe") $candidates += (Join-Path $pf86 "RustDesk\rustdesk.exe") } if ($local) { $candidates += (Join-Path $local "Programs\RustDesk\RustDesk.exe") $candidates += (Join-Path $local "Programs\RustDesk\rustdesk.exe") } foreach ($candidate in $candidates) { if ($candidate -and (Test-Path $candidate)) { return $candidate } } return $null } function Get-RustDeskIdFromFile { $paths = @( (Join-Path $env:APPDATA "RustDesk\config\RustDesk2.toml"), (Join-Path $env:ProgramData "RustDesk\config\RustDesk2.toml"), (Join-Path $env:ProgramData "RustDesk\config\RustDesk.toml") ) foreach ($path in $paths) { try { if (Test-Path $path) { $content = Get-Content -Path $path -ErrorAction Stop foreach ($line in $content) { if ($line -match '^\s*id\s*=\s*"?([0-9]+)"?') { return $Matches[1] } } } } catch {} } return $null } function Get-RustDeskState { $exe = Find-RustDeskExecutable $id = Get-RustDeskIdFromFile $running = $false try { $running = @(Get-Process -Name "RustDesk" -ErrorAction SilentlyContinue).Count -gt 0 } catch {} $status = "not_installed" if ($exe -and $id) { $status = "online" } elseif ($exe -and $running) { $status = "running_no_id" } elseif ($exe) { $status = "installed_no_id" } return @{ id = $id; status = $status; executable_path = $exe } } function Get-StartupItems { $items = @() $paths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" ) foreach ($path in $paths) { try { $props = Get-ItemProperty $path -ErrorAction Stop foreach ($property in @($props.PSObject.Properties | Where-Object { $_.Name -notmatch "^PS" })) { $items += @{ source = $path name = [string]$property.Name command = [string]$property.Value } } } catch {} } $items += @(Get-WmiSafe Win32_StartupCommand | ForEach-Object { @{ source = [string]$_.Location name = [string]$_.Name command = [string]$_.Command } }) return @($items) } function Get-BasePayload { $computer = Get-WmiSafe Win32_ComputerSystem | Select-Object -First 1 $os = Get-WmiSafe Win32_OperatingSystem | Select-Object -First 1 $cpu = Get-WmiSafe Win32_Processor | Select-Object -First 1 $bios = Get-WmiSafe Win32_BIOS | Select-Object -First 1 $product = Get-WmiSafe Win32_ComputerSystemProduct | Select-Object -First 1 $battery = Get-WmiSafe Win32_Battery | Select-Object -First 1 $ips = @(Get-IpAddresses) $macs = @(Get-MacAddresses) $rustDesk = Get-RustDeskState $boot = $null if ($os -and $os.LastBootUpTime) { try { $boot = [Management.ManagementDateTimeConverter]::ToDateTime($os.LastBootUpTime).ToUniversalTime() } catch {} } $uptime = if ($boot) { [int64]([datetime]::UtcNow - $boot).TotalSeconds } else { $null } $serial = if ($bios -and $bios.SerialNumber) { [string]$bios.SerialNumber } else { "" } $uuid = if ($product -and $product.UUID) { [string]$product.UUID } else { "" } $identity = "$($env:COMPUTERNAME)-$serial-$uuid" return @{ device_uid = $identity viewer_token = if ($ViewerToken -and $ViewerToken.Trim()) { $ViewerToken.Trim() } else { $null } viewerToken = if ($ViewerToken -and $ViewerToken.Trim()) { $ViewerToken.Trim() } else { $null } hostname = [string]$env:COMPUTERNAME display_name = [string]$env:COMPUTERNAME device_type = if ($battery) { "laptop" } else { "workstation" } os_name = if ($os -and $os.Caption) { [string]$os.Caption } else { "Windows" } os_version = if ($os -and $os.Version) { [string]$os.Version } else { "" } os_build = if ($os -and $os.BuildNumber) { [string]$os.BuildNumber } else { $null } kernel_version = if ($os -and $os.Version) { [string]$os.Version } else { $null } architecture = if ($os -and $os.OSArchitecture) { [string]$os.OSArchitecture } else { [string]$env:PROCESSOR_ARCHITECTURE } cpu = if ($cpu -and $cpu.Name) { [string]$cpu.Name } else { $null } cpu_cores = if ($cpu -and $cpu.NumberOfLogicalProcessors) { [int]$cpu.NumberOfLogicalProcessors } elseif ($cpu -and $cpu.NumberOfCores) { [int]$cpu.NumberOfCores } else { $null } cpu_load_percent = if ($cpu -and $null -ne $cpu.LoadPercentage) { [double]$cpu.LoadPercentage } else { $null } ram_bytes = if ($computer -and $computer.TotalPhysicalMemory) { [int64]$computer.TotalPhysicalMemory } else { $null } ram_used_bytes = if ($os -and $os.TotalVisibleMemorySize -and $os.FreePhysicalMemory) { [int64](($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) * 1024) } else { $null } disks = @(Get-Disks) ip_addresses = $ips mac_addresses = $macs public_ip = Get-PublicIp logged_in_user = if ($computer -and $computer.UserName) { [string]$computer.UserName } else { [string]$env:USERNAME } agent_version = $ClientVersion agent_channel = $AgentChannel rustdesk_id = $rustDesk.id rustdesk_status = $rustDesk.status serial_number = if ($serial) { $serial } else { $null } manufacturer = if ($computer -and $computer.Manufacturer) { [string]$computer.Manufacturer } else { $null } model = if ($computer -and $computer.Model) { [string]$computer.Model } else { $null } uptime_seconds = $uptime boot_time = if ($boot) { $boot.ToString("o") } else { $null } status = "online" health_status = "unknown" monitoring = @{ source = "mit-viewer-legacy" collected_at = (Get-Date).ToUniversalTime().ToString("o") rustdesk = $rustDesk } } } function Get-AuditPayload { $payload = Get-BasePayload $baseboard = Get-WmiSafe Win32_BaseBoard | Select-Object -First 1 $ramModules = Get-WmiSafe Win32_PhysicalMemory $video = Get-WmiSafe Win32_VideoController $networkAdapters = Get-WmiSafe Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled } $userAccounts = Get-WmiSafe Win32_UserAccount | Select-Object -First 200 $shares = Get-WmiSafe Win32_Share $services = Get-WmiSafe Win32_Service | Select-Object -First 300 $processes = Get-WmiSafe Win32_Process | Select-Object -First 300 $updates = @(Get-WindowsUpdates) $printerInventory = @(Get-PrinterInventory) $monitorInventory = @(Get-MonitorInventory) $usbInventory = @(Get-UsbDeviceInventory) $peripherals = @($printerInventory + $monitorInventory + $usbInventory) $hyperVInventory = Get-HyperVInventory $payload.security = Get-SecuritySummary $payload.patches = @{ installed_count = @($updates).Count pending_count = $null last_update_at = $null } $payload.audit = @{ legacy_client = $true legacy_limitations = @("PowerShell/WMI fallback", "No native service UI bridge", "RustDesk only if supported by installed RustDesk build") motherboard = if ($baseboard) { @{ manufacturer = $baseboard.Manufacturer; product = $baseboard.Product; serial_number = $baseboard.SerialNumber } } else { @{} } ram_modules = @($ramModules | ForEach-Object { @{ capacity_bytes = if ($_.Capacity) { [int64]$_.Capacity } else { $null }; manufacturer = $_.Manufacturer; part_number = $_.PartNumber; speed = $_.Speed } }) monitors = $monitorInventory video_controllers = @($video | ForEach-Object { @{ name = $_.Name; driver_version = $_.DriverVersion; ram_bytes = $_.AdapterRAM } }) network_adapters = @($networkAdapters | ForEach-Object { @{ description = $_.Description; mac_address = $_.MACAddress; ip_addresses = $_.IPAddress; dhcp_enabled = $_.DHCPEnabled } }) user_accounts = @($userAccounts | ForEach-Object { @{ name = $_.Name; domain = $_.Domain; disabled = $_.Disabled; local_account = $_.LocalAccount; sid = $_.SID } }) startup_items = @(Get-StartupItems) running_processes = @($processes | ForEach-Object { @{ name = $_.Name; process_id = $_.ProcessId; executable_path = $_.ExecutablePath; command_line = $_.CommandLine } }) printers = $printerInventory usb_devices = $usbInventory peripherals = $peripherals shares = @($shares | ForEach-Object { @{ name = $_.Name; path = $_.Path; type = $_.Type } }) services = @($services | ForEach-Object { @{ name = $_.Name; display_name = $_.DisplayName; state = $_.State; start_mode = $_.StartMode } }) windows_updates = $updates hyper_v = $hyperVInventory } $payload.peripherals = $peripherals $payload.tags = @("windows", "legacy", "mit-viewer") $payload.software = @(Get-RegistrySoftware) return $payload } function Save-MitState { param($Payload, $Response) try { $state = @{ device_uid = $Payload.device_uid hostname = $Payload.hostname support_id = Get-JsonValue $Response "support_id" last_seen_at = (Get-Date).ToUniversalTime().ToString("o") version = $ClientVersion channel = $AgentChannel } Set-Content -Path $StateFile -Value (ConvertTo-MitJson $state) -Encoding UTF8 } catch {} } function Invoke-MitViewerAudit { $payload = Get-AuditPayload $uri = "$($ServerBaseUrl.TrimEnd('/'))/api/agent/devices/register" $response = Invoke-MitHttpJson -Method "POST" -Uri $uri -Headers (Get-MitHeaders) -Body $payload -TimeoutSeconds 180 Save-MitState -Payload $payload -Response $response Write-MitLog "Legacy audit sent successfully to $uri" return $payload } function Invoke-MitViewerHeartbeat { $payload = Get-BasePayload $heartbeat = @{ device_uid = $payload.device_uid viewer_token = $payload.viewer_token hostname = $payload.hostname status = "online" health_status = "unknown" agent_version = $ClientVersion logged_in_user = $payload.logged_in_user ip_addresses = $payload.ip_addresses public_ip = $payload.public_ip cpu_load_percent = $payload.cpu_load_percent ram_used_bytes = $payload.ram_used_bytes uptime_seconds = $payload.uptime_seconds rustdesk_id = $payload.rustdesk_id rustdesk_status = $payload.rustdesk_status sent_at = (Get-Date).ToUniversalTime().ToString("o") monitoring = $payload.monitoring } $uri = "$($ServerBaseUrl.TrimEnd('/'))/api/agent/devices/$([uri]::EscapeDataString($payload.device_uid))/heartbeat" Invoke-MitHttpJson -Method "POST" -Uri $uri -Headers (Get-MitHeaders) -Body $heartbeat -TimeoutSeconds 60 | Out-Null Write-MitLog "Legacy heartbeat sent successfully." return $payload } function Complete-MitCommand { param($Command, [string]$Status, $Result = @{}, [string]$ErrorMessage = "") $id = Get-JsonValue $Command "id" if (-not $id) { return } $body = @{ status = $Status result = $Result error_message = $ErrorMessage } $uri = "$($ServerBaseUrl.TrimEnd('/'))/api/agent/device-commands/$id/status" try { Invoke-MitHttpJson -Method "POST" -Uri $uri -Headers (Get-MitHeaders) -Body $body -TimeoutSeconds 60 | Out-Null } catch { Write-MitLog "Command status update failed: $($_.Exception.Message)" } } function Get-PendingMitCommands { param([string]$DeviceUid, [string]$CommandType) if (-not $DeviceUid) { return @() } $uri = "$($ServerBaseUrl.TrimEnd('/'))/api/agent/devices/$([uri]::EscapeDataString($DeviceUid))/commands/pending?command_type=$([uri]::EscapeDataString($CommandType))" try { $commands = Invoke-MitHttpJson -Method "GET" -Uri $uri -Headers (Get-MitHeaders) -TimeoutSeconds 60 if ($null -eq $commands) { return @() } if ($commands -is [System.Array]) { return @($commands) } return @($commands) } catch { Write-MitLog "Pending commands fetch failed: $($_.Exception.Message)" return @() } } function Invoke-LegacySelfUpdate { if (-not $ViewerToken -or -not $ViewerToken.Trim()) { return @{ updated = $false; reason = "viewer token is empty" } } $url = "$($ServerBaseUrl.TrimEnd('/'))/viewer/$([uri]::EscapeDataString($ViewerToken.Trim()))/legacy.ps1" $temp = Join-Path $LogDir "mit-viewer-legacy.new.ps1" Set-MitTls $client = New-Object Net.WebClient $client.Encoding = [Text.Encoding]::UTF8 $scriptText = $client.DownloadString($url) if (-not $scriptText -or $scriptText -notmatch "mIT-Viewer Legacy") { throw "Downloaded legacy script does not look valid." } Set-Content -Path $temp -Value $scriptText -Encoding UTF8 Copy-Item -Path $temp -Destination $AgentScript -Force Remove-Item -Path $temp -Force -ErrorAction SilentlyContinue Write-MitSettings return @{ updated = $true; url = $url; version = $ClientVersion } } function Invoke-LegacySelfUninstall { if (-not $env:TEMP) { $env:TEMP = $LogDir } $deleteScript = Join-Path $env:TEMP ("mit-viewer-legacy-uninstall-{0}.cmd" -f $PID) $lines = @( "@echo off", "ping 127.0.0.1 -n 3 > nul", "schtasks.exe /Delete /TN ""$TaskName"" /F > nul 2>&1", "reg.exe delete ""HKLM\Software\Microsoft\Windows\CurrentVersion\Run"" /v ""$RunKeyName"" /f > nul 2>&1", "rmdir /S /Q ""$InstallDir"" > nul 2>&1", "rmdir /S /Q ""$LogDir"" > nul 2>&1", "del /F /Q ""%~f0"" > nul 2>&1" ) Set-Content -Path $deleteScript -Value ($lines -join "`r`n") -Encoding ASCII Start-Process -FilePath $env:ComSpec -ArgumentList "/c `"$deleteScript`"" -WindowStyle Hidden Write-MitLog "Legacy self-uninstall scheduled." return @{ uninstall_started = $true remove_viewer = $true remove_rustdesk = $false legacy = $true install_dir = $InstallDir } } function Process-MitCommands { param([string]$DeviceUid, [string]$Mode) if ($Mode -eq "service") { foreach ($command in Get-PendingMitCommands -DeviceUid $DeviceUid -CommandType "client_uninstall") { try { Complete-MitCommand -Command $command -Status "picked_up" -Result @{ legacy = $true } $result = Invoke-LegacySelfUninstall Complete-MitCommand -Command $command -Status "completed" -Result $result exit 0 } catch { Complete-MitCommand -Command $command -Status "failed" -Result @{ legacy = $true } -ErrorMessage $_.Exception.Message } } foreach ($command in Get-PendingMitCommands -DeviceUid $DeviceUid -CommandType "force_audit") { try { Complete-MitCommand -Command $command -Status "picked_up" -Result @{ legacy = $true } Invoke-MitViewerAudit | Out-Null Complete-MitCommand -Command $command -Status "completed" -Result @{ audit_sent = $true; legacy = $true } } catch { Complete-MitCommand -Command $command -Status "failed" -Result @{ legacy = $true } -ErrorMessage $_.Exception.Message } } foreach ($command in Get-PendingMitCommands -DeviceUid $DeviceUid -CommandType "client_update") { try { Complete-MitCommand -Command $command -Status "picked_up" -Result @{ legacy = $true } $result = Invoke-LegacySelfUpdate Complete-MitCommand -Command $command -Status "completed" -Result $result } catch { Complete-MitCommand -Command $command -Status "failed" -Result @{ legacy = $true } -ErrorMessage $_.Exception.Message } } } if ($Mode -eq "tray") { foreach ($command in Get-PendingMitCommands -DeviceUid $DeviceUid -CommandType "user_notify") { $payload = Get-JsonValue $command "payload" $text = Get-JsonValue $payload "text" if (-not $text) { $text = "Wiadomosc od konsultanta mIT-Desk." } Show-MitBalloon -Title "mIT-Desk" -Message ([string]$text) -Milliseconds 8000 Complete-MitCommand -Command $command -Status "completed" -Result @{ shown = $true; legacy = $true } } } } function Show-MitBalloon { param([string]$Title, [string]$Message, [int]$Milliseconds = 4000) try { if ($script:NotifyIcon) { $script:NotifyIcon.ShowBalloonTip($Milliseconds, $Title, $Message, [System.Windows.Forms.ToolTipIcon]::Info) } } catch {} } function Start-MitViewerTray { Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $script:NotifyIcon = New-Object System.Windows.Forms.NotifyIcon $script:NotifyIcon.Text = "mIT-Viewer Legacy" $script:NotifyIcon.Icon = [System.Drawing.SystemIcons]::Information $script:NotifyIcon.Visible = $true $menu = New-Object System.Windows.Forms.ContextMenuStrip $statusItem = New-Object System.Windows.Forms.ToolStripMenuItem $statusItem.Text = "mIT-Viewer Legacy: aktywny" $statusItem.Enabled = $false [void]$menu.Items.Add($statusItem) $sendItem = New-Object System.Windows.Forms.ToolStripMenuItem $sendItem.Text = "Wyslij audit teraz" $sendItem.Add_Click({ try { Invoke-MitViewerAudit | Out-Null Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Audit wyslany do mIT-Desk." } catch { Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Nie udalo sie wyslac audytu. Sprawdz log." -Milliseconds 7000 } }) [void]$menu.Items.Add($sendItem) $updateItem = New-Object System.Windows.Forms.ToolStripMenuItem $updateItem.Text = "Wymus aktualizacje" $updateItem.Add_Click({ try { $result = Invoke-LegacySelfUpdate if ($result.updated) { Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Klient legacy zostal zaktualizowany." } else { Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Nie znaleziono nowszej wersji oprogramowania." } } catch { Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Aktualizacja nie powiodla sie. Sprawdz log." -Milliseconds 7000 Write-MitLog "Manual legacy update failed: $($_.Exception.Message)" } }) [void]$menu.Items.Add($updateItem) $logItem = New-Object System.Windows.Forms.ToolStripMenuItem $logItem.Text = "Otworz log" $logItem.Add_Click({ if (-not (Test-Path $LogFile)) { Write-MitLog "Log created from tray." } Start-Process notepad.exe $LogFile }) [void]$menu.Items.Add($logItem) $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem $exitItem.Text = "Zamknij ikone" $exitItem.Add_Click({ $script:NotifyIcon.Visible = $false [System.Windows.Forms.Application]::Exit() }) [void]$menu.Items.Add($exitItem) $script:NotifyIcon.ContextMenuStrip = $menu $script:NotifyIcon.Add_DoubleClick({ try { Invoke-MitViewerAudit | Out-Null; Show-MitBalloon -Title "mIT-Viewer Legacy" -Message "Audit wyslany." } catch {} }) $timer = New-Object System.Windows.Forms.Timer $timer.Interval = 5000 $timer.Add_Tick({ try { $payload = Get-BasePayload Process-MitCommands -DeviceUid $payload.device_uid -Mode "tray" } catch {} }) $timer.Start() Write-MitLog "mIT-Viewer Legacy tray started for $env:USERNAME" [System.Windows.Forms.Application]::Run() $script:NotifyIcon.Dispose() } function Start-MitViewerService { Write-MitLog "mIT-Viewer Legacy service loop started as $([Security.Principal.WindowsIdentity]::GetCurrent().Name)" $lastAudit = (Get-Date).AddYears(-1) while ($true) { try { $payload = $null if (((Get-Date) - $lastAudit).TotalHours -ge 6) { $payload = Invoke-MitViewerAudit $lastAudit = Get-Date } else { $payload = Invoke-MitViewerHeartbeat } Process-MitCommands -DeviceUid $payload.device_uid -Mode "service" } catch { Write-MitLog "Legacy service cycle failed: $($_.Exception.Message)" } Start-Sleep -Seconds 60 } } function Install-MitScheduledTask { $taskCommand = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$AgentScript`" -Service" $register = Get-Command Register-ScheduledTask -ErrorAction SilentlyContinue if ($register) { $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$AgentScript`" -Service" $triggerStartup = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest $settings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -StartWhenAvailable -AllowStartIfOnBatteries -ExecutionTimeLimit (New-TimeSpan -Seconds 0) -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $triggerStartup -Principal $principal -Settings $settings -Force | Out-Null } else { & schtasks.exe /Create /TN $TaskName /SC ONSTART /RU SYSTEM /RL HIGHEST /TR $taskCommand /F | Out-Null } } function Install-MitViewer { if (-not (Test-Path $InstallDir)) { New-Item -Path $InstallDir -ItemType Directory -Force | Out-Null } if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $currentScript = $MyInvocation.MyCommand.Path if (-not $currentScript) { throw "Installer must be saved as a .ps1 file before installation." } Copy-Item -Path $currentScript -Destination $AgentScript -Force Write-MitSettings Install-MitScheduledTask $trayCommand = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$AgentScript`" -Tray" New-Item -Path $RunKeyPath -Force | Out-Null Set-ItemProperty -Path $RunKeyPath -Name $RunKeyName -Value $trayCommand Write-MitLog "mIT-Viewer Legacy installed. Scheduled task: $TaskName" Write-MitLog "mIT-Viewer Legacy tray autostart registered in HKLM Run." } Read-MitSettings if (-not $ServerBaseUrl -or -not $ServerBaseUrl.Trim()) { throw "ServerBaseUrl is empty." } $ServerBaseUrl = $ServerBaseUrl.TrimEnd("/") if ($UpdateSelf) { Invoke-LegacySelfUpdate | Out-Null exit 0 } if ($Tray) { Start-MitViewerTray exit 0 } if ($Service) { Start-MitViewerService exit 0 } if ($RunOnce) { Invoke-MitViewerAudit | Out-Null exit 0 } if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { throw "Uruchom instalator jako administrator." } if (-not $ViewerToken) { try { $typedToken = Read-Host "Token Viewera (Enter = bez tokenu, trafi do zbiorczego katalogu Klienci)" if ($typedToken) { $ViewerToken = $typedToken.Trim() } } catch {} } Install-MitViewer if (-not $InstallOnly) { Invoke-MitViewerAudit | Out-Null }