How to execute commands in the same process - c++

I made my own function to execute a command and save the result. The problem is the command is executed once and I can't execute several commands with the same "context". For example. If I execute cd .., the next command my cd .. will be ignored. This is my code :
std::string executeCommand(const std::string& cmd)
{
system((cmd + " > temp.txt").c_str());
std::ifstream ifs("temp.txt");
std::string ret{ std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>() };
ifs.close(); // must close the inout stream so the file can be cleaned up
return ret;
}
Can I have a sample with this feature ?

Do you use Windows system?
In Windows, you can interact with cmd through a Pipe.
like this:
#define BUFFER_SIZE 131072 //128*1024‬
void work() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION ProcessInformation;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &ProcessInformation) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
char* pbuff = new char[BUFFER_SIZE];
DWORD dw;
Sleep(100);//wait cmd start
ReadFile(houtRead, pbuff, BUFFER_SIZE-1, &dw, NULL);
pbuff[dw] = 0;
printf(pbuff);//version info
while (1) {
char* p = pbuff;
while ((*p = getchar()) != '\n') {
++p;
}
WriteFile(hinWrite, pbuff, p - pbuff + 1, &dw, 0);
while (1) {
ReadFile(houtRead, pbuff, BUFFER_SIZE - 1, &dw, NULL);
pbuff[dw] = 0;
printf("%s", pbuff);
if (pbuff[dw - 1] == '>')break;
}
}
}
Pipe can read/write like a file.
In other systems, there is also something like pipe,but I'm not familiar with them.
edit: another more clear example
int main() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;//no window
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION pi;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
Sleep(100);//wait cmd start
char* pbuff = new char[1024 * 128];
DWORD dw;
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);//cmd version info
WriteFile(hinWrite, "cd ..\n", 6, &dw, NULL);//send command to cmd.exe
Sleep(100);//wait command execute and cmd outputs
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
WriteFile(hinWrite, "dir\n", 4, &dw, NULL);//another command
Sleep(100);
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
delete[] pbuff;
TerminateProcess(pi.hProcess, 0);//Terminate cmd.exe
return 0;
}

Related

CreateProcessA fails for some programs

Found the below snippet here on SO:
https://stackoverflow.com/a/35658917/9265719.
It executes a command without creating a window. CreateProcessA() returns TRUE for cmd.exe but for any program in C:\Program Files(x86)\Windows Kits\10\Debuggers\x64\ it returns FALSE and GetLastError() returns 2 (ERROR_PATH_NOT_FOUND).
Why is it failing to create a process for programs in this directory?
#include <iostream>
#include <windows.h>
//
// Execute a command and get the results. (Only standard output)
//
std::string ExecCmd(
char cmd[] // [in] command to execute
)
{
std::string strResult;
HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe to get results from child's stdout.
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
return strResult;
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
// Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi = { 0 };
BOOL fSuccess = ::CreateProcessA(NULL, cmd, NULL, NULL, TRUE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
if (!fSuccess)
{
DWORD dw = GetLastError();
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
return strResult;
}
bool bProcessEnded = false;
for (; !bProcessEnded;)
{
// Give some timeslice (50 ms), so we won't waste 100% CPU.
bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if
// there is some data available over pipe.
for (;;)
{
char buf[1024];
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // No data available, return
break;
if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
// Error, the child process might ended
break;
buf[dwRead] = 0;
strResult += buf;
}
} //for
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return strResult;
} //ExecCmd
int main()
{
//char cmd[1000] = R"("C:\WINDOWS\system32\cmd.exe")";
char cmd[1000] = R"("C:\Program Files(x86)\Windows Kits\10\Debuggers\x64\cdb.exe")";
std::string op = ExecCmd(cmd);
std::cout << op.c_str();
}
You are missing a space in the path, between "Program Files" and "(x86)". Should be:
char cmd[1000] = R"("C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe")";

Routing stdout of process with PPID Spoofing

I tried to write a code that uses CreateProcess() to execute CMD commands and will redirect the stdout to a named pipe. I wanted to add a functionality to spoof the Parent PID so that the cmd will spawn under explorer.exe. Each of the functionalities works on it's own but when I tried to merge them it will not work.
The stdout routing:
int main()
{
HANDLE hStdout_Rd = NULL;
HANDLE hStdout_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&hStdout_Rd, &hStdout_Wr, &saAttr, NULL);
SetHandleInformation(hStdout_Rd, HANDLE_FLAG_INHERIT, 0);
//Set startup info
STARTUPINFO si;
ZeroMemory(&si, (sizeof(STARTUPINFO)));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdError = hStdout_Wr;
si.hStdOutput = hStdout_Wr;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
CString cmd;
if (CreateProcess(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
//Great success read pipe contents
}
CloseHandle(hStdout_Rd);
CloseHandle(hStdout_Wr);
}
The PPID Spoof:
int main() {
CString cmd;
STARTUPINFOEXA sInfoEX;
PROCESS_INFORMATION pInfo;
SIZE_T sizeT;
HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
CreateProcessA(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX), &pInfo);
return 0;
}
All Together:
int main() {
HANDLE hStdout_Rd = NULL;
HANDLE hStdout_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CString cmd;
STARTUPINFOEXA sInfoEX;
PROCESS_INFORMATION pInfo;
ZeroMemory(&pInfo, sizeof(PROCESS_INFORMATION));
SIZE_T sizeT;
HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
sInfoEX.StartupInfo = sizeof(STARTUPINFO);
sInfoEX.StartupInfo = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
sInfoEX.StartupInfo = hStdout_Wr;
sInfoEX.StartupInfo = hStdout_Wr;
sInfoEX.StartupInfo = SW_HIDE;
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
if (CreateProcessA(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX), &pInfo))
{
//Read pipe contents
}
return 0;
}
Is there anything I'm missing?

