From 46d53b8cc9309884595fe66908b7426830d0adc2 Mon Sep 17 00:00:00 2001 From: Jean-Pierre LESUEUR Date: Fri, 11 Feb 2022 14:31:17 +0100 Subject: [PATCH] 3.1 Stable (#23) * Code refactoring and improvement. * Desktop streaming improvement to gain few more FPS. * Support password-protected external x509 Certificates. --- .../PowerRemoteDesktop_Server.psd1 | Bin 8390 -> 8390 bytes .../PowerRemoteDesktop_Server.psm1 | 503 +++++++++++------- .../PowerRemoteDesktop_Viewer.psd1 | Bin 8460 -> 8460 bytes .../PowerRemoteDesktop_Viewer.psm1 | 260 +++++---- README.md | 20 +- TestServer.ps1 | 81 ++- TestViewer.ps1 | 126 ++++- 7 files changed, 699 insertions(+), 291 deletions(-) diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psd1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psd1 index 704fdfecc7524a74fdaa21882bbe79240b9e059d..4d96f64238cc53b31baa3d0fe03ce61d0370bae4 100644 GIT binary patch delta 14 VcmX@+c+7D_9uuSC=6t4bIRGq51o{8~ delta 14 VcmX@+c+7D_9uuR%=6t4bIRGp~1o;2} diff --git a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 index 317a4df..92ed706 100644 --- a/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 +++ b/PowerRemoteDesktop_Server/PowerRemoteDesktop_Server.psm1 @@ -72,18 +72,18 @@ Add-Type @" public static extern void CopyMemory( IntPtr dest, IntPtr src, - uint count + IntPtr count ); } public static class MSVCRT { [DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - public static extern int memcmp(IntPtr p1, IntPtr p2, UInt64 count); + public static extern IntPtr memcmp(IntPtr p1, IntPtr p2, IntPtr count); } "@ -$global:PowerRemoteDesktopVersion = "3.0.0" +$global:PowerRemoteDesktopVersion = "3.1.0" $global:HostSyncHash = [HashTable]::Synchronized(@{ host = $host @@ -262,10 +262,8 @@ function Invoke-PreventSleepMode #> $ES_AWAYMODE_REQUIRED = [uint32]"0x00000040" - $ES_CONTINUOUS = [uint32]"0x80000000" - $ES_DISPLAY_REQUIRED = [uint32]"0x00000002" - $ES_SYSTEM_REQUIRED = [uint32]"0x00000001" - $ES_USER_PRESENT = [uint32]"0x00000004" + $ES_CONTINUOUS = [uint32]"0x80000000" + $ES_SYSTEM_REQUIRED = [uint32]"0x00000001" return [Kernel32]::SetThreadExecutionState( $ES_CONTINUOUS -bor @@ -331,7 +329,9 @@ function Test-PasswordComplexity * At least of upper case character. .PARAMETER SecurePasswordCandidate - The password object to test + Type: SecureString + Default: None + Description: Secure String object containing the password to test. #> param ( [Parameter(Mandatory=$True)] @@ -392,29 +392,45 @@ function New-DefaultX509Certificate * base64 -i phrozen.p12 .PARAMETER X509_CN - Certificate Common Name. + Type: String + Default: PowerRemoteDesktop.Server + Description: Certificate Common Name. .PARAMETER X509_O - Certificate Organisation. + Type: String + Default: Phrozen + Description: Certificate Organisation. .PARAMETER X509_L - Certificate Locality (City) + Type: String + Default: Maisons Laffitte + Description: Certificate Locality (City) .PARAMETER X509_S - Certificate State. + Type: String + Default: Yvelines + Description: Certificate State. .PARAMETER X509_C - Certificate Company Name. + Type: String + Default: FR + Description: Certificate Company Name. .PARAMETER X509_OU - Certificate Organizational Unit. + Type: String + Default: Freeware + Description: Certificate Organizational Unit. .PARAMETER HashAlgorithmName - Certificate Hash Algorithm. - Example: SHA128, SHA256, SHA512... + Type: String + Default: SHA512 + Description: Certificate Hash Algorithm. + Example: SHA128, SHA256, SHA512... .PARAMETER CertExpirationInDays - Certificate Expiration in days. + Type: Integer + Default: 365 + Description: Certificate expiration in days. #> param ( [string] $X509_CN = "PowerRemoteDesktop.Server", @@ -621,7 +637,9 @@ function Get-X509CertificateFromStore certificate when starting a new Remote Desktop Server. .PARAMETER SubjectName - The certificate Subject Name to retrieve from local machine certificate store. + Type: String + Default: PowerRemoteDesktop.Server + Description: The certificate Subject Name to retrieve from local machine certificate store. .EXAMPLE Get-X509CertificateFromStore -SubjectName "PowerRemoteDesktop.Server" @@ -701,7 +719,9 @@ function Get-SHA512FromString Return the SHA512 value from string. .PARAMETER String - A String to hash. + Type: String + Default : None + Description: A String to hash. .EXAMPLE Get-SHA512FromString -String "Hello, World" @@ -760,11 +780,16 @@ function Resolve-AuthenticationChallenge the candidate to remote peer. .PARAMETER Password - Registered password string for server authentication. + Type: SecureString + Default: None + Description: Secure String object containing the password for resolving challenge. .PARAMETER Candidate - Random string used to solve the challenge. This string is public and is set across network by server. - Each time a new connection is requested to server, a new candidate is generated. + Type: String + Default: None + Description: + Random string used to solve the challenge. This string is public and is set across network by server. + Each time a new connection is requested to server, a new candidate is generated. .EXAMPLE Resolve-AuthenticationChallenge -Password "s3cr3t!" -Candidate "rKcjdh154@]=Ldc" @@ -789,7 +814,7 @@ function Resolve-AuthenticationChallenge $global:DesktopStreamScriptBlock = { $BlockSize = [int]$Param.SafeHash.ViewerConfiguration.BlockSize - $HighQualityResize = $Param.SafeHash.ViewerConfiguration.HighQualityResize + $FastResize = $Param.SafeHash.ViewerConfiguration.FastResize $packetSize = [int]$Param.SafeHash.ViewerConfiguration.PacketSize $WidthConstrainsts = 0 @@ -853,6 +878,9 @@ $global:DesktopStreamScriptBlock = { $bitmapPixelFormat ) + $graphics = [System.Drawing.Graphics]::FromImage($desktopImage) + $graphics.CompositingMode = [System.Drawing.Drawing2D.CompositingMode]::SourceCopy + if ($ResizeDesktop) { $fullSizeDesktop = New-Object System.Drawing.Bitmap( @@ -861,65 +889,37 @@ $global:DesktopStreamScriptBlock = { $bitmapPixelFormat ) - $fullSizeDesktopGraphics = [System.Drawing.Graphics]::FromImage($fullSizeDesktop) + $fullSizeDesktopGraphics = [System.Drawing.Graphics]::FromImage($fullSizeDesktop) - if ($HighQualityResize -and $ResizeDesktop) - { - $fullSizeDesktopGraphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality - $fullSizeDesktopGraphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality - $fullSizeDesktopGraphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic - $fullSizeDesktopGraphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality + if ($FastResize -and $ResizeDesktop) + { + $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::NearestNeighbor } - } + } # SizeOf(DWORD) * 3 (SizeOf(Desktop) + SizeOf(Left) + SizeOf(Top)) $struct = New-Object -TypeName byte[] -ArgumentList (([Runtime.InteropServices.Marshal]::SizeOf([System.Type][UInt32])) * 3) - $graphics = [System.Drawing.Graphics]::FromImage($desktopImage) - try - { - $stopWatch = [System.Diagnostics.Stopwatch]::StartNew() + $blockRect = New-Object -TypeName System.Drawing.Rectangle - while ($true) + $topLeftBlock = [System.Drawing.Point]::Empty + $bottomRightBlock = [System.Drawing.Point]::Empty + try + { + while ($Param.SafeHash.SessionActive) { - # Using a stopwatch instead of replacing main loop "while ($true)" by "while($this.SafeHash.SessionActive)" - # sounds strange but this is done to avoid locking our SafeHash to regularly and loosing some - # performance. If you think this is useless, just use while($this.SafeHash.SessionActive) in main - # loop instead of while($true). - if ($stopWatch.ElapsedMilliseconds -ge 2000) - { - if (-not $Param.SafeHash.SessionActive) - { - $stopWatch.Stop() - - break - } - - $stopWatch.Restart() - } - - # /// - - if ($firstIteration) - { - $updatedRect = $virtualScreenBounds - } - else - { - $updatedRect = New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, 0, 0 - } - if ($ResizeDesktop) { try - { + { $fullSizeDesktopGraphics.CopyFromScreen( $screen.Bounds.Location, [System.Drawing.Point]::Empty, [System.Drawing.Size]::New( $fullSizeDesktop.Width, $fullSizeDesktop.Height - ) + ), + [System.Drawing.CopyPixelOperation]::SourceCopy ) } catch @@ -945,7 +945,8 @@ $global:DesktopStreamScriptBlock = { [System.Drawing.Size]::New( $virtualScreenBounds.Width, $virtualScreenBounds.Height - ) + ), + [System.Drawing.CopyPixelOperation]::SourceCopy ) } catch @@ -954,72 +955,86 @@ $global:DesktopStreamScriptBlock = { } } + $updated = $false + for ($y = 0; $y -lt $vertBlockCount; $y++) { for ($x = 0; $x -lt $horzBlockCount; $x++) - { - $rect = New-Object -TypeName System.Drawing.Rectangle - - $rect.X = ($x * $BlockSize) - $rect.Y = ($y * $BlockSize) - $rect.Width = $BlockSize - $rect.Height = $BlockSize - - $rect = [System.Drawing.Rectangle]::Intersect($rect, $virtualScreenBounds) + { + $blockRect.X = ($x * $BlockSize) + $blockRect.Y = ($y * $BlockSize) + $blockRect.Width = $BlockSize + $blockRect.Height = $BlockSize + + if ( + # Intersecting consume some time, only intersect if required. + $blockRect.Right -gt $virtualScreenBounds.Width -or + $blockRect.Bottom -gt $virtualScreenBounds.Height + ) + { + $blockRect.Intersect($virtualScreenBounds) + } - $bmpBlock = $desktopImage.Clone($rect, $bitmapPixelFormat) + $bmpBlock = $desktopImage.Clone($blockRect, $bitmapPixelFormat) + + $blockRect.X = 0 + $blockRect.Y = 0 $bmpBlockData = $bmpBlock.LockBits( - [System.Drawing.Rectangle]::New(0, 0, $bmpBlock.Width, $bmpBlock.Height), - [System.Drawing.Imaging.ImageLockMode]::ReadOnly, + $blockRect, + [System.Drawing.Imaging.ImageLockMode]::WriteOnly, $bitmapPixelFormat ) try { $blockMemSize = ($bmpBlockData.Stride * $bmpBlock.Height) + $ptrBlockMemSize = [IntPtr]::New($blockMemSize) + if ($firstIteration) { # Big bang occurs, tangent univers is getting created, where is Donnie? $SpaceGrid[$y][$x] = [Runtime.InteropServices.Marshal]::AllocHGlobal($blockMemSize) - [Kernel32]::CopyMemory($SpaceGrid[$y][$x], $bmpBlockData.Scan0, $blockMemSize) + [Kernel32]::CopyMemory($SpaceGrid[$y][$x], $bmpBlockData.Scan0, $ptrBlockMemSize) } else - { - if ([MSVCRT]::memcmp($bmpBlockData.Scan0, $SpaceGrid[$y][$x], $blockMemSize) -ne 0) + { + if ([MSVCRT]::memcmp($bmpBlockData.Scan0, $SpaceGrid[$y][$x], $ptrBlockMemSize) -ne [IntPtr]::Zero) { - [Kernel32]::CopyMemory($SpaceGrid[$y][$x], $bmpBlockData.Scan0, $blockMemSize) + [Kernel32]::CopyMemory($SpaceGrid[$y][$x], $bmpBlockData.Scan0, $ptrBlockMemSize) + + if (-not $updated) + { + # Initialize with the first dirty block coordinates + $topLeftBlock.X = $x + $topLeftBlock.Y = $y - if ($updatedRect.IsEmpty) - { - $updatedRect.X = $x * $BlockSize - $updatedRect.Width = $BlockSize + $bottomRightBlock = $topLeftBlock - $updatedRect.Y = $y * $BlockSize - $updatedRect.Height = $BlockSize + $updated = $true } else { - if ($x * $BlockSize -lt $updatedRect.X) + if ($x -lt $topLeftBlock.X) { - $updatedRect.X = $x * $BlockSize + $topLeftBlock.X = $x } - if (($x+1) * $BlockSize -gt $updatedRect.Right) + if ($y -lt $topLeftBlock.Y) { - $updatedRect.Width = (($x + 1) * $BlockSize) - $updatedRect.X + $topLeftBlock.Y = $y } - if ($y * $BlockSize -lt $updatedRect.Y) + if ($x -gt $bottomRightBlock.X) { - $updatedRect.Y = $y * $BlockSize + $bottomRightBlock.X = $x } - if (($y+1) * $BlockSize -gt $updatedRect.Bottom) + if ($y -gt $bottomRightBlock.Y) { - $updatedRect.Height = (($y + 1) * $BlockSize) - $updatedRect.Y - } - } + $bottomRightBlock.Y = $y + } + } } } } @@ -1031,8 +1046,23 @@ $global:DesktopStreamScriptBlock = { } } } - } + } + + if ($firstIteration) + { + # Send the full desktop if we are in the first iteration + $updatedRect = $virtualScreenBounds + } + elseif ($updated) + { + # Create new updated rectangle pointing to the dirty region (since last snapshot) + $updatedRect.X = $topLeftBlock.X * $BlockSize + $updatedRect.Y = $topLeftBlock.Y * $BlockSize + $updatedRect.Width = (($bottomRightBlock.X * $BlockSize) + $BlockSize) - $updatedRect.Left + $updatedRect.Height = (($bottomRightBlock.Y * $BlockSize) + $BlockSize) - $updatedRect.Top + } + if (-not $updatedRect.IsEmpty -and $desktopImage) { try @@ -1133,7 +1163,7 @@ $global:DesktopStreamScriptBlock = { } } -$global:IngressEventScriptBlock = { +$global:IngressEventScriptBlock = { enum MouseFlags { MOUSEEVENTF_ABSOLUTE = 0x8000 MOUSEEVENTF_LEFTDOWN = 0x0002 @@ -1191,9 +1221,9 @@ $global:IngressEventScriptBlock = { 0 ); - } + } - while ($Param.SafeHash.SessionActive) + while ($true) { try { @@ -1458,10 +1488,14 @@ $global:EgressEventScriptBlock = { Send an event to remote peer. .PARAMETER AEvent - Define what kind of event to send. + Type: Enum + Default: None + Description: The event to send to remote viewer. .PARAMETER Data - An optional object containing additional information about the event. + Type: PSCustomObject + Default: None + Description: Additional information about the event. #> param ( [Parameter(Mandatory=$True)] @@ -1499,7 +1533,7 @@ $global:EgressEventScriptBlock = { $stopWatch = [System.Diagnostics.Stopwatch]::StartNew() - while ($Param.SafeHash.SessionActive) + while ($true) { # Events that occurs every seconds needs to be placed bellow. # If no event has occured during this second we send a Keep-Alive signal to @@ -1546,7 +1580,7 @@ $global:EgressEventScriptBlock = { } # Monitor for global mouse cursor change - # Update Frequently (Maximum probe time to be efficient: 30ms) + # Update Frequently (Maximum probe time to be efficient: 50ms) $currentCursor = Get-GlobalMouseCursorIconHandle if ($currentCursor -ne 0 -and $currentCursor -ne $oldCursor) { @@ -1562,7 +1596,7 @@ $global:EgressEventScriptBlock = { $oldCursor = $currentCursor } - Start-Sleep -Milliseconds 30 + Start-Sleep -Milliseconds 50 } $stopWatch.Stop() @@ -1579,10 +1613,14 @@ function New-RunSpace Terminal. .PARAMETER ScriptBlock - A PowerShell block of code to be evaluated on the new Runspace. + Type: ScriptBlock + Default: None + Description: Instructions to execute in new runspace. .PARAMETER Param - Optional extra parameters to be attached to Runspace. + Type: PSCustomObject + Default: None + Description: Object to attach in runspace context. .EXAMPLE New-RunSpace -Client $newClient -ScriptBlock { Start-Sleep -Seconds 10 } @@ -1596,7 +1634,7 @@ function New-RunSpace ) $runspace = [RunspaceFactory]::CreateRunspace() - $runspace.ThreadOptions = "ReuseThread" + $runspace.ThreadOptions = "UseNewThread" $runspace.ApartmentState = "STA" $runspace.Open() @@ -1620,7 +1658,6 @@ function New-RunSpace } } - class ClientIO { [System.Net.Sockets.TcpClient] $Client = $null [System.IO.StreamWriter] $Writer = $null @@ -1685,10 +1722,12 @@ class ClientIO { Handle authentication process with remote peer. .PARAMETER Password - Password used to validate challenge and grant access for a new Client. + Type: SecureString + Default: None + Description: Secure String object containing the password. .EXAMPLE - .Authentify("s3cr3t!") + .Authentify((ConvertTo-SecureString -String "urCompl3xP@ssw0rd" -AsPlainText -Force)) #> try { @@ -1761,7 +1800,8 @@ class ClientIO { Read string message from remote peer with timeout support. .PARAMETER Timeout - Define the maximum time (in milliseconds) to wait for remote peer message. + Type: Integer + Description: Maximum period of time to wait for incomming data. #> $defautTimeout = $this.SSLStream.ReadTimeout try @@ -1793,7 +1833,8 @@ class ClientIO { peer. .PARAMETER Object - A PowerShell Object to be serialized as JSON String. + Type: PSCustomObject + Description: Object to be serialized in JSON. #> $this.Writer.WriteLine(($Object | ConvertTo-Json -Compress)) @@ -1811,7 +1852,8 @@ class ClientIO { Read json string from remote peer and attempt to deserialize as a PowerShell Object. .PARAMETER Timeout - Define the maximum time (in milliseconds) to wait for remote peer message. + Type: Integer + Description: Maximum period of time to wait for reading data. #> return ($this.ReadLine($Timeout) | ConvertFrom-Json) } @@ -1906,14 +1948,16 @@ class ServerIO { Accept new client and associate this client with a new ClientIO Object. .PARAMETER Timeout - By default AcceptTcpClient() will block current thread until a client connects. - - Using Timeout and a cool technique, you can stop waiting for client after a certain amount - of time (In Milliseconds) + Type: Integer + Description: + By default AcceptTcpClient() will block current thread until a client connects. + + Using Timeout and a cool technique, you can stop waiting for client after a certain amount + of time (In Milliseconds) - If Timeout is greater than 0 (Milliseconds) then connection timeout is enabled. + If Timeout is greater than 0 (Milliseconds) then connection timeout is enabled. - Other method: AsyncWaitHandle.WaitOne([timespan])'h:m:s') -eq $true|$false with BeginAcceptTcpClient(...) + Other method: AsyncWaitHandle.WaitOne([timespan])'h:m:s') -eq $true|$false with BeginAcceptTcpClient(...) #> if (-not (Test-PasswordComplexity -SecurePasswordCandidate $SecurePassword)) @@ -1999,7 +2043,7 @@ class ViewerConfiguration { [int] $ImageCompressionQuality = 100 [PacketSize] $PacketSize = [PacketSize]::Size9216 [BlockSize] $BlockSize = [BlockSize]::Size64 - [bool] $HighQualityResize = $false + [bool] $FastResize = $false [bool] ResizeDesktop() { @@ -2059,7 +2103,8 @@ class ServerSession { Compare two session object. In this case just compare session id string. .PARAMETER Id - A session id to compare with current session object. + Type: String + Description: A session id to compare with current session object. #> return ($this.Id -ceq $Id) } @@ -2071,7 +2116,8 @@ class ServerSession { Create a new desktop streaming worker (Runspace/Thread). .PARAMETER Client - An established connection with remote peer as a ClientIO Object. + Type: ClientIO + Description: Established connection with a remote peer. #> $param = New-Object -TypeName PSCustomObject -Property @{ Client = $Client @@ -2092,7 +2138,8 @@ class ServerSession { Create a new egress / ingress worker (Runspace/Thread) to process outgoing / incomming events. .PARAMETER Client - An established connection with remote peer as a ClientIO Object. + Type: ClientIO + Description: Established connection with a remote peer. #> $param = New-Object -TypeName PSCustomObject -Property @{ @@ -2316,7 +2363,8 @@ class SessionManager { Find a session by its id on current session pool. .PARAMETER SessionId - Session id to search in current pool. + Type: String + Description: SessionId to retrieve from session pool. #> foreach ($session in $this.Sessions) { @@ -2346,8 +2394,7 @@ class SessionManager { about current server marchine state then wait for viewer acknowledgement with desired configuration (Ex: desired screen to capture, quality and local size constraints). - When session creation is done, client is then closed. - + When session creation is done, client is then closed. #> try { @@ -2425,9 +2472,9 @@ class SessionManager { $session.SafeHash.ViewerConfiguration.BlockSize = [BlockSize]$viewerExpectation.BlockSize } - if ($viewerExpectation.PSobject.Properties.name -contains "HighQualityResize") + if ($viewerExpectation.PSobject.Properties.name -contains "FastResize") { - $session.SafeHash.ViewerConfiguration.HighQualityResize = $viewerExpectation.HighQualityResize + $session.SafeHash.ViewerConfiguration.FastResize = $viewerExpectation.FastResize } Write-Verbose "New session successfully created." @@ -2468,7 +2515,7 @@ class SessionManager { { $Client.WriteLine(([ProtocolCommand]::ResourceNotFound)) - throw "Session object matchin given id could not be find in active session pool." + throw "Could not locate session." } Write-Verbose "Client successfully attached to session: ""$($session.id)""" @@ -2496,7 +2543,11 @@ class SessionManager { } [void] ListenForWorkers() - { + { + <# + .SYNOPSIS + Process server client queue and dispatch accordingly. + #> while ($true) { if (-not $this.Server -or -not $this.Server.Active()) @@ -2505,9 +2556,7 @@ class SessionManager { } try - { - # It is important to check regularly for dead sessions to let the garbage collector do his job - # and avoid dead threads (most of the time desktop streaming threads). + { $this.CheckSessionsIntegrity() } catch @@ -2569,6 +2618,11 @@ class SessionManager { [void] CheckSessionsIntegrity() { + <# + .SYNOPSIS + Check if existing server sessions integrity is respected. + Use this method to free dead/half-dead sessions. + #> foreach ($session in $this.Sessions) { $session.CheckSessionIntegrity() @@ -2579,7 +2633,7 @@ class SessionManager { { <# .SYNOPSIS - Close all existing sessions + Terminate existing server sessions. #> foreach ($session in $this.Sessions) @@ -2594,7 +2648,7 @@ class SessionManager { { <# .SYNOPSIS - Close all existing sessions and dispose server. + Terminate existing server sessions then release server. #> $this.CloseSessions() @@ -2612,7 +2666,7 @@ function Test-Administrator { <# .SYNOPSIS - Return true if current PowerShell is running with Administrator privilege, otherwise return false. + Check if current user is administrator. #> $windowsPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent() @@ -2623,84 +2677,146 @@ function Test-Administrator ) } +class ValidateFileAttribute : System.Management.Automation.ValidateArgumentsAttribute +{ + <# + .SYNOPSIS + Check if file argument exists on disk. + #> + + [void]Validate([System.Object] $arguments, [System.Management.Automation.EngineIntrinsics] $engineIntrinsics) + { + if(-not (Test-Path -Path $arguments)) + { + throw [System.IO.FileNotFoundException]::new() + } + } +} + +class ValidateBase64StringAttribute : System.Management.Automation.ValidateArgumentsAttribute +{ + <# + .SYNOPSIS + Check if string argument is a valid Base64 String. + #> + + [void]Validate([System.Object] $arguments, [System.Management.Automation.EngineIntrinsics] $engineIntrinsics) + { + [Convert]::FromBase64String($arguments) + } +} + function Invoke-RemoteDesktopServer { <# .SYNOPSIS - Start a new Remote Desktop Server. + Create and start a new PowerRemoteDesktop Server. .DESCRIPTION - Notice: Certificate options are evaluated in this order. - 1) CertificateFile. - 2) EncodedCertificate. + Notices: + + 1- Prefer using SecurePassword over plain-text password even if a plain-text password is getting converted to SecureString anyway. + + 2- Not specifying a custom certificate using CertificateFile or EncodedCertificate result in generating a default + self-signed certificate (if not already generated) that will get installed on local machine thus requiring administrator privilege. + If you want to run the server as a non-privileged account, specify your own certificate location. + + 3- If you don't specify a SecurePassword or Password, a random complex password will be generated and displayed on terminal + (this password is temporary) .PARAMETER ListenAddress - Define in which interface to listen for new viewer. + Type: String + Default: 0.0.0.0 + Description: IP Address that represents the local IP address. .PARAMETER ListenPort - Define in which port to listen for new viewer. + Type: Integer + Default: 2801 (0 - 65535) + Description: The port on which to listen for incoming connection. .PARAMETER SecurePassword - SecureString Password object used by remote viewer to authenticate with server (Recommended) - - Call "ConvertTo-SecureString -String "YouPasswordHere" -AsPlainText -Force" on this parameter to convert - a plain-text String to SecureString. + Type: SecureString + Default: None + Description: SecureString object containing password used to authenticate remote viewer (Recommended) .PARAMETER Password - Plain-Text Password used by remote viewer to authenticate with server (Not recommended, use SecurePassword instead) - - If no password is specified, then a random complex password will be generated - and printed on terminal. + Type: String + Default: None + Description: Plain-Text Password used to authenticate remote viewer (Not recommended, use SecurePassword instead) .PARAMETER CertificateFile - A valid X509 Certificate (With Private Key) File. If set, this parameter is prioritize. - + Type: String + Default: None + Description: A file containing valid certificate information (x509), must include the private key. + .PARAMETER EncodedCertificate - A valid X509 Certificate (With Private Key) encoded as a Base64 String. + Type: String (Base64 Encoded) + Default: None + Description: A base64 representation of the whole certificate file, must include the private key. .PARAMETER UseTLSv1_3 - Define whether or not TLS v1.3 must be used for communication with Viewer. + Type: Switch + Default: False + Description: If present, TLS v1.3 will be used instead of TLS v1.2 (Recommended if applicable to both systems) .PARAMETER DisableVerbosity - Disable verbosity (not recommended) + Type: Switch + Default: False + Description: If present, program wont show verbosity messages. .PARAMETER Clipboard - Define clipboard synchronization rules: - - "Disabled": Completely disable clipboard synchronization. - - "Receive": Update local clipboard with remote clipboard only. - - "Send": Send local clipboard to remote peer. - - "Both": Clipboards are fully synchronized between Viewer and Server. + Type: Enum + Default: Both + Description: + Define clipboard synchronization mode (Both, Disabled, Send, Receive) see bellow for more detail. + + * Disabled -> Clipboard synchronization is disabled in both side + * Receive -> Only incomming clipboard is allowed + * Send -> Only outgoing clipboard is allowed + * Both -> Clipboard synchronization is allowed on both side .PARAMETER ViewOnly (Default: None) - If this switch is present, viewer wont be able to take the control of mouse (moves, clicks, wheel) and keyboard. - Useful for view session only. + Type: Swtich + Default: False + Description: If present, remote viewer is only allowed to view the desktop (Mouse and Keyboard are not authorized) .PARAMETER PreventComputerToSleep Type: Switch + Default: False + Description: If present, this option will prevent computer to enter in sleep mode while server is active and waiting for new connections. + + .PARAMETER CertificatePassword + Type: SecureString Default: None - Description: - If present, this option will prevent computer to enter in sleep mode while server is active and waiting for new connections. + Description: Specify the password used to open a password-protected x509 Certificate provided by user. + + .EXAMPLE + Invoke-RemoteDesktopServer -ListenAddress "0.0.0.0" -ListenPort 2801 -SecurePassword (ConvertTo-SecureString -String "urCompl3xP@ssw0rd" -AsPlainText -Force) + Invoke-RemoteDesktopServer -ListenAddress "0.0.0.0" -ListenPort 2801 -SecurePassword (ConvertTo-SecureString -String "urCompl3xP@ssw0rd" -AsPlainText -Force) -CertificateFile "c:\certs\phrozen.p12" #> param ( [string] $ListenAddress = "0.0.0.0", [ValidateRange(0, 65535)] - [int] $ListenPort = 2801, + [int] $ListenPort = 2801, - [SecureString] $SecurePassword, - [string] $Password = "", + [SecureString] $SecurePassword = $null, + [string] $Password = "", + + [ValidateFile()] + [String] $CertificateFile = $null, - [string] $CertificateFile = "", # 1 - # Or - [string] $EncodedCertificate = "", # 2 + [ValidateBase64String()] + [string] $EncodedCertificate = "", [switch] $UseTLSv1_3, [switch] $DisableVerbosity, [ClipboardMode] $Clipboard = [ClipboardMode]::Both, [switch] $ViewOnly, - [switch] $PreventComputerToSleep - ) + [switch] $PreventComputerToSleep, + [SecureString] $CertificatePassword = $null + ) $oldErrorActionPreference = $ErrorActionPreference $oldVerbosePreference = $VerbosePreference @@ -2731,17 +2847,44 @@ function Invoke-RemoteDesktopServer $Certificate = $null - if (($CertificateFile -and (Test-Path -Path $CertificateFile)) -or $EncodedCertificate) + if ($CertificateFile -or $EncodedCertificate) { $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - if ($CertificateFile) + try { - $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CertificateFile + if ($CertificateFile) + { + $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CertificateFile, $CertificatePassword + } + else + { + $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 ([Convert]::FromBase64String($EncodedCertificate)), $CertificatePassword + } } - else + catch + { + $message = "Could not open provided x509 Certificate. Possible Reasons:`r`n" + + "* Provided certificate is not a valid x509 Certificate.`r`n" + + "* Certificate is corrupted.`r`n" + + if (-not $CertificatePassword) + { + $message += "* Certificate is protected by a password.`r`n" + } + else + { + $message += "* Provided certificate password is not valid.`r`n" + } + + $message += "More detail: $($_)" + + throw $message + } + + if (-not $Certificate.HasPrivateKey) { - $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 @(, [Convert]::FromBase64String($EncodedCertificate)) + throw "Provided Certificate must have private-key included." } } diff --git a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psd1 b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psd1 index 3d5402e729fe2d247c020bda49b7a4d75197545d..ec21e57cb1570a87a5366962535d8557a91dcf7a 100644 GIT binary patch delta 14 VcmeBi>T%kT$HZv3IiG2+8~`T@1kL~e delta 14 VcmeBi>T%kT$HZu`IiG2+8~`T-1kC^d diff --git a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 index 0aac480..bb2e836 100644 --- a/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 +++ b/PowerRemoteDesktop_Viewer/PowerRemoteDesktop_Viewer.psm1 @@ -51,17 +51,15 @@ Add-Type @" } "@ -$global:PowerRemoteDesktopVersion = "3.0.0" +$global:PowerRemoteDesktopVersion = "3.1.0" $global:HostSyncHash = [HashTable]::Synchronized(@{ host = $host ClipboardText = (Get-Clipboard -Raw) }) -# Last until PowerShell session is closed $global:EphemeralTrustedServers = @() -# Local storage definitions $global:LocalStoragePath = "HKCU:\SOFTWARE\PowerRemoteDesktop_Viewer" $global:LocalStoragePath_TrustedServers = -join($global:LocalStoragePath, "\TrustedServers") @@ -207,7 +205,9 @@ function Write-ServerFingerprintToLocalStorage Write a trusted server certificate fingerprint to our local storage. .PARAMETER Fingerprint - The server certificate fingerprint to store. + Type: String + Default: None + Description: Fingerprint to store in local storage. #> param ( [Parameter(Mandatory=$True)] @@ -231,7 +231,9 @@ function Remove-TrustedServer Remove trusted server from local storage. .PARAMETER Fingerprint - Server certificate to remove from trusted server list. + Type: String + Default: None + Description: Fingerprint to remove from local storage. #> param ( [Parameter(Mandatory=$True)] @@ -312,7 +314,9 @@ function Test-ServerFingerprintFromLocalStorage Check if a server certificate fingerprint was saved to local storage. .PARAMETER Fingerprint - The server certificate fingerprint to check. + Type: String + Default: None + Description: Fingerprint to check in local storage. #> param ( [Parameter(Mandatory=$True)] @@ -329,7 +333,9 @@ function Get-SHA512FromString Return the SHA512 value from string. .PARAMETER String - A String to hash. + Type: String + Default : None + Description: A String to hash. .EXAMPLE Get-SHA512FromString -String "Hello, World" @@ -349,16 +355,25 @@ function Resolve-AuthenticationChallenge <# .SYNOPSIS Algorithm to solve the server challenge during password authentication. + + .DESCRIPTION + Server needs to resolve the challenge and keep the solution in memory before sending + the candidate to remote peer. - .PARAMETER SecurePassword - Registered password string for server authentication. + .PARAMETER Password + Type: SecureString + Default: None + Description: Secure String object containing the password for resolving challenge. .PARAMETER Candidate - Random string used to solve the challenge. This string is public and is set across network by server. - Each time a new connection is requested to server, a new candidate is generated. + Type: String + Default: None + Description: + Random string used to solve the challenge. This string is public and is set across network by server. + Each time a new connection is requested to server, a new candidate is generated. .EXAMPLE - Resolve-AuthenticationChallenge -SecurePassword "s3cr3t!" -Candidate "rKcjdh154@]=Ldc" + Resolve-AuthenticationChallenge -Password "s3cr3t!" -Candidate "rKcjdh154@]=Ldc" #> param ( [Parameter(Mandatory=$True)] @@ -398,19 +413,6 @@ class ClientIO { [System.IO.BinaryReader] $BinaryReader = $null ClientIO( - <# - .SYNOPSIS - Class constructor. - - .PARAMETER RemoteAddress - IP/HOST of remote server. - - .PARAMETER RemotePort - Remote server port. - - .PARAMETER UseTLSv1_3 - Define whether or not SSL/TLS v1.3 must be used. - #> [string] $RemoteAddress = "127.0.0.1", [int] $RemotePort = 2801, [bool] $UseTLSv1_3 = $false @@ -562,13 +564,15 @@ class ClientIO { [void]Authentify([SecureString] $SecurePassword) { <# .SYNOPSIS - Handle authentication process with remote server. + Handle authentication process with remote peer. .PARAMETER Password - Password used for authentication with server. + Type: SecureString + Default: None + Description: Secure String object containing the password. .EXAMPLE - .Authentify("s3cr3t!") + .Authentify((ConvertTo-SecureString -String "urCompl3xP@ssw0rd" -AsPlainText -Force)) #> Write-Verbose "Authentify with remote server (Challenged-Based Authentication)..." @@ -619,7 +623,8 @@ class ClientIO { Read string message from remote peer with timeout support. .PARAMETER Timeout - Define the maximum time (in milliseconds) to wait for remote peer message. + Type: Integer + Description: Maximum period of time to wait for incomming data. #> $defautTimeout = $this.SSLStream.ReadTimeout try @@ -651,7 +656,8 @@ class ClientIO { peer. .PARAMETER Object - A PowerShell Object to be serialized as JSON String. + Type: PSCustomObject + Description: Object to be serialized in JSON. #> $this.Writer.WriteLine(($Object | ConvertTo-Json -Compress)) @@ -669,7 +675,8 @@ class ClientIO { Read json string from remote peer and attempt to deserialize as a PowerShell Object. .PARAMETER Timeout - Define the maximum time (in milliseconds) to wait for remote peer message. + Type: Integer + Description: Maximum period of time to wait for incomming data. #> return ($this.ReadLine($Timeout) | ConvertFrom-Json) } @@ -739,7 +746,7 @@ class ViewerSession [int] $ResizeRatio = 0 [PacketSize] $PacketSize = [PacketSize]::Size9216 [BlockSize] $BlockSize = [BlockSize]::Size64 - [bool] $HighQualityResize = $false + [bool] $FastResize = $false [ClientIO] $ClientDesktop = $null [ClientIO] $ClientEvents = $null @@ -939,7 +946,7 @@ class ViewerSession ImageCompressionQuality = $this.ImageCompressionQuality PacketSize = $this.PacketSize BlockSize = $this.BlockSize - HighQualityResize = $this.HighQualityResize + FastResize = $this.FastResize } if ($this.ViewerConfiguration.RequireResize) @@ -1103,6 +1110,8 @@ $global:VirtualDesktopUpdaterScriptBlock = { $scene = $null $sceneGraphics = $null + $destPoint = [System.Drawing.Point]::New(0, 0) + while ($true) { try @@ -1110,8 +1119,8 @@ $global:VirtualDesktopUpdaterScriptBlock = { $null = $Param.Client.SSLStream.Read($struct, 0, $struct.Length) $totalBufferSize = [System.Runtime.InteropServices.Marshal]::ReadInt32($struct, 0x0) - $rectLeft = [System.Runtime.InteropServices.Marshal]::ReadInt32($struct, 0x4) - $rectTop = [System.Runtime.InteropServices.Marshal]::ReadInt32($struct, 0x8) + $destPoint.X = [System.Runtime.InteropServices.Marshal]::ReadInt32($struct, 0x4) + $destPoint.Y = [System.Runtime.InteropServices.Marshal]::ReadInt32($struct, 0x8) $stream.SetLength($totalBufferSize) @@ -1125,38 +1134,31 @@ $global:VirtualDesktopUpdaterScriptBlock = { } $null = $stream.Write($Param.Client.BinaryReader.ReadBytes($bufferSize), 0, $bufferSize) - } until ($stream.Position -eq $stream.Length) - - $stream.Position = 0 + } until ($stream.Position -eq $stream.Length) if ($stream.Length -eq 0) { continue } - + if (-not $scene) { # First Iteration $scene = [System.Drawing.Image]::FromStream($stream) - $sceneGraphics = [System.Drawing.Graphics]::FromImage($scene) + + $sceneGraphics = [System.Drawing.Graphics]::FromImage($scene) + $sceneGraphics.CompositingMode = [System.Drawing.Drawing2D.CompositingMode]::SourceCopy $Param.VirtualDesktopSyncHash.VirtualDesktop.Picture.Image = $scene } else - { + { # Next Iterations - $sceneChunk = [System.Drawing.Image]::FromStream($stream) - $sceneGraphics.DrawImage( - $sceneChunk, - [System.Drawing.Point]::New( - $rectLeft, - $rectTop - ) + [System.Drawing.Image]::FromStream($stream), + $destPoint ) - $sceneChunk.Dispose() - $Param.VirtualDesktopSyncHash.VirtualDesktop.Picture.Invalidate() } } @@ -1445,13 +1447,19 @@ function New-VirtualDesktopForm It returns a PowerShell object containing both Form and PaintBox. .PARAMETER Width - Width of new form. + Type: Integer + Default: 1200 + Description: The pre-defined width of new form .PARAMETER Height - Height of new form. + Type: Integer + Default: 800 + Description: The pre-defined height of new form .PARAMETER Caption - Caption of new form. + Type: String + Default: PowerRemoteDesktop Viewer + Description: The pre-defined caption of new form. .EXAMPLE New-VirtualDesktopForm -Caption "New Desktop Form" -Width 1200 -Height 800 @@ -1470,7 +1478,7 @@ function New-VirtualDesktopForm $form.Text = $Caption $form.KeyPreview = $true # Necessary to capture keystrokes. $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle - $form.MaximizeBox = $false + $form.MaximizeBox = $false $pictureBox = New-Object System.Windows.Forms.PictureBox $pictureBox.Dock = [System.Windows.Forms.DockStyle]::Fill @@ -1494,10 +1502,14 @@ function New-RunSpace Terminal. .PARAMETER ScriptBlock - A PowerShell block of code to be evaluated on the new Runspace. + Type: ScriptBlock + Default: None + Description: Instructions to execute in new runspace. .PARAMETER Param - Optional extra parameters to be attached to Runspace. + Type: PSCustomObject + Default: None + Description: Object to attach in runspace context. .EXAMPLE New-RunSpace -Client $newClient -ScriptBlock { Start-Sleep -Seconds 10 } @@ -1539,52 +1551,71 @@ function Invoke-RemoteDesktopViewer { <# .SYNOPSIS - Open a new Remote Desktop Session to remote Server. + Open a new remote desktop session with a remote server. + + .DESCRIPTION + Notice: Prefer using SecurePassword over plain-text password even if a plain-text password is getting converted to SecureString anyway. .PARAMETER ServerAddress - Remote Server Address. + Type: String + Default: 127.0.0.1 + Description: Remote server host/address. .PARAMETER ServerPort - Remote Server Port. + Type: Integer + Default: 2801 (0 - 65535) + Description: Remote server port. .PARAMETER SecurePassword - SecureString Password object used to authenticate with remote server (Recommended) - - Call "ConvertTo-SecureString -String "YouPasswordHere" -AsPlainText -Force" on this parameter to convert - a plain-text String to SecureString. - - See example section. + Type: SecureString + Default: None + Description: SecureString object containing password used to authenticate with remote server (Recommended) .PARAMETER Password - Plain-Text Password used to authenticate with remote server (Not recommended, use SecurePassword instead) + Type: String + Default: None + Description: Plain-Text Password used to authenticate with remote server (Not recommended, use SecurePassword instead) .PARAMETER UseTLSv1_3 - Define whether or not client must use SSL/TLS v1.3 to communicate with remote server. - Recommended if possible. + Type: Switch + Default: False + Description: If present, TLS v1.3 will be used instead of TLS v1.2 (Recommended if applicable to both systems) .PARAMETER DisableVerbosity - Disable verbosity (not recommended) + Type: Boolean + Default: False + Description: If present, program wont show verbosity messages. .PARAMETER Clipboard - Define clipboard synchronization rules: - - "Disabled": Completely disable clipboard synchronization. - - "Receive": Update local clipboard with remote clipboard only. - - "Send": Send local clipboard to remote peer. - - "Both": Clipboards are fully synchronized between Viewer and Server. + Type: Enum + Default: Both + Description: + Define clipboard synchronization mode (Both, Disabled, Send, Receive) see bellow for more detail. + + * Disabled -> Clipboard synchronization is disabled in both side + * Receive -> Only incomming clipboard is allowed + * Send -> Only outgoing clipboard is allowed + * Both -> Clipboard synchronization is allowed on both side .PARAMETER ImageCompressionQuality - JPEG Compression level from 0 to 100 - 0 = Lowest quality. - 100 = Highest quality. + Type: Integer (0 - 100) + Default: 75 + Description: JPEG Compression level from 0 to 100. 0 = Lowest quality, 100 = Highest quality. .PARAMETER Resize - If this switch is present, remote desktop resize will be forced according ResizeRatio option value. + Type: Switch + Default: None + Description: If present, remote desktop will get resized accordingly with ResizeRatio option. .PARAMETER ResizeRatio - Define the resize ratio to apply to remote desktop (30 to 99) + Type: Integer (30 - 99) + Default: None + Description: Used with Resize option, define the resize ratio in percentage. .PARAMETER AlwaysOnTop - If this switch is present, virtual desktop form will be above all other windows. + Type: Switch + Default: False + Description: If present, virtual desktop form will be above all other window's. .PARAMETER BlockSize Type: Enum @@ -1594,6 +1625,14 @@ function Invoke-RemoteDesktopViewer (Advanced) Define the screen grid block size. Choose the block size accordingly to remote screen size / computer constrainsts (CPU / Network) + Size1024 -> 1024 Bytes (1KiB) + Size2048 -> 2048 Bytes (2KiB) + Size4096 -> 4096 Bytes (4KiB) + Size8192 -> 8192 Bytes (8KiB) + Size9216 -> 9216 Bytes (9KiB) + Size12288 -> 12288 Bytes (12KiB) + Size16384 -> 16384 Bytes (16KiB) + .PARAMETER PacketSize Type: Enum Values: Size1024, Size2048, Size4096, Size8192, Size9216, Size12288, Size16384 @@ -1602,12 +1641,19 @@ function Invoke-RemoteDesktopViewer (Advanced) Define the network packet size for streams. Choose the packet size accordingly to your network constrainsts. - .PARAMETER HighQualityResize + Size32 -> 32x32 + Size64 -> 64x64 + Size96 -> 96x96 + Size128 -> 128x128 + Size256 -> 256x256 + Size512 -> 512x512 + + .PARAMETER FastResize Type: Switch Default: None Description: Control the quality of remote desktop resize (smoothing) if applicable. - If you lack of network speed, this option is not recommended. + If you lack of network speed, this option is recommended. .EXAMPLE Invoke-RemoteDesktopViewer -ServerAddress "192.168.0.10" -ServerPort "2801" -SecurePassword (ConvertTo-SecureString -String "s3cr3t!" -AsPlainText -Force) @@ -1621,16 +1667,14 @@ function Invoke-RemoteDesktopViewer [ValidateRange(0, 65535)] [int] $ServerPort = 2801, - [switch] $UseTLSv1_3, - + [switch] $UseTLSv1_3, [SecureString] $SecurePassword, [String] $Password, - [switch] $DisableVerbosity, [ClipboardMode] $Clipboard = [ClipboardMode]::Both, [ValidateRange(0, 100)] - [int] $ImageCompressionQuality = 100, + [int] $ImageCompressionQuality = 75, [switch] $Resize, @@ -1640,7 +1684,7 @@ function Invoke-RemoteDesktopViewer [switch] $AlwaysOnTop, [PacketSize] $PacketSize = [PacketSize]::Size9216, [BlockSize] $BlockSize = [BlockSize]::Size64, - [switch] $HighQualityResize = $false + [switch] $FastResize = $false ) [System.Collections.Generic.List[PSCustomObject]]$runspaces = @() @@ -1689,7 +1733,7 @@ function Invoke-RemoteDesktopViewer $session.ImageCompressionQuality = $ImageCompressionQuality $session.PacketSize = $PacketSize $session.BlockSize = $BlockSize - $session.HighQualityResize = $HighQualityResize + $session.FastResize = $FastResize if ($Resize) { @@ -1754,16 +1798,24 @@ function Invoke-RemoteDesktopViewer This event is used to simulate mouse move and clicks. .PARAMETER X - The position of mouse in horizontal axis. + Type: Integer + Default: None + Description: The position of mouse in horizontal axis. .PARAMETER Y - The position of mouse in vertical axis. + Type: Integer + Default: None + Description: The position of mouse in vertical axis. .PARAMETER Type - The type of mouse event (Example: Move, Click) + Type: Enum + Default: None + Description: The type of mouse event (Example: Move, Click) .PARAMETER Button - The pressed button on mouse (Example: Left, Right, Middle) + Type: String + Default: None + Description: The pressed button on mouse (Example: Left, Right, Middle) .EXAMPLE New-MouseEvent -X 10 -Y 35 -Type "Up" -Button "Left" @@ -1798,7 +1850,9 @@ function Invoke-RemoteDesktopViewer This event is used to simulate keyboard strokes. .PARAMETER Keys - Plain text keys to be simulated on remote computer. + Type: String + Default: None + Description: Plain text keys to be simulated on remote computer. .EXAMPLE New-KeyboardEvent -Keys "Hello, World" @@ -1825,16 +1879,24 @@ function Invoke-RemoteDesktopViewer When event is generated, it is immediately sent to remote server. .PARAMETER X - The position of virtual mouse in horizontal axis. + Type: Integer + Default: None + Description: The position of virtual mouse in horizontal axis. .PARAMETER Y - The position of virtual mouse in vertical axis. + Type: Integer + Default: None + Description: The position of virtual mouse in vertical axis. .PARAMETER Type - The type of mouse event (Example: Move, Click) + Type: Integer + Default: None + Description: The type of mouse event (Example: Move, Click) .PARAMETER Button - The pressed button on mouse (Example: Left, Right, Middle) + Type: Integer + Default: None + Description: The pressed button on mouse (Example: Left, Right, Middle) .EXAMPLE Send-VirtualMouse -X 10 -Y 20 -Type "Move" @@ -1876,7 +1938,9 @@ function Invoke-RemoteDesktopViewer Send to remote server key strokes to simulate. .PARAMETER KeyChain - A string representing character(s) to simulate remotely. + Type: String + Default: None + Description: A string representing character(s) to simulate remotely. .EXAMPLE Send-VirtualKeyboard -KeyChain "Hello, World" diff --git a/README.md b/README.md index 545f018..694b394 100644 --- a/README.md +++ b/README.md @@ -239,13 +239,13 @@ Create a new remote desktop session with a Power Remote Desktop Server. | DisableVerbosity | Switch | False | If present, program wont show verbosity messages | | UseTLSv1_3 | Switch | False | If present, TLS v1.3 will be used instead of TLS v1.2 (Recommended if applicable to both systems) | | Clipboard | Enum | Both | Define clipboard synchronization mode (`Both`, `Disabled`, `Send`, `Receive`) see bellow for more detail | -| ImageCompressionQuality | Integer (0-100) | 100 | JPEG Compression level from 0 to 100. 0 = Lowest quality, 100 = Highest quality. | +| ImageCompressionQuality | Integer (0-100) | 75 | JPEG Compression level from 0 to 100. 0 = Lowest quality, 100 = Highest quality. | | Resize | Switch | False | If present, remote desktop will get resized accordingly with `ResizeRatio` option. | | ResizeRatio | Integer (30-99) | 90 | Used with `Resize` option, define the resize ratio in percentage. | | AlwaysOnTop | Switch | False | If present, virtual desktop form will be above all other window's | | PacketSize | Enum | Size9216 | Define the network packet size for streams. Choose the packet size accordingly to your network constrainsts. | | BlockSize | Enum | Size64 | Define the screen grid block size. Choose the block size accordingly to remote screen size / computer constrainsts (CPU / Network) | -| HighQualityResize | Switch | False | Control the quality of remote desktop resize (smoothing) if applicable. If you lack of network speed, this option is not recommended. | +| FastResize | Switch | False | Control the quality of remote desktop resize (smoothing) if applicable. If you lack of network speed, this option is recommended. | ##### Clipboard Mode Enum Properties @@ -352,7 +352,8 @@ Invoke-RemoteDesktopServer | CertificateFile | String | None | A file containing valid certificate information (x509), must include the **private key** | | EncodedCertificate | String | None | A **base64** representation of the whole certificate file, must include the **private key** | | ViewOnly | Switch | False | If present, remote viewer is only allowed to view the desktop (Mouse and Keyboard are not authorized) | -| PreventComputerToSleep | Switch | None | If present, this option will prevent computer to enter in sleep mode while server is active and waiting for new connections. | +| PreventComputerToSleep | Switch | False | If present, this option will prevent computer to enter in sleep mode while server is active and waiting for new connections. | +| CertificatePassword | SecureString | None | Specify the password used to open a password-protected x509 Certificate provided by user. | ##### Server Address Examples @@ -494,13 +495,16 @@ https://user-images.githubusercontent.com/2520298/151220460-d620402b-da78-4d6d-8 * Keyboard simulation improved. * Various Optimization and fixes. +### 10 February 2022 (3.1.0) + +* Code refactoring and improvement. +* Desktop streaming improvement to gain few more FPS. +* Support password-protected external x509 Certificates. + ### List of ideas and TODO -* 🟢 Support Password Protected external Certificates. -* 🟢 Mutual Authentication for SSL/TLS (Client Certificate). -* 🟠 Listen for local/remote screen resolution update event. -* 🟠 Desktop Streaming Optimization. -* 🟠 Mouse Move / Events Optimization. +* 🟢 Mutual Authentication for SSL/TLS (Client Certificate) +* 🟠 Interrupt sessions when local resolution has changed. * 🔴 LogonUI Support. 🟢 = Easy diff --git a/TestServer.ps1 b/TestServer.ps1 index bb2cfc3..5379141 100644 --- a/TestServer.ps1 +++ b/TestServer.ps1 @@ -1,5 +1,82 @@ -Write-Output "This script is used during development phase. Never run this script under a production environment." +# cd .\Projects\PowerRemoteDesktop\; IEX (Get-Content .\TestServer.ps1 -Raw -Encoding UTF8) + +Write-Output "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️" +Write-Output "⚠️ Only use this script for testing the application NOT in production ⚠️" +Write-Output "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️" Invoke-Expression -Command (Get-Content "PowerRemoteDesktop_Server\PowerRemoteDesktop_Server.psm1" -Raw) -Invoke-RemoteDesktopServer -PreventComputerToSleep -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -EncodedCertificate "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" \ No newline at end of file +$password = "Jade@123@Pwd" +$encodedCertificate = "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" +$pwdProtectedEncodedCertificate = "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" + +Write-Host "Scenarios" +Write-Host "---------------" +Write-Host "1. Classic (Secure Password)" +Write-Host "2. Classic (Plain-text password)" +Write-Host "3. Classic Default Certificate" +Write-Host "4. TLS v1.3" +Write-Host "5. Verbosity Disabled, View Only, Prevent computer to sleep" +Write-Host "6. Receive clipboard only" +Write-Host "7. Send clipboard only" +Write-Host "8. Clipboard synchronization disabled" + +Write-Host "" + +[int]$scenario = Read-Host "Please choose scenario (default: 1)" + +switch ($scenario) +{ + 2 + { + Invoke-RemoteDesktopServer -Password $password -EncodedCertificate $pwdProtectedEncodedCertificate -CertificatePassword (ConvertTo-SecureString -String "hello" -AsPlainText -Force) + } + + 3 + { + Invoke-RemoteDesktopServer -Password $password + } + + 4 + { + Write-Host "⚡Check that TLSv1.3 is working." + Write-Host "⚡Check that certificate file is correctly loaded and used." + + Invoke-RemoteDesktopServer -Password $password -CertificateFile "c:\temp\phrozen-pwd.pfx" -UseTLSv1_3 -CertificatePassword (ConvertTo-SecureString -String "hello" -AsPlainText -Force) + } + + 5 + { + Write-Host "⚡Check that verbosity is disabled." + Write-Host "⚡Check that remote viewer can't control mouse and keyboard." + Write-Host "⚡Check that computer wont go to sleep." + + Invoke-RemoteDesktopServer -Password $password -CertificateFile "c:\temp\phrozen.p12" -DisableVerbosity -ViewOnly -PreventComputerToSleep + } + + 6 + { + Write-Host "⚡Check if server is only authorized to receive remote clipboard." + + Invoke-RemoteDesktopServer -Password $password -EncodedCertificate $encodedCertificate -Clipboard "Receive" + } + + 7 + { + Write-Host "⚡Check if server is only authorized to send local clipboard." + + Invoke-RemoteDesktopServer -Password $password -EncodedCertificate $encodedCertificate -Clipboard "Send" + } + + 8 + { + Write-Host "⚡Check if clipboard synchronization is completely disabled." + + Invoke-RemoteDesktopServer -Password $password -EncodedCertificate $encodedCertificate -Clipboard "Disabled" + } + + default + { + Invoke-RemoteDesktopServer -SecurePassword (ConvertTo-SecureString -String $password -AsPlainText -Force) -EncodedCertificate $encodedCertificate + } +} \ No newline at end of file diff --git a/TestViewer.ps1 b/TestViewer.ps1 index 42f43ea..b29715d 100644 --- a/TestViewer.ps1 +++ b/TestViewer.ps1 @@ -1,6 +1,126 @@ -Write-Output "This script is used during development phase. Never run this script under a production environment." +# cd .\Projects\PowerRemoteDesktop\; IEX (Get-Content .\TestViewer.ps1 -Raw -Encoding UTF8) + +Write-Output "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️" +Write-Output "⚠️ Only use this script for testing the application NOT in production ⚠️" +Write-Output "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️" Invoke-Expression -Command (Get-Content "PowerRemoteDesktop_Viewer\PowerRemoteDesktop_Viewer.psm1" -Raw) -Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "192.168.1.25" -BlockSize "Size128" -ImageCompressionQuality 100 -PacketSize "Size16384" -Resize -ResizeRatio 50 -#Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String "Jade@123@Pwd" -AsPlainText -Force) -ServerAddress "172.31.115.183" \ No newline at end of file +# Different Scenarios + +$remoteHost = "127.0.0.1" +$password = "Jade@123@Pwd" + +Write-Host "Scenarios" +Write-Host "---------------" +Write-Host "1. Classic (Secure Password)" +Write-Host "2. Classic (Plain-text password)" +Write-Host "3. Always On Top, Disable Verbosity" +Write-Host "4. TLS v1.3" +Write-Host "5. Clipboard Receive" +Write-Host "6. Clipboard Send" +Write-Host "7. Clipboard Disabled" +Write-Host "8. Image Quality Really Bad" +Write-Host "9. Image Quality Bad" +Write-Host "10. Image Quality High" +Write-Host "11. Resize 10% + Bad Resize Quality (FastResize)" +Write-Host "12. Resize 80%, Packet Size 16KiB, BlockSize 128x128" +Write-Host "13. Bad Password" + +Write-Host "" + +[int]$scenario = Read-Host "Please choose scenario (default: 1)" + +switch ($scenario) +{ + 2 + { + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost + } + + 3 + { + Write-Host "⚡Check that verbosity is not shown." + Write-Host "⚡Check that virtual desktop form is above all windows." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -DisableVerbosity -AlwaysOnTop + } + + 4 + { + Write-Host "⚡Check that TLSv1.3 is working." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -UseTLSv1_3 + } + + 5 + { + Write-Host "⚡Check if viewer is only authorized to receive remote clipboard." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -Clipboard "Receive" + } + + 6 + { + Write-Host "⚡Check if viewer is only authorized to send local clipboard." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -Clipboard "Send" + } + + 7 + { + Write-Host "⚡Check if clipboard synchronization is completely disabled." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -Clipboard "Disabled" + } + + 8 + { + Write-Host "⚡Check if image quality is really low." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -ImageCompressionQuality 0 + } + + 9 + { + Write-Host "⚡Check if image quality is not really good." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -ImageCompressionQuality 30 + } + + 10 + { + Write-Host "⚡Check if image quality is really good." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -ImageCompressionQuality 100 + } + + 11 + { + Write-Host "⚡Check if desktop image is reduced by 10%." + Write-Host "⚡Check if resize quality is bad." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -Resize -ResizeRatio 90 -FastResize + } + + 12 + { + Write-Host "⚡Check if desktop image is reduced by 20%." + Write-Host "⚡Control block size." + Write-Host "⚡Control packet size." + + Invoke-RemoteDesktopViewer -Password $password -ServerAddress $remoteHost -Resize -ResizeRatio 80 -PacketSize "Size16384" -BlockSize "Size128" + } + + 13 + { + Write-Host "⚡Be sure that authentication fails with remote server." + + Invoke-RemoteDesktopViewer -Password "bad@Bad123!Bad" -ServerAddress $remoteHost + } + + default + { + Invoke-RemoteDesktopViewer -SecurePassword (ConvertTo-SecureString -String $password -AsPlainText -Force) -ServerAddress $remoteHost + } +} \ No newline at end of file