Create nonelevated process from elevated process with CreateProcessAsUser function - c++

I am running a program with SYSTEM privleges. I ask for the usename and password for a handle from LogonUser(szUserName, NULL, szPassword, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken) for a local logon. I want to run the cmd.exe program as the user credentials provided from the logon. The logon is successful, but when I try to run the process, nothing happens. The program aborts. My code is...
// cmd.cpp : Defines the entry point for the console application.
//
#include <Windows.h>
#include <Lmcons.h>
#include <iostream>
#include <ctype.h>
#include <string>
#include <stdio.h>
#define winstring LPWSTR
#define stcas(x) static_cast<x>
#define INFO_BUFFER_SIZE 260
using namespace std;
void ReportError(LPCWSTR pszFunction, DWORD dwError = GetLastError())
{
wprintf(L"%s failed w/err 0x%08lx\n", pszFunction, dwError);
}
int main()
{
TCHAR un[UNLEN+1];
DWORD size = UNLEN + 1;
GetUserName(un, &size);
string una(un);
bool sys = !una.compare("SYSTEM");
if(!sys) {
system("cls");
system("title Command Prompt");
system("cmd");
return 0;
}
char szUserName[INFO_BUFFER_SIZE] = {};
char szPassword[INFO_BUFFER_SIZE] = {};
char *pc = NULL;
HANDLE hToken = NULL;
BOOL fSucceeded = FALSE;
BOOL logon = FALSE;
printf("Enter the username: ");
fgets(szUserName, ARRAYSIZE(szUserName), stdin);
pc = strchr(szUserName, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
cout << endl;
//string un(szUserName);
printf("Enter the password: ");
fgets(szPassword, ARRAYSIZE(szPassword), stdin);
pc = strchr(szPassword, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
if (!LogonUser(szUserName, NULL, szPassword, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken))
{
ReportError(L"Logon");
goto Cleanup;
}
else logon = true;
// Impersonate the logged on user.
if (!ImpersonateLoggedOnUser(hToken))
{
ReportError(L"imp");
goto Cleanup;
}
fSucceeded = true;
Cleanup:
// Clean up the buffer containing sensitive password.
SecureZeroMemory(szPassword, sizeof(szPassword));
LPTSTR szCmdline[] = {"C:\\windows\\system32\\cmd.exe"};
STARTUPINFO si;
PROCESS_INFORMATION pi;
TCHAR uni[UNLEN+1];
DWORD sizei = UNLEN + 1;
GetUserName(uni, &sizei);
string unai(uni);
cout << unai << endl;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
HANDLE phToken = NULL;
BOOL dup = FALSE;
if(!DuplicateTokenEx(hToken, TOKEN_DUPLICATE | TOKEN_IMPERSONATE, NULL, SecurityImpersonation, TokenImpersonation, &phToken)){
ReportError(L"DUPLICATE TOKEN");
}
else dup = TRUE;
system("pause");
// If the impersonation was successful, undo the impersonation.
if (fSucceeded && logon && dup)
{
system("cls");
system("title Command Prompt");
//system("cmd");
if(!CreateProcessAsUser(
phToken, // client's access token
NULL, // file to execute
*szCmdline, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
)){
ReportError(L"Create Process");
}
if (!RevertToSelf())
{
ReportError(L"Undo Imp");
}
}
system("pause");
}
I really would like the nonelevated proccess to avoid security issues.
Please don't tell me that my typed password is shown. I know that and will fix that later.

Try using the CreateProcessWithLogonUser Function. This should do that all for you with no errors.

You're not initializing si. You need to do this before calling CreateProcessAsUser:
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);

Related

C++ on Windows: Pipe to stdin not properly transferring data + ReadFile stucks when reading from pipe

I tried to create a pwntools-like program for windows. I implemented a send and recv to send data to stdin of a and to receive data from stdout. I did that using pipes.
#include <iostream>
#include <cstdio>
#include <windows.h>
void spawn();
void pwn_send(CONST CHAR chBuf[]);
CHAR* pwn_recv(SIZE_T sz);
CHAR* pwn_recv();
HANDLE stdout_write = NULL;
HANDLE stdout_read = NULL;
HANDLE stdin_write = NULL;
HANDLE stdin_read = NULL;
int main() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if(!CreatePipe(&stdout_read, &stdout_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) return -1;
if(!CreatePipe(&stdin_read, &stdin_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0)) return -1;
spawn();
CHAR chBuf[] = "mkdir C:\\Users\\comma\\Desktop\\x\r\n";
pwn_send(chBuf);
std::cout << pwn_recv(20) << std::endl;
return 0;
}
void spawn() {
TCHAR cmd[] = TEXT("cmd.exe");
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = stdout_write;
si.hStdOutput = stdout_write;
si.hStdInput = stdin_read;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
if(!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) exit(-1);
return;
}
void pwn_send(CONST CHAR chBuf[]) {
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
while(1) {
bSuccess = WriteFile(stdin_write, chBuf, strlen(chBuf), &dwWritten, NULL);
if (bSuccess) break;
}
CloseHandle(stdin_write);
return;
}
CHAR* pwn_recv(SIZE_T sz) {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, sz+1);
while (1) {
std::cout << "XXX" << std::endl;
bSuccess = ReadFile(stdout_read, chBuf, sz, &dwRead, NULL);
std::cout << "YYY" << std::endl;
if (bSuccess) break;
}
return chBuf;
}
CHAR* pwn_recv() {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, 0x1000);
while (1) {
bSuccess = ReadFile(stdout_read, chBuf, 0x1000, &dwWritten, NULL);
if (bSuccess) break;
}
return chBuf;
}
So the first issue here is that the program I started with spawn(cmd.exe) doesn't properly receive the command I send to stdin. No folder is created anywhere. CreateProcess succeeds. Receiving output doesn't work either, because ReadFile seems to stuck. XXX is displayed in the console, butYYY never. Any ideas?