How to execute commands from an attached console

I'm coding a WinAPI GUI program that needs calling ftp and possibly other console programs while getting their console output to act accordingly ie. waiting for ftp to complete execution before reading all its output wouldn't do.
My current approach is calling CreateProcess() to create a cmd.exe process potentially hiding the ugly console window, AttachConsole() to make it my own, GetStdHandle() to get input and output handles, SetConsoleCursorPosition() to the end of the console buffer, and WriteConsole() with commands such as ftp\n or dir\n. Yet this commands are written but not executed. However, I can manually use the same console ( using CreateProcess() with CREATE_NEW_CONSOLE flag ) to type ftp press enter and get it executed.
Previous approaches involved:
Calling ftp directly with CreateProcess() and redirected inputs/outputs.
Couldn't get ftp output until the CreateProcess() process had already ended.
Using system().
Was advised against its usage before getting any output.
My current stripped down code:
// Next two structures might be a bit misleading, they were used for the 1. previous
// approach
PROCESS_INFORMATION piProcInfo;
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.lpSecurityDescriptor = NULL;
security.bInheritHandle = FALSE;
CreateProcess( NULL, "cmd", &security, &security, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo);
uint32_t pidConsole = piProcInfo.dwProcessId;
while ( ! AttachConsole(pidConsole) ){};
HANDLE myConsoleIn, myConsoleOut;
myConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
myConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(100);
CONSOLE_SCREEN_BUFFER_INFO myConsoleCursorInformation = {};
GetConsoleScreenBufferInfo(myConsoleOut,&myConsoleCursorInformation);
SetConsoleCursorPosition(myConsoleOut,myConsoleCursorInformation.dwSize);
CHAR myConsoleBuffer[200]="dir\n";
DWORD myConsoleProcessed;
WriteConsole( myConsoleOut, myConsoleBuffer, 4, &myConsoleProcessed, NULL);
How can I get a command written in the console to execute? Is there an alternative to my attempt of ending commands with a trailing \n ie. using WriteConsole() with a dir\n or ftp\n argument.
I thought about sending a keypress to the process in question after typing the desired command. Yet the created console needs not only to manually press the enter key but also having dir, ftp or whatever command to be manually typed.
Please feel free to point out any missing information !
How can I get a command written in the console to execute? Is there an
alternative to my attempt of ending commands with a trailing \n ie.
using WriteConsole() with a dir\n or ftp\n argument.
Try the following code to see if it works:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const wchar_t *cmdPath = L"C:\\Windows\\System32\\cmd.exe";
wchar_t *cmdArgs = (wchar_t *)L"C:\\Windows\\System32\\cmd.exe /k dir";
BOOL result = CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
Use /C if you want "Run Command and then terminate".
Update: Complete code for communicating with a child process(cmd.exe) using pipes.
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
#define BUFSIZE 1024
void ErrorExit(LPCTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
DWORD bytesAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvail, NULL)) {
std::cout << "Failed to call PeekNamedPipe" << std::endl;
}
if (bytesAvail) {
DWORD n;
BOOL success = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
}
bSuccess = WriteFile(hParentStdOut, chBuf,n, &dwWritten, NULL);
}
else
{
break;
}
}
}
void WriteToPipe(void)
{
DWORD dwWritten;
BOOL bSuccess = FALSE;
CHAR buf[] = "dir\n";
bSuccess = WriteFile(g_hChildStd_IN_Wr, buf, sizeof(buf)-1, &dwWritten, NULL);
}
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
TCHAR cmdPath[] = TEXT("C:\\Windows\\System32\\cmd.exe");
BOOL result = CreateProcess(cmdPath, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
for (;;)
{
ReadFromPipe();
WriteToPipe();
}
}

