• Welcome to TechPowerUp Forums, Guest! Please check out our forum guidelines for info related to our community.

C/C++/C# Console Redirection with Sockets - Win32

Oliver_FF

New Member
Joined
Oct 15, 2006
Messages
544 (0.08/day)
Processor Intel q9400 @ stock
Motherboard Lanparty P45-T2RS
Cooling Zalman CNPS-9500
Memory 8GB OCZ PC2-6400
Video Card(s) BFG Nvidia GTX285 OC
Storage 1TB, 500GB, 500GB
Display(s) 20" Samsung T200HD
Case Antec Mini P180
Audio Device(s) Sound Blaster X-Fi Elite Pro
Power Supply 700w Hiper
Software Ubuntu x64 virtualising Vista
Here's another interesting thing that you can do with sockets in C or C++. If any of you are familiar with the concepts of SSH or Telnet you'll get this straight away. Every console application has three IO paths...

1. stdIn is the standard input - aka when you type something into the console and it goes to your program.
2. stdOut is the standard output - aka when your program outputs feedback the data is written to stdOut, then its contents are written to the console you are working in.
3. stdErr is the standard error - aka if your program crashes and burns you can tell the OS roughly what happened (in an ideal world...)

Well with C or C++ in Windows you can redirect stdIn and stdOut of any console application to go through a network socket. To write to stdIn you simply send data over the socket. To read from stdOut you simply read data off of the socket. Pretty cool, eh? The most obvious use of something like this is for remote access to your computer - create a host program that sits around waiting for you to connect to it and redirect output to, say, cmd.exe so you have "complete" control over your PC remotely. "Complete" because cmd.exe sucks ;)


Code:
#using <windows.h> //sorry not entirely sure where inside there the specific headers are :(

//The socket to use
Socket thisSocket;
//Information about the process we are redirecting.
PROCESS_INFORMATION pi;
//Information about where the IO goes ;)
STARTUPINFO si;

/* Initialise the socket somewhere here */

//cb is the size of the STARTUPINFO structure
si.cb = sizeof( si );
//Tell it we do not want to see the application we are redirecting to/from
si.wShowWindow = SW_HIDE;
//Tell it we want to be able to use .wShowWinwow, .hStdInput, .hstdOutput, .hstdError members.
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
        
//Tell it we want the standard input to come from thisSocket
si.hStdInput = (HANDLE)thisSocket;
//Tell it we want the standard output to go to thisSocket
si.hStdOutput = (HANDLE)thisSocket;
//Tell it we want the stardard error to appear on thisSocket
si.hStdError = (HANDLE)thisSocket;
        
//Spawn the process we want to redirect using the information we just declared.
CreateProcess( NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi );
        

/* From now everything is set up and ticking over. */


//Wait until the process we started ends - then we can quit.
WaitForSingleObject( pi.hProcess, INFINITE );
        
//Close the process handles before exiting.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
MSDN on PROCESS_INFORMATION - http://msdn.microsoft.com/en-us/library/ms684873(VS.85).aspx
MSDN on STARTUPINFO - http://msdn.microsoft.com/en-us/library/ms686331(VS.85).aspx

The same effect can be done using C#. The code is easier to read and follow in C# but also it's trickier because you have to transfer the data between the socket yourself... At least that's what i'm assuming when i attempted it...

Code:
using System.Diagnostics;

process = new Process();
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
process.Start();

//use process.StandardInput.WriteLine(); to write to the applications stdIn

//use process.StandardOutput.ReadLine(); to read from the applications stdOut
 
Joined
May 20, 2004
Messages
10,487 (1.39/day)
I've been looking for simple code to send data over a network to build upon. This seems relatively simple. The examples I found were huge and didn't even work out of the box.

The downside is that I don't use C, I'll be looking into this though.
 

Oliver_FF

New Member
Joined
Oct 15, 2006
Messages
544 (0.08/day)
Processor Intel q9400 @ stock
Motherboard Lanparty P45-T2RS
Cooling Zalman CNPS-9500
Memory 8GB OCZ PC2-6400
Video Card(s) BFG Nvidia GTX285 OC
Storage 1TB, 500GB, 500GB
Display(s) 20" Samsung T200HD
Case Antec Mini P180
Audio Device(s) Sound Blaster X-Fi Elite Pro
Power Supply 700w Hiper
Software Ubuntu x64 virtualising Vista
If you just want to send data over the network you might want to check my basic sockets tutorial thing here:
http://forums.techpowerup.com/showthread.php?t=56901

The syntax may vary between programming languages, but the method of using the Windows API is usually fairly similar. It's all pretty well documented over at MSDN too :toast:
 

Kreij

Senior Monkey Moderator
Joined
Feb 6, 2007
Messages
13,817 (2.11/day)
Location
Cheeseland (Wisconsin, USA)
I've been looking for simple code to send data over a network to build upon. This seems relatively simple. The examples I found were huge and didn't even work out of the box.