Win32 communicating with MariaDb

Greetings StackOverflow comrades. Last time I inquired about environment variables. Thanks to Remy for informing me.
Thanks to him I completed my Process class. Now the real problem was connecting to and communicating with MariaDb. I successfully launched MariaDb; but for some reason, reading from MariaDb deadlocks my program. I know before hand that, once connected to MariaDb using, mysql --user=root, MariaDb writes MariaDb[NONE]> to the console. And expects an SQL query input. But I my application deadlocks when trying to read.
I am wondering if MariaDb is using the handles I passed it in CreateProcess StartUpInfo. I did some search on google and found a library on MariaDb website which allows C/C++ programs to connect to MariaDb. So probably they are coercing us to use there library to connect to MariaDb.
Edit:
#Secumen I am trying to communicate with MariaDb via win32 CreateProcess; you know that popular database program? I am using the one shipped with Xampp software.
I want to be able to automate the tasks of adding tables, data, users, etc.
I created the pipes with CreatePipe(...). Then I launched MariaDb using CreateProcess(...). The second argument to CreateProcess was the command line, mysql --user=root. Note that Xampp calls MariaDb MySql. Now I am connected to MariaDb and expect it to write MariaDb[NONE]> to the console. Which means that I should have data to read via ReadFile(...). However ReadFile deadlocks and PeekNamedFile shows that there was zero bytes available to be read.
How the heck then would I communicate with MariaDb if it is not writing to the handles I passed it in CreateProcess?
Edit - Minimal Example
SECURITY_ATTRIBUTES sa = {};
sa.bInheritHandle = true;
sa.lpSecurityDescriptor =NULL;
sa.nLength = sizeof(sa);
HANDLE r,w;
HANDLE r1,w1;
if(!CreatePipe(&r,&w,&sa,0)) throw "Failed to create pipe\n";
if(!CreatePipe(&r1,&w1,&sa,0)) throw "Failed to create pipe\n";
auto cmd = "MYSQL --user=root";
auto current_dir = R"(C:\Program Files\xampp\mysql\bin)";
SetCurrentDirectoryA(current_dir);
STARTUPINFOA si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = w;
si.hStdOutput = w;
si.hStdInput = r1;
if(!CreateProcessA(NULL,cmd,NULL,NULL,true,0,NULL,NULL,&si,&pi))
throw "Failed to create process";
CloseHandle(w);
CloseHandle(r1);
{
DWORD sz, avail;
char *buf = new char[1024];
PeekNamedPipe(r,NULL,0,NULL,&avail,NULL);
printf("available %i",avail);
ReadFile(r,buf,1023,&sz,NULL);
buf[sz] = 0;
printf("%s",buf);
delete[] buf;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
I have written the following code by referring to MSDN. I am using visual studio 2017 and test with win32 application.
I have passed several SQL statements through PIPE for testing, and confirmed that the results were exactly obtained through PIPE.
#include <string>
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hChildOutRd = NULL;
HANDLE hChildOutWr = NULL;
HANDLE hChildInRd = NULL;
HANDLE hChildInWr = NULL;
//. Internal functions.
int CreatePipes();
int CreateChildProcess();
int PipeIO(string & request, string & response);
int main()
{
if (CreatePipes() != ERROR_SUCCESS)
{
cout << "Failed to create pipe. error: " << GetLastError() << endl;
return -1;
}
//. Create the child process.
if (CreateChildProcess() != ERROR_SUCCESS)
{
cout << "Failed to create child process. error: " << GetLastError() << endl;
return -2;
}
//. Write and Read.
string request, response;
request = "use test_db; select count(*) from test_table;";
PipeIO(request, response);
cout << "[Request]: " << request << "\n[Response]: \n" << response << endl << endl;
return 0;
}
int CreatePipes()
{
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
//. Create a pipe for the child process's output.
if (!CreatePipe(&hChildOutRd, &hChildOutWr, &sa, 0))
{
return -1;
}
if (!SetHandleInformation(hChildOutRd, HANDLE_FLAG_INHERIT, 0))
{
return -2;
}
//. Create a pipe for the child process's input.
if (!CreatePipe(&hChildInRd, &hChildInWr, &sa, 0))
{
return -3;
}
if (!SetHandleInformation(hChildInWr, HANDLE_FLAG_INHERIT, 0))
{
return -4;
}
return ERROR_SUCCESS;
}
int CreateChildProcess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.hStdError = hChildOutWr;
si.hStdOutput = hChildOutWr;
si.hStdInput = hChildInRd;
si.dwFlags |= STARTF_USESTDHANDLES;
wchar_t cmd[] = L" -uroot -ppassword";
BOOL bRet = CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!bRet)
{
return -5;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hChildInRd);
CloseHandle(hChildOutWr);
}
return ERROR_SUCCESS;
}
int PipeIO(string & request, string & response)
{
int nRet = ERROR_SUCCESS;
DWORD dwRead = 0, dwWrite = 0;
response.clear();
if (!WriteFile(hChildInWr, request.c_str(), request.length(), &dwWrite, NULL))
{
cout << "ERROR: failed to write pipe. error: " << GetLastError() << endl;
return -1;
}
CloseHandle(hChildInWr);
while (true)
{
char buffer[1024] = { 0 };
if (!ReadFile(hChildOutRd, buffer, 1024, &dwRead, NULL) || dwRead == 0)
{
break;
}
response += buffer;
}
CloseHandle(hChildOutRd);
return ERROR_SUCCESS;
}
Then, you can do this asynchronously.
I referred to RbMm's answer at this article.
#include <malloc.h>
#include <windows.h>
#include <winternl.h>
#include <array>
#include <string>
#include <iostream>
typedef ULONG(__stdcall *RTLNTSTATUSTODOSERROR)(NTSTATUS);
RTLNTSTATUSTODOSERROR pRtlNtStatusToDosError = NULL;
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
ULONG Create(HANDLE hFile);
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL is_ok)
{
if (is_ok)
{
OnIoComplete(NOERROR, InternalHigh);
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer) - 1, 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
_buffer[dwNumberOfBytesTransfered] = 0;
std::cout << _buffer;
}
Read();
}
break;
case write:
break;
}
Release();
}
static VOID WINAPI _OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(pRtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
int main()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
pRtlNtStatusToDosError = (RTLNTSTATUSTODOSERROR)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError");
HANDLE hFile = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_READ_DATA | FILE_WRITE_DATA | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return -1;
}
IO_COUNT obj;
if (obj.Create(hFile) != NOERROR)
{
CloseHandle(hFile);
return -2;
}
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si) };
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFile(name, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError == INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return -3;
}
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR param[] = L" -uroot -ppassword";
if (!CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", param, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(hFile);
return -4;
}
//. Close unneeded handles.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(si.hStdError);
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
std::array<std::string, 5> commands = {
"show databases;\n",
"use test_db;\n",
"select count(*) from test_table;\n",
"select * from test_table;\n",
"exit\n"
};
for (auto & iter : commands)
{
if (p = new U_IRP(&obj))
{
p->Write(iter.c_str(), iter.length());
p->Release();
}
}
obj.Wait();
CloseHandle(hFile);
DisconnectNamedPipe(hFile);
return 0;
}