How to pass named pipe into CreateProcess (C++ Windows)

The hierarchy of my project is that I have 3 processes.
Main exe
Middle exe
Worker exe
Main exe will spawn middle exe using createprocess, from there middle exe will spawn worker exe using createprocess as well. Once worker exe is running middle exe terminates.
I would like to be able to pass the output data from worker exe back to main exe.
The way I think is best is to use a named pipe as worker exe's stdout.
In main exe I have this code:
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
HANDLE hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\MPipe"),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
PIPE_UNLIMITED_INSTANCES,
1024 * 4,
1024 * 4,
0,
&sa);
//CreateProcess() code here for middle exe
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL, "Failed to create pipe", NULL, MB_OK);
}
int count = 0;
while (true) {
if (!ConnectNamedPipe(hPipe, 0)) {
std::this_thread::sleep_for(std::chrono::seconds(20));
count++;
}
if (count > 5)
break;
}
if (count > 5) {
MessageBoxA(NULL, "Failed...", NULL, MB_OK);
}
Then in middle exe I have
memset(&si, 0x00, sizeof(si));
memset(&pi, 0x00, sizeof(pi));
si.cb = sizeof(si);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hPipe;
BOOL keepGoing = TRUE;
while(keepGoing){
hPipe = CreateFile(TEXT("\\\\.\\pipe\\MPipe"),
GENERIC_WRITE | GENERIC_READ,
0,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
MessageBoxA(NULL, "Pipe busy...", NULL, MB_OK);
WaitNamedPipe("\\\\.\\pipe\\MPipe", 2000);
}
else {
MessageBoxA(NULL, "Failed to open pipe for writing", NULL, MB_OK);
break;
}
}
else {
keepGoing = FALSE;
}
}
MessageBoxA(NULL, "Client pipe connected!", NULL, MB_OK);
SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, 0);
si.hStdError = hPipe;
si.hStdOutput = hPipe;
processFlags = CREATE_SUSPENDED;
CreateProcessW(processPath, commandLine, NULL, NULL, TRUE, processFlags, NULL, NULL, &si, &pi);
For some reason in Main exe it will output "Failed to establish connection..." But in my Middle exe it will output "Client connected!".
I can't figure out why the Middle exe appears to establish a connection but the Main exe doesn't recognize it when ConnectNamedPipe() is called.
Any ideas?

CreateProcessWithLogonW get standard output