The downside is that I don't use C, I'll be looking into this though.

What language are you using Dan?
 

Oliver_FF

New Member
Joined
Oct 15, 2006
Messages
544 (0.08/day)
Processor Intel q9400 @ stock
Motherboard Lanparty P45-T2RS
Cooling Zalman CNPS-9500
Memory 8GB OCZ PC2-6400
Video Card(s) BFG Nvidia GTX285 OC
Storage 1TB, 500GB, 500GB
Display(s) 20" Samsung T200HD
Case Antec Mini P180
Audio Device(s) Sound Blaster X-Fi Elite Pro
Power Supply 700w Hiper
Software Ubuntu x64 virtualising Vista
Well I guess it's lucky for you that the structures and function calls are all in VB :p

Code:
Dim pInfo As PROCESS_INFORMATION = New PROCESS_INFORMATION()
Dim sInfo As STARTUPINFO = New STARTUPINFO()
...[options here]...

CreateProcess("cmd.exe", Nothing, IntPtr.Zero, IntPtr.Zero, True, 0, IntPtr.Zero, Nothing, sInfo, pInfo)
 
Last edited:

lo0ney

New Member
Joined
Jan 14, 2009
Messages
6 (0.00/day)
Could you please explain how i would use the send and recv functions in order to send commands to cmd.exe and how to retrieve output?

Thanks alot!

Lo0ney
 

FordGT90Concept

"I go fast!1!11!1!"
Joined
Oct 13, 2008
Messages
26,259 (4.44/day)
Location
IA, USA
System Name BY-2021
Processor AMD Ryzen 7 5800X (65w eco profile)
Motherboard MSI B550 Gaming Plus
Cooling Scythe Mugen (rev 5)
Memory 2 x Kingston HyperX DDR4-3200 32 GiB
Video Card(s) AMD Radeon RX 7900 XT
Storage Samsung 980 Pro, Seagate Exos X20 TB 7200 RPM
Display(s) Nixeus NX-EDG274K (3840x2160@144 DP) + Samsung SyncMaster 906BW (1440x900@60 HDMI-DVI)
Case Coolermaster HAF 932 w/ USB 3.0 5.25" bay + USB 3.2 (A+C) 3.5" bay
Audio Device(s) Realtek ALC1150, Micca OriGen+
Power Supply Enermax Platimax 850w
Mouse Nixeus REVEL-X
Keyboard Tesoro Excalibur
Software Windows 10 Home 64-bit
Benchmark Scores Faster than the tortoise; slower than the hare.
If you want to use cmd on the local machine to do something, you're better off just using System.Diagnostics.ProcessStartInfo, System.Diagnostics.Process, and a System.IO.StreamReader. Simply make ProcessStartInfo point to command.exe on Windows 9x and cmd.exe on Windows NT. In the arguments, do:

/C "your commands > file to save output in"

Use the StreamReader to parse the file.


Also, there is Process.StandardOutput, Process.StandardInput, and Process.StandardError that you could use for redirection.
 

lo0ney

New Member
Joined
Jan 14, 2009
Messages
6 (0.00/day)
Well im using C and im trying to send commands to cmd.exe remotely. IE: Client/Server

So i would need the client to send commands to the server which would then pass the cmd to cmd.exe then return output to the client
 

FordGT90Concept

"I go fast!1!11!1!"
Joined
Oct 13, 2008
Messages
26,259 (4.44/day)
Location
IA, USA
System Name BY-2021
Processor AMD Ryzen 7 5800X (65w eco profile)
Motherboard MSI B550 Gaming Plus
Cooling Scythe Mugen (rev 5)
Memory 2 x Kingston HyperX DDR4-3200 32 GiB
Video Card(s) AMD Radeon RX 7900 XT
Storage Samsung 980 Pro, Seagate Exos X20 TB 7200 RPM
Display(s) Nixeus NX-EDG274K (3840x2160@144 DP) + Samsung SyncMaster 906BW (1440x900@60 HDMI-DVI)
Case Coolermaster HAF 932 w/ USB 3.0 5.25" bay + USB 3.2 (A+C) 3.5" bay
Audio Device(s) Realtek ALC1150, Micca OriGen+
Power Supply Enermax Platimax 850w
Mouse Nixeus REVEL-X
Keyboard Tesoro Excalibur
Software Windows 10 Home 64-bit
Benchmark Scores Faster than the tortoise; slower than the hare.
What kind of commands?
 

lo0ney

New Member
Joined
Jan 14, 2009
Messages
6 (0.00/day)
Any command that can be executed in cmd.exe. For example: "dir", "ipconfig", "cd C:\"

I have an edit box on the client where the user will type a command to be executed 'remotely' from the server, which launced cmd.exe, then returns the output of the command back to the client.
 

FordGT90Concept