WINAPI CreateProcess child process is not running

I'm using the article in msdn creating child process because it's pretty close to what I want to achieve.
The questions in creating a child process c#, redirection using pipes
were helpful do find a few error on my approach but there are mostl likely more errors than what I was able to find.
I'm trying to create a connection between parent and child processes using pipes where they are able to write and read to each others StdOut. In my solution I created two console applications named ParentTalk and ChildTalk.
From what I was able to see the child process is not running the code I created for it so I might be creating the child process wrongly
Parent.cpp
#include <windows.h>
#include "pch.h"
#include <iostream>
#include <stdio.h>
HANDLE childInRead = NULL;
HANDLE childInWrite = NULL;
HANDLE childOutRead = NULL;
HANDLE childOutWrite = NULL;
HANDLE parentInRead = NULL;
#define BUFSIZE 4096
void CreateChildProcess() {
TCHAR applicationName[] = TEXT("ChildTalk.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL success = FALSE;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = childInRead;
si.hStdOutput = childInRead;
si.hStdInput = childOutWrite;
si.dwFlags |= STARTF_USESTDHANDLES;
success = CreateProcess(NULL, applicationName, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!success) {
printf("Error creating child process \n");
}
else {
printf("Child process successfuly created \n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main()
{
printf("Parent process running.... \n");
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
SECURITY_ATTRIBUTES secAttr;
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
printf("Creating first pipe \n");
if (!CreatePipe(&parentInRead, &childInWrite, &secAttr, 0)) {
printf("\n error creating first pipe \n");
}
printf("Creating second pipe \n");
if (!CreatePipe(&childOutWrite, &childOutRead, &secAttr, 0)) {
printf("\n error creating second pipe \n");
}
if (!SetHandleInformation(parentInRead, HANDLE_FLAG_INHERIT, 0)) {
printf("\n parentInRead SetHandleInformation \n");
}
if (!SetHandleInformation(childInWrite, HANDLE_FLAG_INHERIT, 0)) {
printf("\n childInWrite SetHandleInformation \n");
}
childOutRead = GetStdHandle(STD_OUTPUT_HANDLE);
parentInRead = GetStdHandle(STD_INPUT_HANDLE);
printf("\n Creating child process..... \n");
CreateChildProcess();
for (;;){
printf("Inside for loop \n");
bSuccess = ReadFile(parentInRead, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
bSuccess = WriteFile(childInWrite, chBuf,
dRead, &dWritten, NULL);
if (!bSuccess) {
printf("error writing \n");
break;
}
}
return 0;
}
ChildTalk.cpp
#include <windows.h>
#include <stdio.h>
#include "pch.h"
#define BUFSIZE 4096
int main()
{
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE];
BOOL success = FALSE;
HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
printf("Child process running....");
if (stdIn == INVALID_HANDLE_VALUE || stdOut == INVALID_HANDLE_VALUE) {
ExitProcess(1);
}
for (;;) {
success = ReadFile(stdIn, chBuf, BUFSIZE, &dRead, NULL);
if (!success || dRead == 0) break;
success = WriteFile(stdOut, chBuf, dRead, &dWritten, NULL);
if (!success) break;
}
return 0;
}
EDIT1: There's no errors while running the code, the program stays at
bSuccess = WriteFile(childStdOut, chBuf,
dRead, &dWritten, NULL);
if (!bSuccess) {
printf("error writing");
break;
}
because it's waiting for input but when I type anything it print the "error writing" message. The message that I added to the child code "Child process running...." isnt printed
EDIT2: Changed code because i think the Handles were incorrect now it's printing "Inside for loop" but still no child process. Do I need to launch the console application of the child process first?
There are some errors in your code.
First,
childOutRead = GetStdHandle(STD_OUTPUT_HANDLE);
parentInRead = GetStdHandle(STD_INPUT_HANDLE);
the 2 handle get from CreatePipe, have been reseted to stdhandle, and lost the pipe handle.
Second,
CreatePipe(&childOutWrite, &childOutRead, &secAttr, 0);
should be CreatePipe(&childOutRead, &childOutWrite, &secAttr, 0)
Third,
the handle in SetHandleInformation() is not correct.
Well, let's return your question.
For the hiding of child process, its stdin/stdout handle are seted to pipe handle, so you are not able see the console, but it does run in the background.
I did some change with your example(for simplicity, asynchrony is not used):
Parent.cpp
#include <windows.h>
#include <iostream>
#include <stdio.h>
HANDLE childInRead = NULL;
HANDLE W1 = NULL;
HANDLE W2 = NULL;
HANDLE R2 = NULL;
HANDLE R1 = NULL;
#define BUFSIZE 4096
void CreateChildProcess() {
TCHAR applicationName[] = TEXT("ChildTalk.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL success = FALSE;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = W1;
si.hStdOutput = W1;
si.hStdInput = R2;
si.dwFlags |= STARTF_USESTDHANDLES;
success = CreateProcess(NULL, applicationName, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (!success) {
printf("Error creating child process \n");
}
else {
printf("Child process successfuly created \n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main()
{
printf("Parent process running.... \n");
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE] = "hello";
BOOL bSuccess = FALSE;
SECURITY_ATTRIBUTES secAttr;
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
printf("Creating first pipe \n");
if (!CreatePipe(&R1, &W1, &secAttr, 0)) {
printf("\n error creating first pipe \n");
}
printf("Creating second pipe \n");
if (!CreatePipe(&R2, &W2, &secAttr, 0)) {
printf("\n error creating second pipe \n");
}
if (!SetHandleInformation(R1, HANDLE_FLAG_INHERIT, 0)) {
printf("\n R1 SetHandleInformation \n");
}
if (!SetHandleInformation(W2, HANDLE_FLAG_INHERIT, 0)) {
printf("\n W1 SetHandleInformation \n");
}
printf("\n Creating child process..... \n");
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
CreateChildProcess();
for (;;) {
printf("Inside for loop \n");
//1. read from stdin
bSuccess = ReadFile(hStdIn, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//2. write to Pipe2
bSuccess = WriteFile(W2, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//3. read from Pipe1
bSuccess = ReadFile(R1, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//4. write to stdout
bSuccess = WriteFile(hStdOut, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
}
return 0;
}
And Its logical process:

C ++ Inter Process Communication using redirected Input and Output not work with release build

I'm working on a project which needed to create a child process and re-directed its input and output to the parent process.
I'm following this example on MSDN (link). Currently it works with debug build, but not release build. I can't figure it out why. Please help me
The coding of the child process as follow (very similar to the example):
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string>
#include <Iepmapi.h>
#include <Wininet.h>
#include <vector>
#define BUFSIZE 4096
using namespace std;
vector<wstring> subStringByString(wstring input, wstring delimiter)
{
int pos = input.find(delimiter);
vector<wstring> arr;
while (pos != wstring::npos)
{
wstring token = input.substr(0, pos);
arr.push_back(token);
input = input.substr(pos + delimiter.size(), input.size());
pos = input.find(delimiter);
}
arr.push_back(input);
return arr;
}
int main(void)
{
WCHAR chBuf[BUFSIZE];
DWORD dwRead, dwWritten;
HANDLE hStdin, hStdout;
BOOL bSuccess;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (
(hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE)
)
ExitProcess(1);
// Send something to this process's stdout using printf.
printf("\n ** This is a message from the child process. ** \n");
// This simple algorithm uses the existence of the pipes to control execution.
// It relies on the pipe buffers to ensure that no data is lost.
// Larger applications would use more advanced process control.
int rc = 0;
for (;;)
{
// Read from standard input and stop on error or no data.
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
wstring input = wstring(chBuf);
wstring delimiter = L" ";
vector<wstring> arr = subStringByString(input, delimiter);
int rc = 0;
if (!bSuccess || dwRead == 0)
break;
if (IESetProtectedModeCookie(arr[0].c_str(), arr[1].c_str(), arr[2].c_str(), INTERNET_COOKIE_THIRD_PARTY) != S_OK)
{
DWORD error = GetLastError();
rc = -27;
}
// Write to standard output and stop on error.
if (rc == 0 )
bSuccess = WriteFile(hStdout, L"0", dwRead, &dwWritten, NULL);
else
bSuccess = WriteFile(hStdout, L"-27", dwRead, &dwWritten, NULL);
if (!bSuccess)
break;
}
return 0;
}
Thanks,
Vinh
Parent process code:
std::wstring invoke(const std::wstring input)
{
if (!CreateChildProcess())
return L"error";
DWORD dwRead, dwWritten;
WCHAR chBuf[BUFSIZE] ;
const WCHAR* temp = input.c_str();
wcscpy(chBuf, temp);
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess) break;
}
CloseHandle(g_hChildStd_IN_Wr);
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess) break;
bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess) break;
}
//TerminateProcess(_processId, 0);
return std::wstring(&chBuf[0]);
}
bool CreateChildProcess(void)
{
SECURITY_ATTRIBUTES saAttr;
// 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 STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return false;
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
{
cleanUpHandle();
return false;
}
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
{
cleanUpHandle();
return false;
}
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
cleanUpHandle();
return false;
}
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
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;
// Create the child process.
HANDLE hToken = NULL;
HANDLE hNewToken = NULL;
bSuccess = OpenProcessToken(GetCurrentProcess(),
TOKEN_DUPLICATE |
TOKEN_ADJUST_DEFAULT |
TOKEN_QUERY |
TOKEN_ASSIGN_PRIMARY,
&hToken);
if (!bSuccess) {
return 0;
}
hNewToken = CreateLowLevelToken(hToken);
wa_wstring deploymentPath;
if (WAAPI_FAILED(ProcessUtils::getDeploymentPath(deploymentPath)))
{
return false;
}
wa_wstring path = deploymentPath + wa_wstring(PROCESS_PATH);
LPTSTR szCmdline = wstring_to_LPTSTR(path);
if (_processId != 0)
{
TerminateProcess(_processId, 0);
}
TCHAR szCmdline2[] = PROCESS_PATH;
bSuccess = CreateProcessAsUser(hNewToken, NULL, szCmdline2, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &piProcInfo);
DWORD error = GetLastError();
if (!bSuccess)
{
cleanUpHandle();
return false;
}
else {
_processId = piProcInfo.dwProcessId;
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
return true;
}
I found the problem.
In "debug build, the value of dwRead, dwWritten are trash value, however, the value of those in relase build are 0. Which cause the parent hang.
To fix this :
DWORD dwRead = -1, dwWritten = -1;

The application was unable to start correctly 0xc0000142 CreateProcessWithLogonW

I have a block of of code
if(!CreateProcessWithLogonW(
szUserName,
NULL,
szPassword,
LOGON_WITH_PROFILE,
L"C:\\Windows\\System32\\cmd.exe", // file to execute
NULL,
NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
)){
ReportError(L"Create Process");
}
ReportError is not invoked, but csrss.exe pops up with
What am I doing wrong?!
The username and password ARE correct.
the whole file:
// cmd.cpp : Defines the entry point for the console application.
//
#include <Windows.h>
#include <Lmcons.h>
#include <iostream>
#include <ctype.h>
#include <string>
#include <stdio.h>
#define winstring LPWSTR
#define stcas(x) static_cast<x>
#define INFO_BUFFER_SIZE 260
using namespace std;
void ReportError(LPCWSTR pszFunction, DWORD dwError = GetLastError())
{
wprintf(L"%s failed w/err 0x%08lx\n", pszFunction, dwError);
}
int main()
{
TCHAR un[UNLEN+1];
DWORD size = UNLEN + 1;
GetUserName(un, &size);
string una(un);
bool sys = !una.compare("SYSTEM");
/*
if(!sys) {
system("cls");
system("title Command Prompt");
system("cmd");
return 0;
}
*/
wchar_t szUserName[INFO_BUFFER_SIZE] = {};
wchar_t szPassword[INFO_BUFFER_SIZE] = {};
wchar_t *pc = NULL;
HANDLE hToken = NULL;
BOOL fSucceeded = FALSE;
BOOL logon = FALSE;
printf("Enter the username: ");
fgetws(szUserName, ARRAYSIZE(szUserName), stdin);
pc = wcschr(szUserName, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
cout << endl;
//string un(szUserName);
printf("Enter the password: ");
fgetws(szPassword, ARRAYSIZE(szPassword), stdin);
pc = wcschr(szPassword, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
if (!LogonUserW(szUserName, NULL, szPassword, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken))
{
ReportError(L"Logon");
goto Cleanup;
}
else logon = true;
HANDLE phToken = NULL;
BOOL dup = FALSE;
if(!DuplicateTokenEx(hToken, TOKEN_DUPLICATE|TOKEN_IMPERSONATE|TOKEN_QUERY, NULL, SecurityImpersonation, TokenPrimary, &phToken)){
ReportError(L"DUPLICATE TOKEN");
}
else dup = TRUE;
// Impersonate the logged on user.
if (!ImpersonateLoggedOnUser(phToken))
{
ReportError(L"imp");
goto Cleanup;
}
fSucceeded = true;
Cleanup:
// Clean up the buffer containing sensitive password.
LPTSTR szCmdline[] = {"cmd"};
STARTUPINFOW si;
PROCESS_INFORMATION pi;
TCHAR uni[UNLEN+1];
DWORD sizei = UNLEN + 1;
GetUserName(uni, &sizei);
string unai(uni);
cout << unai << endl;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
system("pause");
// If the impersonation was successful, undo the impersonation.
if (fSucceeded && logon)
{
system("cls");
system("title Command Prompt");
//system("cmd");
if(!CreateProcessWithLogonW(
szUserName,
NULL,
szPassword,
LOGON_WITH_PROFILE,
L"cmd.exe", // file to exec
NULL,
NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
)){
ReportError(L"Create Process");
}
if (!RevertToSelf())
{
ReportError(L"Undo Imp");
}
}
SecureZeroMemory(szPassword, sizeof(szPassword));
system("pause");
}
The code you posted works for me if I skip the calls to LogonUser, DuplicateToken and ImpersonateLoggedOnUser. You don't want to impersonate another user in order to call CreateProcessWithLogon since it basically does that for you, so just remove all that logic to do the impersonation.
Impersonating a user could also cause your program to be unable to launch an application at all if the target application requires a higher level of privilege than the user has.