I want to start "telnet" through cmd.exe and write the result of this command execution into file. When I run "ipconfig" command I get all information I need, but after running of "telnet" command I get just empty file.
Here is my code:
#include "windows.h"
#include "iostream"
void SaveResult(const wchar_t *fileName, const wchar_t *commandLine)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFile(fileName, FILE_WRITE_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return;
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { sizeof(si) };
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = NULL;
si.hStdError = h;
si.hStdOutput = h;
wchar_t *writable_cmdline = _wcsdup(commandLine);
BOOL success = CreateProcess(NULL, writable_cmdline,
NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
bool finished = false;
//wait for 1 second
for (int i = 0; i < 10; i++)
{
if (WaitForSingleObject(pi.hProcess, 100) <= 0)
{
finished = true;
break;
}
}
if (success)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CloseHandle(h);
free(writable_cmdline);
if (!finished)
printf("Process didn't finish\n");
}
int main()
{
SaveResult(L"telnet.txt", L"C:\\windows\\system32\\cmd.exe /c telnet test.com");
SaveResult(L"ipconfig.txt", L"C:\\windows\\system32\\cmd.exe /c ipconfig");
return 0;
}
Have you run telnet command directly in cmd console? Is there anything output? if so, The empty file may because the buffer has not been refreshed to the file. As#David pointed out,
you just wait for 1s, but if there be more than 1s to connect, then you will close the handler without refresh the buffer. Try to add the FlushFileBuffers(h) before CloseHandle(h).
For WaitForSingleObject, you could try WaitForSingleObject(pi.hProcess, INFINITE), wait until the telnet process exit.
Related
Other than creating pipes for standard in/out/error, is there any easy way in Win32 API to redirect those handles to the parent process? If the child process has a console window, the output/error seems to go to it, rather than the parent's console window, even when the handles are inherited. What I'd really like is for a windowless child process to send its out/error to the parent's console (easily?). Advice appreciated.
If you only want to display the stdout and stderr of your child process to the console of the parent process, you can use AttachConsole(ATTACH_PARENT_PROCESS) in the child process to attach it to the console of the parent process. Or In parent, specify dwCreationFlags of CreateProcess as 0, according to the Creation of a Console:
By default, a console process inherits its parent's console.
(there is no guarantee that input is received by the process for which it was intended. Make sure you don' t need to use the stdin for child process)
However, this method cannot interact with the parent process. If the parent process needs to obtain these output data, it still needs to use Interprocess Communications to send the data to the parent process.
This method allows the child process and the parent process to use the same console, but the parent process cannot directly obtain the output data of the child process. If the parent process needs to get the data, it still needs to use Interprocess Communications to send the data to the parent process.
EDIT:
Here is the sample
child:
#include <windows.h>
#include <iostream>
int main(VOID)
{
AttachConsole(ATTACH_PARENT_PROCESS);
int i = 5;
while (i--)
{
printf("printf\n");
std::cout << "child " << i << std::endl;
fflush(stdout);
Sleep(1000);
}
return 0;
}
parent:
#include <windows.h>
#include <iostream>
int main()
{
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION pi = {};
WCHAR cmd[] = L"Path\\child.exe";
SECURITY_ATTRIBUTES se = {};
se.nLength = sizeof(SECURITY_ATTRIBUTES);
se.bInheritHandle = true;
se.lpSecurityDescriptor = NULL;
HANDLE hFile = CreateFileW(L"test.txt", GENERIC_WRITE, FILE_SHARE_READ, &se, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
BOOL ret = SetStdHandle(STD_OUTPUT_HANDLE, hFile);
printf("parent1\n");
ret = CreateProcessW(cmd, NULL, 0, 0, 1, 0, 0, 0, &si, &pi);
std::cout << "parent2 " << std::endl;
WaitForSingleObject(pi.hProcess,INFINITE);
system("pause");
}
result:
Another sample:
child
int main(VOID)
{
//AttachConsole(ATTACH_PARENT_PROCESS);
int i = 5;
while (i--)
{
printf("printf\n");
std::cout << "child " << i << std::endl;
fflush(stdout);
Sleep(1000);
}
return 0;
}
parent
int main()
{
SECURITY_ATTRIBUTES se = {};
se.nLength = sizeof(SECURITY_ATTRIBUTES);
se.bInheritHandle = true;
se.lpSecurityDescriptor = NULL;
HANDLE hFile = CreateFileW(L"test.txt", GENERIC_WRITE, FILE_SHARE_READ, &se, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
BOOL ret = SetStdHandle(STD_OUTPUT_HANDLE, hFile);
printf("parent1\n");
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi = {};
WCHAR cmd[] = L"Path\\child.exe";
CreateProcessW(cmd, NULL, 0, 0, 1, 0, 0, 0, &si, &pi);
std::cout << "parent2 " << std::endl;
WaitForSingleObject(pi.hProcess, INFINITE);
system("pause");
}
In my project I have two applications, one is Pipe Server and Pipe Client(Slave).
I am trying to send text via pipe to display it on client's console. Thus effectively creating disposable consoles.
I have tested the code by manually running the server first and then client. It runs perfectly. Then I added some code in the constructor of Server to invoke Slave.exe with pipename as arguments however the console of Slave disappears after couple seconds.
Slave's Constructor calls this function:
int OpenNamedPipe(std::string pipename)
{
pipeurl += pipename;
hPipe = CreateNamedPipe(
pipeurl .c_str(), // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // Datatype Byte
PIPE_WAIT, // blocking mode
1, // max. instances
outputBuffer, // output buffer size
inputBuffer, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
try
{
Throw_Last_Error("CreateNamedPipe failed");
}
catch (const std::runtime_error err)
{
std::cout << "Runtime Error: " << err.what();
return 0;
}
}
int timeout = 100000;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
int retnVal = CreateProcessA("Slave.exe", (LPSTR)pipename.c_str(), NULL, NULL, NULL, DETACHED_PROCESS, NULL, NULL, &si, &pi);
if (!retnVal)
{
retnVal = GetLastError();
}
if (!ConnectNamedPipe(hPipe, NULL))
{
if (!GetLastError() == ERROR_PIPE_CONNECTED)
{
try
{
Throw_Last_Error("Error while connecting to named pipe.");
}
catch (std::runtime_error err)
{
std::cout << "GLE= " << GetLastError();
Block();
return 0;
}
}
}
std::cout << "Connected to pipe.\n";
return 0;
}
In Client's main program:
int main(int argc, char *argv[])
{
AllocConsole();
std::string argstr = " ";
argstr = argv[1];
PipeClient pc(argstr);
pc.Update();
system("pause");
return 0;
}
Now I need both Server's console and Client's console to remain open for further testing but when Server is waiting for the Slave to connect to pipe, Slave's console and process closes, which shouldn't happen as I have paused it before it can return.
Edit: Pipe Client object constructor:
PipeClient(std::string pipename)
{
pipeName = pipeName + pipename;
Connect();
if (hPipe != INVALID_HANDLE_VALUE || GetLastError() != ERROR_PIPE_BUSY)
{
std::cout << "Created Pipe, GLE=" << GetLastError();
}
if (hPipe == INVALID_HANDLE_VALUE)
{
ThrowLastError("Failed to connect to named pipe.");
}
}
int Connect()
{
while (true)
{
WaitNamedPipeA(pipeName.c_str(), NMPWAIT_WAIT_FOREVER);
hPipe = CreateFileA(
pipeName.c_str(),
GENERIC_READ |
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hPipe != INVALID_HANDLE_VALUE || GetLastError() != ERROR_PIPE_BUSY)
{
std::cout << "Created Pipe, GLE=" << GetLastError();
break;
}
}
return 0;
}
Class Fields:
DWORD inputBuffer = 256;
DWORD outputBuffer = 256;
HANDLE hPipe;
std::string pipeName = "\\\\.\\pipe\\";
char * testpipename = "\\\\.\\pipe\\namedpipe";
Github repo:https://github.com/BhayanakMoth2/PipedConsole
So I fixed the problem, I was not using the CreateProcess function properly.
This should be the fixed function call:
std::string cmd = "Slave.exe " + pipename;
int retnVal = CreateProcessA("Slave.exe", (LPSTR)cmd.c_str(), NULL, NULL, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
I misread the documentation. And the reason Slave.exe was crashing because the arguments were not being passed properly and so when it reached:
argstr = argv[1]
it crashed silently. The second argument in CreateProcessA() fixes this problem by properly passing the arguments.
I want to show the stdout of commandline program in MFC edit control. So I start a worker thread by AfxBeginThread to update the UI by PostMessage (this part works great), and the worker thread communicates with the commandline child process by a pipe. But my worker thread cannot read anything from the pipe (ReadFile always return FALSE) and my worker cannot quit even if the child process quits. So please help me.
Here is my code.
create child process part:
BOOL CMFCApplication3Dlg::execCmd(LPCSTR pCmdArg)
{
//ASSERT(s_hCmdProcess == NULL);
if (s_hCmdProcess != NULL)
{
return FALSE;
}
STARTUPINFO si; // specifies startup parameters for child process.
ZeroMemory(&si, sizeof(STARTUPINFO));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; // STARTF_USESTDHANDLES is Required.
si.hStdOutput = s_cmdout_ChildSide; // Requires STARTF_USESTDHANDLES in dwFlags.
si.hStdError = s_cmdout_ChildSide; // Requires STARTF_USESTDHANDLES in dwFlags.
// si.hStdInput remains null.
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
// Create the child process.
BOOL result = CreateProcess(
NULL,
(LPSTR)pCmdArg, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // TRUE=handles are inherited. Required.
CREATE_NEW_CONSOLE, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // __in, STARTUPINFO pointer
&pi); // __out, receives PROCESS_INFORMATION
if (result)
{
s_hCmdProcess = pi.hProcess;
s_hCmdProcessThread = pi.hThread;
}
return result;
}
create pipe part:
LRESULT CMFCApplication3Dlg::createPipe()
{
ASSERT(s_cmdout_ChildSide == NULL && s_cmdout_MainSide == NULL);
if (s_cmdout_ChildSide != NULL || s_cmdout_MainSide != NULL) return FALSE;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
return CreatePipe(&s_cmdout_MainSide, &s_cmdout_ChildSide, &saAttr, 0);
}
the worker thread part:
void CMFCApplication3Dlg::startWorkThread()
{
if (s_hThread == NULL)
{
if (s_hThreadEvent != NULL) CloseHandle(s_hThreadEvent);
s_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//s_hThread = AfxBeginThread(TestThreadProc, this);
s_hThread = AfxBeginThread(ReadCmdOutProc, this);
}
}
UINT ReadCmdOutProc(LPVOID pParam)
{
CWnd *hDlg = (CWnd *)pParam;
//start the child process
BOOL result = CMFCApplication3Dlg::execCmd("ping 127.0.0.1 -n 99");
ASSERT(CMFCApplication3Dlg::s_hCmdProcess != NULL);
for(;;)
{
DWORD dwRetVal_cmd;
//wait for child process quit.
dwRetVal_cmd = WaitForSingleObject(CMFCApplication3Dlg::s_hCmdProcess,100);
DWORD dwRead = 0;
CHAR chBuf[4096];
BOOL readResult = ReadFile(CMFCApplication3Dlg::s_cmdout_MainSide, chBuf, 4096, &dwRead, NULL);
if (readResult)
{
chBuf[dwRead] = 0;
TRACE("read: %s\n",chBuf);
CString* cmdread = new CString(chBuf, dwRead);
//send ui thread update message.
MsgWrapper::Post(hDlg, WM_APP + 11, (WPARAM)cmdread);
}
if (dwRetVal_cmd != WAIT_TIMEOUT)
{
TRACE("thread quit\n");
//release the resource
CMFCApplication3Dlg::releaseCmd();
GetExitCodeThread(CMFCApplication3Dlg::s_hThread, &dwExitCode);
AfxEndThread(dwExitCode);
CMFCApplication3Dlg::s_hThread = NULL;
break;
}
}
}
I want to get the primary token so that I can get the access of OpenInputDesktop() and do my necessary things.
I browsed all over the sites for help and found the conclusive code as below but I got an error on calling DuplicateTokenEx() is 998 which means invalid access to memory location.
HANDLE GetCurrentUserToken()
{
HANDLE currentToken = 0;
PHANDLE primaryToken = 0;
unsigned int winlogonPid = 0;
int dwSessionId = 0;
PHANDLE hUserToken = 0;
PHANDLE hTokenDup = 0;
PWTS_SESSION_INFO pSessionInfo = 0;
DWORD dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&pSessionInfo, &dwCount);
//TestLog("Error on WTSEnumerateSessions(): %d",GetLastError());
int dataSize = sizeof(WTS_SESSION_INFO);
for (DWORD i = 0; i < dwCount; ++i)
{
WTS_SESSION_INFO si = pSessionInfo[i];
if (WTSActive == si.State)
{
dwSessionId = si.SessionId;
break;
}
}
WTSFreeMemory(pSessionInfo);
array<Process^>^localByName = Process::GetProcessesByName( "winlogon" );
for (int i=0;i<localByName->Length;i++)
{
Process ^ p1 = (Process^)(localByName->GetValue(i));
if ((unsigned int)p1->SessionId == dwSessionId)
{
winlogonPid = (unsigned int)p1->Id;
}
}
// obtain a handle to the winlogon process
HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
TestLog("Error on OpenProcess():",GetLastError());
// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ¤tToken))
{
TestLog("Error on OpenProcessToken():",GetLastError());
CloseHandle(hProcess);
return false;
}
BOOL bRet ;
// bRet = DuplicateTokenEx(currentToken,
// MAXIMUM_ALLOWED /*TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS*/,
// NULL/*0*/,
// SecurityImpersonation, TokenImpersonation, primaryToken);
bRet = DuplicateTokenEx(currentToken,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityImpersonation,
TokenPrimary, primaryToken);
TestLog("Error on DuplicateTokenEx():",GetLastError());
TestLog("return value of DuplicateTokenEx()",bRet);
int errorcode = GetLastError();
if (bRet == false)
{
return 0;
}
return primaryToken;
}
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
TestLog("**Start TestLaunchExeOneTime**",0);
HANDLE hTokenNew = NULL, hTokenDup = NULL;
HMODULE hmod = LoadLibrary(L"kernel32.dll");
hTokenDup = GetCurrentUserToken();
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof( STARTUPINFO );
si.lpDesktop = L"winsta0\\default";
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
HMODULE hModule = LoadLibrary(L"Userenv.dll");
if(hModule )
{
if(CreateEnvironmentBlock(&pEnv,hTokenDup,FALSE))
{
//WriteToLog("CreateEnvironmentBlock Ok");
dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
TestLog("Error on CreateEnvironmentBlock():",GetLastError());
pEnv = NULL;
}
}
//
if ( !CreateProcessAsUser( hTokenDup,
NULL,
L"C:\\temp\\DesktopDuplicationmilliseconds.exe",
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&si,
&pi
))
{
}
else
{
TestLog("Error on CreateProcessAsUser():",GetLastError());
// printf("error : %d",GetLastError());
}
return 0;
}
You haven't allocated any memory for the primary token. The primaryToken variable is a pointer to a handle, but you haven't actually pointed it to anything. (You've also declared GetCurrentUserToken as a function that returns a handle, but are actually returning a pointer to a handle.)
You need to either explicitly allocate the memory for the handle:
primaryToken = malloc(sizeof(HANDLE));
[...]
return *primaryToken;
or, more sensibly, define primaryToken as a HANDLE rather than a pointer and pass a reference to it in the appropriate place:
HANDLE primaryToken;
[...]
bRet = DuplicateTokenEx(currentToken,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityImpersonation,
TokenPrimary, &primaryToken);
I am seeking example code:
For a service calls CreateProcessAsUser() I want the process to run in the user's session, not session 0
thus far the created process is only running like a service in session 0
This was stripped from some old code that launched a console app from a service. It worked under NT4 but I haven't tested it with a modern version of Windows so can't guarantee it will work as it did on NT4.
EDIT: No, that's not going to work as-is. You need to add the code found here to create a desktop, set the SID, etc.
if (!LogonUser(userId,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hUserToken))
{
return GetLastError();
}
if (!ImpersonateLoggedOnUser(hUserToken))
{
DWORD rc = GetLastError();
CloseHandle(hUserToken);
return rc;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
rc = CreateProcessAsUser(hUserToken, // user token
0, // app name
"foo.exe", // command line
0, // process attributes
0, // thread attributes
FALSE, // don't inherit handles
DETACHED_PROCESS, // flags
0, // environment block
0, // current dir
&si, // startup info
&pi); // process info gets put here
if (!rc)
{
DWORD rc = GetLastError();
RevertToSelf();
CloseHandle(hUserToken);
return rc;
}
RevertToSelf();
CloseHandle(hUserToken);
return 0;
I know this is an ancient post but I happen to be working on this so here's some code that works for me.
Determine the session ID of the currently logged-on user
DWORD GetCurrentSessionId ()
{
WTS_SESSION_INFO *pSessionInfo;
DWORD n_sessions = 0;
BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
if (!ok)
return 0;
DWORD SessionId = 0;
for (DWORD i = 0; i < n_sessions; ++i)
{
if (pSessionInfo [i].State == WTSActive)
{
SessionId = pSessionInfo [i].SessionId;
break;
}
}
WTSFreeMemory (pSessionInfo);
return SessionId;
}
Launch process as the currently logged-on user
bool LaunchProcess (const char *process_path)
{
DWORD SessionId = GetCurrentSessioId ();
if (SessionId == 0) // no-one logged in
return false;
HANDLE hToken;
BOOL ok = WTSQueryUserToken (SessionId, &hToken);
if (!ok)
return false;
void *environment = NULL;
ok = CreateEnvironmentBlock (&environment, hToken, TRUE);
if (!ok)
{
CloseHandle (hToken);
return false;
}
STARTUPINFO si = { sizeof (si) } ;
PROCESS_INFORMATION pi = { } ;
si.lpDesktop = "winsta0\\default";
// Do NOT want to inherit handles here
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT;
ok = CreateProcessAsUser (hToken, process_path, NULL, NULL, NULL, FALSE,
dwCreationFlags, environment, NULL, &si, &pi);
DestroyEnvironmentBlock (environment);
CloseHandle (hToken);
if (!ok)
return false;
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return true;
}