From 0008631771afc2156d87448726b388ec4d668a87 Mon Sep 17 00:00:00 2001 From: Faryan Rezagholi Date: Sat, 7 Sep 2019 18:16:36 +0200 Subject: [PATCH] Added PowerShell Protocol for remote PS-Sessions --- .../ConfigWindowGeneralTests.cs | 9 + .../Connection/AbstractConnectionRecord.cs | 2 +- mRemoteV1/Connection/ConnectionInfo.cs | 3 + .../Connection.Protocol.PowerShell.cs | 165 ++++++++++++++++++ .../Connection/Protocol/ProtocolFactory.cs | 5 +- mRemoteV1/Connection/Protocol/ProtocolType.cs | 5 +- .../Resources/Language/Language.Designer.cs | 11 ++ mRemoteV1/Resources/Language/Language.resx | 3 + mRemoteV1/mRemoteV1.csproj | 1 + 9 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 mRemoteV1/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs diff --git a/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs b/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs index 9dbbbffc..4b6bc09c 100644 --- a/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs +++ b/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs @@ -342,6 +342,15 @@ namespace mRemoteNGTests.UI.Window.ConfigWindowTests nameof(ConnectionInfo.CacheBitmaps), }); break; + case ProtocolType.PowerShell: + expectedProperties.AddRange(new[] + { + nameof(ConnectionInfo.Username), + nameof(ConnectionInfo.Password), + nameof(ConnectionInfo.Domain), + nameof(ConnectionInfo.Port), + }); + break; case ProtocolType.IntApp: expectedProperties.AddRange(new[] { diff --git a/mRemoteV1/Connection/AbstractConnectionRecord.cs b/mRemoteV1/Connection/AbstractConnectionRecord.cs index 15f4fcbd..10f74bd2 100644 --- a/mRemoteV1/Connection/AbstractConnectionRecord.cs +++ b/mRemoteV1/Connection/AbstractConnectionRecord.cs @@ -176,7 +176,7 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory("strCategoryConnection", 2), LocalizedAttributes.LocalizedDisplayName("strPropertyNameDomain"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionDomain"), - UsedInProtocol(ProtocolType.RDP, ProtocolType.ICA, ProtocolType.IntApp)] + UsedInProtocol(ProtocolType.RDP, ProtocolType.ICA, ProtocolType.IntApp, ProtocolType.PowerShell)] public string Domain { get => GetPropertyValue("Domain", _domain).Trim(); diff --git a/mRemoteV1/Connection/ConnectionInfo.cs b/mRemoteV1/Connection/ConnectionInfo.cs index 23bba1a1..38c678af 100644 --- a/mRemoteV1/Connection/ConnectionInfo.cs +++ b/mRemoteV1/Connection/ConnectionInfo.cs @@ -7,6 +7,7 @@ using mRemoteNG.App; using mRemoteNG.Connection.Protocol; using mRemoteNG.Connection.Protocol.Http; using mRemoteNG.Connection.Protocol.ICA; +using mRemoteNG.Connection.Protocol.PowerShell; using mRemoteNG.Connection.Protocol.RAW; using mRemoteNG.Connection.Protocol.RDP; using mRemoteNG.Connection.Protocol.Rlogin; @@ -265,6 +266,8 @@ namespace mRemoteNG.Connection return (int)ProtocolHTTPS.Defaults.Port; case ProtocolType.ICA: return (int)IcaProtocol.Defaults.Port; + case ProtocolType.PowerShell: + return (int)ProtocolPowerShell.Defaults.Port; case ProtocolType.IntApp: return (int)IntegratedProgram.Defaults.Port; } diff --git a/mRemoteV1/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs b/mRemoteV1/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs new file mode 100644 index 00000000..7b204639 --- /dev/null +++ b/mRemoteV1/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs @@ -0,0 +1,165 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Forms; +using mRemoteNG.App; +using mRemoteNG.Messages; +using mRemoteNG.Tools; + +namespace mRemoteNG.Connection.Protocol.PowerShell +{ + public class ProtocolPowerShell : ProtocolBase + { + #region Private Fields + + private IntPtr _handle; + private Process _process; + private readonly ConnectionInfo _connectionInfo; + + public ProtocolPowerShell(ConnectionInfo connectionInfo) + { + _connectionInfo = connectionInfo; + } + + #endregion + + #region Public Methods + + public override bool Connect() + { + try + { + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, + $"Attempting to start remote PowerShell session.", true); + + _process = new Process + { + StartInfo = + { + UseShellExecute = true, + FileName = @"C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe", + Arguments = $@"-NoExit -Command ""$password = ConvertTo-SecureString '{_connectionInfo.Password}' -AsPlainText -Force; $cred = New-Object System.Management.Automation.PSCredential -ArgumentList @('{_connectionInfo.Domain}\{_connectionInfo.Username}', $password); Enter-PSSession -ComputerName {_connectionInfo.Hostname} -Credential $cred""" + }, + EnableRaisingEvents = true + }; + + _process.Exited += ProcessExited; + + _process.Start(); + Thread.Sleep(500); + //_process.WaitForInputIdle(); + NativeMethods.SetParent(_process.MainWindowHandle, InterfaceControl.Handle); + + + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, Language.strIntAppStuff, true); + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, + string.Format(Language.strIntAppHandle, _handle), true); + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, + string.Format(Language.strIntAppTitle, _process.MainWindowTitle), + true); + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, + string.Format(Language.strIntAppParentHandle, + InterfaceControl.Parent.Handle), true); + + Resize(this, new EventArgs()); + base.Connect(); + return true; + } + catch (Exception ex) + { + Runtime.MessageCollector?.AddExceptionMessage(Language.strIntAppConnectionFailed, ex); + return false; + } + } + + [DllImport("user32.dll")] + static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + public override void Focus() + { + try + { + NativeMethods.SetForegroundWindow(_handle); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage(Language.strIntAppFocusFailed, ex); + } + } + + public override void Resize(object sender, EventArgs e) + { + try + { + if (InterfaceControl.Size == Size.Empty) return; + NativeMethods.MoveWindow(_handle, -SystemInformation.FrameBorderSize.Width, + -(SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height), + InterfaceControl.Width + SystemInformation.FrameBorderSize.Width * 2, + InterfaceControl.Height + SystemInformation.CaptionHeight + + SystemInformation.FrameBorderSize.Height * 2, true); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage(Language.strIntAppResizeFailed, ex); + } + } + + public override void Close() + { + /* only attempt this if we have a valid process object + * Non-integrated tools will still call base.Close() and don't have a valid process object. + * See Connect() above... This just muddies up the log. + */ + if (_process != null) + { + try + { + if (!_process.HasExited) + { + _process.Kill(); + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage(Language.strIntAppKillFailed, ex); + } + + try + { + if (!_process.HasExited) + { + _process.Dispose(); + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage(Language.strIntAppDisposeFailed, ex); + } + } + + base.Close(); + } + + #endregion + + #region Private Methods + + private void ProcessExited(object sender, EventArgs e) + { + Event_Closed(this); + } + + #endregion + + #region Enumerations + + public enum Defaults + { + Port = 5985 + } + + #endregion + } +} \ No newline at end of file diff --git a/mRemoteV1/Connection/Protocol/ProtocolFactory.cs b/mRemoteV1/Connection/Protocol/ProtocolFactory.cs index f659454f..f97aecb8 100644 --- a/mRemoteV1/Connection/Protocol/ProtocolFactory.cs +++ b/mRemoteV1/Connection/Protocol/ProtocolFactory.cs @@ -7,6 +7,7 @@ using mRemoteNG.Connection.Protocol.SSH; using mRemoteNG.Connection.Protocol.Telnet; using mRemoteNG.Connection.Protocol.VNC; using System; +using mRemoteNG.Connection.Protocol.PowerShell; namespace mRemoteNG.Connection.Protocol { @@ -43,6 +44,8 @@ namespace mRemoteNG.Connection.Protocol var icaProtocol = new IcaProtocol(); icaProtocol.tmrReconnect.Elapsed += icaProtocol.tmrReconnect_Elapsed; return icaProtocol; + case ProtocolType.PowerShell: + return new ProtocolPowerShell(connectionInfo); case ProtocolType.IntApp: if (connectionInfo.ExtApp == "") { @@ -51,7 +54,7 @@ namespace mRemoteNG.Connection.Protocol return new IntegratedProgram(); } - return default(ProtocolBase); + return default; } } } \ No newline at end of file diff --git a/mRemoteV1/Connection/Protocol/ProtocolType.cs b/mRemoteV1/Connection/Protocol/ProtocolType.cs index 44aca2f9..b262f8b9 100644 --- a/mRemoteV1/Connection/Protocol/ProtocolType.cs +++ b/mRemoteV1/Connection/Protocol/ProtocolType.cs @@ -1,4 +1,4 @@ -using mRemoteNG.Tools; +using mRemoteNG.Tools; namespace mRemoteNG.Connection.Protocol { @@ -34,6 +34,9 @@ namespace mRemoteNG.Connection.Protocol [LocalizedAttributes.LocalizedDescription("strICA")] ICA = 9, + [LocalizedAttributes.LocalizedDescription("strPowerShell")] + PowerShell = 10, + [LocalizedAttributes.LocalizedDescription("strExtApp")] IntApp = 20 } diff --git a/mRemoteV1/Resources/Language/Language.Designer.cs b/mRemoteV1/Resources/Language/Language.Designer.cs index 6b1d659e..f3f0706d 100644 --- a/mRemoteV1/Resources/Language/Language.Designer.cs +++ b/mRemoteV1/Resources/Language/Language.Designer.cs @@ -5694,6 +5694,17 @@ namespace mRemoteNG } } + /// + /// Looks up a localized string similar to PowerShell. + /// + internal static string strPowerShell + { + get + { + return ResourceManager.GetString("strPowerShell", resourceCulture); + } + } + /// /// Looks up a localized string similar to (These properties will only be saved if you select mRemote/mRemoteNG XML as output file format!). /// diff --git a/mRemoteV1/Resources/Language/Language.resx b/mRemoteV1/Resources/Language/Language.resx index 93722b8b..b632af16 100644 --- a/mRemoteV1/Resources/Language/Language.resx +++ b/mRemoteV1/Resources/Language/Language.resx @@ -2825,4 +2825,7 @@ Development Channel includes Alphas, Betas & Release Candidates. Set a password needed to encrypt the connection file with. You will be prompted to enter your passcode before starting mRemoteNG. + + PowerShell + \ No newline at end of file diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index bbf5c65b..1c256413 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -285,6 +285,7 @@ + True