"I go fast!1!11!1!"
Joined
Oct 13, 2008
Messages
26,259 (4.44/day)
Location
IA, USA
System Name BY-2021
Processor AMD Ryzen 7 5800X (65w eco profile)
Motherboard MSI B550 Gaming Plus
Cooling Scythe Mugen (rev 5)
Memory 2 x Kingston HyperX DDR4-3200 32 GiB
Video Card(s) AMD Radeon RX 7900 XT
Storage Samsung 980 Pro, Seagate Exos X20 TB 7200 RPM
Display(s) Nixeus NX-EDG274K (3840x2160@144 DP) + Samsung SyncMaster 906BW (1440x900@60 HDMI-DVI)
Case Coolermaster HAF 932 w/ USB 3.0 5.25" bay + USB 3.2 (A+C) 3.5" bay
Audio Device(s) Realtek ALC1150, Micca OriGen+
Power Supply Enermax Platimax 850w
Mouse Nixeus REVEL-X
Keyboard Tesoro Excalibur
Software Windows 10 Home 64-bit
Benchmark Scores Faster than the tortoise; slower than the hare.
I would advise against doing that as there is nothing to stop a user from completely destroying a computer. Additionally, some commands return a massive stack of data so you'd have to use TCP instead of the preferred UDP. Have you already got the network layers sorted because that will take the most work.
 

lo0ney

New Member
Joined
Jan 14, 2009
Messages
6 (0.00/day)
I can set cmd restrictions and filters later, but for now its just for testing purposes. And I am using TCP sockets, but could i not just use the source posted by Oliver FF to do this?

I have the source (first post) basically as my server, and then i have a client which connects to the server.
 

portnoid

New Member
Joined
Aug 22, 2010
Messages
1 (0.00/day)
I had the error 87 problem and it turned out to be not using a protocol that supported
IFS handles. Here is how I created a socket that uses IFS handles:

// Load version 2.2 of Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsda))
{
fprintf(stderr, (char *)"WSAStartup failed error=%d\n", WSAGetLastError());
exit(1);
}
// Find a protocol where socket descriptors returned by the provider
// are operating system Installable File System (IFS) handles.
// First, have WSAEnumProtocols tell you how big a buffer you need.
bool bProtocolFound = false;
LPWSAPROTOCOL_INFO lpProtocolBuf = NULL;
DWORD dwBufLen, dwErr;
int nRet = WSAEnumProtocolsA(NULL, lpProtocolBuf, &dwBufLen);
if (SOCKET_ERROR != nRet)
{
die((char *)"WSAEnumProtocols: should not have succeeded");
}
else if (WSAENOBUFS != (dwErr = WSAGetLastError()))
{
// WSAEnumProtocols failed for some reason not relating to buffer size.
die((char *)"WSAEnumProtocols");
}
else
{
// WSAEnumProtocols failed for the "expected" reason. Therefore,
// you need to allocate a buffer of the appropriate size.
lpProtocolBuf = (WSAPROTOCOL_INFO *)malloc(dwBufLen);
if (lpProtocolBuf)
{
// Now call WSAEnumProtocols again with the expectation
// that it will succeed because you have allocated a big enough
// buffer.
nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
if (SOCKET_ERROR == nRet)
{
die((char *)"WSAEnumProtocols");
}
else
{
// Loop through protocols, looking for the first service
// provider that meets the matching criteria.
for (i = 0; i < nRet; i++)
{
if ((IPPROTO_TCP == lpProtocolBuf.iProtocol) &&
(XP1_IFS_HANDLES == (XP1_IFS_HANDLES &
lpProtocolBuf.dwServiceFlags1)))
{
bProtocolFound = true;

// Create socket.
sock = WSASocket(
FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
&lpProtocolBuf,
0,
0);
if (INVALID_SOCKET == sock)
{
die((char *)"WSASocket");
}
break;
}
}
}
}
}
 

jackrabbit

New Member
Joined
Aug 6, 2015
Messages
1 (0.00/day)
I found an easy workaround to read the combined output and errors when I launch a process from c#.

Instead of calling your program, just call a batch file that calls your program with error redirection. In my case, the c# program first creates the temporary batch file, invoke this batch file, then delete it instead of directly calling the script.

Example of a batch file:
echo off
C:\Temp\MyProgram Arg1 Arg2 Arg3 2>&1

"2>&1" will redirect all errors of MyProgram into the STDOUT, so the process can now retrieve the combined output by reading the Process.StandardOutput only.

If you wish to invoke MyProgram and log STDOUT and STDERR at the same time, simply use this batch file:
echo off
C:\Temp\MyProgram Arg1 Arg2 Arg3 1>C:\Temp\MyProgram.log 2>&1

Or this if you wish to append to the log on every call:
echo off
C:\Temp\MyProgram Arg1 Arg2 Arg3 1>>C:\Temp\MyProgram.log 2>&1
 
Top