I found the below comment on Microsoft's documentation site:
Also, CreateProcessWithLogonW rejects STD handles (0/1/2) i.e. what GetStdHandle() returns by default, when using STARTF_USESTDHANDLES. It returns error 6, invalid handles, because 0/1/2 are not "real" handles. The only way we found to redirect the console input/output was to create custom handles (pipes) and use those instead (e.g. even just as dummy handles that you don't use). This is missing functionality from CreateProcessWithLogonW, because CreateProcess (and maybe CreateProcessAsUser, I didn't verify that) accepts STD handles.
This points to creating custom pipes.
I found this StackOverflow answer which points to using things such as "SafeFileHandle". This is apparently C++/CLI which I am not familiar with how to use. I am using Visual C++ and therefore "native" C++?
I am looking for a simple solution of how to fix this issue. I am a newbie in C++ and the CLI thing confused me because I simply don't know how to use it, or make Visual C++ compatible with it.
Given my normal C++ and my question, HOW EXACTLY would I create the custom pipe which will allow me to get the standard output?
Here is my code:
#include "stdafx.h"
void DisplayError(LPWSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpvMessageBuffer, 0, NULL);
//
//... now display this string
//
wprintf(L"ERROR: API = %s.\n", pszAPI);
wprintf(L" error code = %d.\n", GetLastError());
wprintf(L" message = %s.\n", (LPWSTR)lpvMessageBuffer);
//
// Free the buffer allocated by the system
//
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void _tmain(int argc, WCHAR *argv[])
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
HANDLE pipe1 = CreateNamedPipe(L"\\\\.\\pipe\\pipe1", PIPE_ACCESS_DUPLEX,
PIPE_WAIT, 1024, 1024, 1024, 60, NULL);
HANDLE pipe2 = CreateNamedPipe(L"\\\\.\\pipe\\pipe2", PIPE_ACCESS_DUPLEX,
PIPE_WAIT, 1024, 1024, 1024, 60, NULL);
si.dwFlags |= STARTF_USESHOWWINDOW;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = pipe1;
si.hStdError = pipe2;
DWORD dwSize;
HANDLE hToken;
LPVOID lpvEnv;
PROCESS_INFORMATION pi = {0};
WCHAR szUserProfile[256] = L"";
si.cb = sizeof(STARTUPINFO);
if (argc != 4)
{
wprintf(L"Usage: %s [user#domain] [password] [cmd]", argv[0]);
wprintf(L"\n\n");
return;
}
//
// TO DO: change NULL to '.' to use local account database
//
if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken))
DisplayError(L"LogonUser");
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
DisplayError(L"CreateEnvironmentBlock");
dwSize = sizeof(szUserProfile)/sizeof(WCHAR);
if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
DisplayError(L"GetUserProfileDirectory");
//
// TO DO: change NULL to '.' to use local account database
//
if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
LOGON_WITH_PROFILE, NULL, argv[3],
CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
&si, &pi))
DisplayError(L"CreateProcessWithLogonW");
if (!DestroyEnvironmentBlock(lpvEnv))
DisplayError(L"DestroyEnvironmentBlock");
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
I tried this and I am getting no output on the console window. As I said, I checked the answer on StackOverflow and this whole C++/CLI thing confuses me. If someone knows a simple solution to redirecting the output using a custom pipe using Visual C++, let me know.
And if I did anything wrong above, let me know as well.
EDIT:
After following the suggestions given by Remy's answer. I came up with the following code, which yields an error code 3: The system cannot find the path specified.
#include "stdafx.h"
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
int BUFSIZE = 1064;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
void DisplayError(LPWSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpvMessageBuffer, 0, NULL);
//
//... now display this string
//
wprintf(L"ERROR: API = %s.\n", pszAPI);
wprintf(L" error code = %d.\n", GetLastError());
wprintf(L" message = %s.\n", (LPWSTR)lpvMessageBuffer);
//
// Free the buffer allocated by the system
//
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void _tmain(int argc, WCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
/////////////////////////////Start CreateChildProcess////////
STARTUPINFO si;
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESHOWWINDOW;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
DWORD dwSize;
HANDLE hToken;
LPVOID lpvEnv;
PROCESS_INFORMATION pi = {0};
WCHAR szUserProfile[256] = L"";
si.cb = sizeof(STARTUPINFO);
if (argc != 4)
{
wprintf(L"Usage: %s [user#domain] [password] [cmd]", argv[0]);
wprintf(L"\n\n");
return;
}
//
// TO DO: change NULL to '.' to use local account database
//
if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken))
DisplayError(L"LogonUser");
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
DisplayError(L"CreateEnvironmentBlock");
dwSize = sizeof(szUserProfile)/sizeof(WCHAR);
if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
DisplayError(L"GetUserProfileDirectory");
//
// TO DO: change NULL to '.' to use local account database
//
if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
LOGON_WITH_PROFILE, NULL, argv[3],
CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
&si, &pi))
DisplayError(L"CreateProcessWithLogonW");
if (!DestroyEnvironmentBlock(lpvEnv))
DisplayError(L"DestroyEnvironmentBlock");
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
/////////////////////////////End CreateChildProcess///////////
if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[3],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if ( g_hInputFile == INVALID_HANDLE_VALUE )
ErrorExit(TEXT("CreateFile"));
WriteToPipe();
printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe();
}
void WriteToPipe(void)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[4096];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if ( ! bSuccess ) break;
}
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[4096];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) break;
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
Use anonymous pipes via CreatePipe(), not named pipes via CreateNamedPipe(). See MSDN for an example:
Creating a Child Process with Redirected Input and Output