Windows -- inherit console file handle in child process - c++

I have a Windows application (subsystem=windows, not a console application). I am creating a console in that application, then creating a child process. When I create the console, I make a console file handle inheritable (see below). When I create the child process, I set the bInheritHandles argument of CreateProcess to TRUE. I want the child process to be able to read to and write from the console, but I get error 0x06, invalid handle.
I do the following:
1) AllocConsole();
2) CreateFile("CONIN$", ...), CreateFile("CONOUT$", ...) or
CreateConsoleScreenBuffer(...) with the following SetConsoleActiveScreenBuffer(...). Always have SecurityAttributes with bInheritHandle=TRUE.
but see bInheritHandle=1.
3) CreateProcess(NULL, GetCommandLine(), NULL, NULL,
TRUE, /* inherit handles */ 0, NULL, NULL, &sinfo, &child);
In child process:
1) _open_osfhandle((intptr_t)console_handle, 0) gives me -1 and GetLastError() returns error 0x06 -- "Invalid handle".
The child process is a copy of its parent, so both processes have the same subsystem: windows (not console application).
I have checked that other file handles are inherited normally and can be used with fdopen(_open_osfhandle(file_handle), ...). For example, it works for a text file. But it doesn't work for a console handle.
What I am doing wrong?

Yes, arx (see comments above) absolutelly right: console file handle is "fake" handle as it not exists at OS level (and can not be inherited). This type of file handle is only known for Win32 api libraries (kernel32.dll) and I/O requests processed at this level only. Windows doesn't have real console files, as virtual terminals in Unix (except of Windows8). :-( So, I need to change subsystem of my application from "windows" type to "console" and then application can use pre-allocated console (but file handle still can't be inherited -- need to reopen "CONOUT$" in child process...)

Related

CTRL_BREAK_EVENT to a GUI application will compromise parent process

I created a console application with code beneath.
Those code will just start a child process of the explorer.exe then send a CTRL_BREAK_EVENT to it which is expected to be a NO-OP since the child explorer process is not a console application. Then using TerminateProcess to kill the child process which had no effect either but it's ok too.
The problem is, after those steps, the whole console event system for this parent process is broken - I cannot shutdown this console application by clicking X on the console window nor by pressing CTRL-C.
#include <Windows.h>
#include <iostream>
int main() {
const LPWSTR exe = const_cast<LPTSTR>(TEXT("C:\\Windows\\explorer.exe"));
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
CreateProcess(exe,
exe,
NULL,
NULL,
FALSE,
CREATE_NEW_PROCESS_GROUP,
NULL,
NULL,
&si,
&pi);
Sleep(1000);
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
Sleep(1000);
TerminateProcess(pi.hProcess, 0);
Sleep(1000);
while (true) {
Sleep(1000);
std::cout << "Hello World!\n";
}
}
I do this because I'm making an application hosting platform that people can upload and run applications on a private cloud. We don't know what kinds of EXEs users will upload. If user uploads a console application that relying on CTRL_BREAK_EVENT for a graceful exit, we want to make it possible, so we try CTRL_BREAK_EVENT first. If the program doesn't shutdown in time after a while (for example a GUI program) we will kill it, just like the code above did. We don't want it to break the whole console control event system if we do this on a GUI application since we rely on it to manage lifecycles other services which run as children processes.
By the way, if we created the process with creation flag "DETACHED_PROCESS", the breaking of console control event system will break too even the EXE is a console application.
Any idea to workaround this?
Many thanks!
This is a workaround to prevent this issue from occuring which I use currently in our production, for anyone who is facing the same issue.
Use QueryFullProcessImageName to find the PE image file path of the child process.
Find the subsystem of this PE image. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
If the subsystem is GUI, don't use GenerateConsoleCtrlEvent on it.
If you are not familiar with PE format and don't want to read the doc, just do this:
Read at 0x3c as UINT16 of the PE file to find the offset of PE header.
Read UINT8 at PE_HEADER + 0x18 + 0x44, and 0x02 means a GUI application.
I would like to not mark this as an answer since I'm hoping some more elegant way.

How do I make child processes in Win32 so that they show up as nested in Task Manager?

I have a Win32 C++ application. I'm trying to launch one or several child processes with CreateProcess. I want the children to close when the parent does.
I achieved this by creating a job and enabling JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE:
HANDLE hJob = CreateJobObject(NULL, NULL);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo;
ZeroMemory(&extendedInfo, sizeof(extendedInfo));
extendedInfo.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(
hJob, JOBOBJECTINFOCLASS::JobObjectExtendedLimitInformation,
&extendedInfo, sizeof(extendedInfo));
Then adding the current (parent) and created (child) process to this job:
// assign parent to job
AssignProcessToJobObject(hJob, GetCurrentProcess());
// launch child with no inherited handles
PROCESS_INFORMATION procInfo;
ZeroMemory(&procInfo, sizeof(procInfo));
STARTUPINFOA startInfo;
ZeroMemory(&startInfo, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
startInfo.dwFlags |= STARTF_USESTDHANDLES;
bool success = CreateProcessA(NULL,
"test.exe", // command line
NULL, // process security attributes
NULL, // primary thread security attributes
FALSE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&startInfo, // STARTUPINFO pointer
&procInfo); // receives PROCESS_INFORMATION
// assign child to job
AssignProcessToJobObject(hJob, procInfo.hProcess);
This works, but the parent app and the child app (main.exe and test.exe) show up as two unrelated processes in the task manager:
(Even though closing main.exe will close test.exe).
What am I doing differently than, say, Microsoft Teams or Chrome, which both have nested processes?
Exactly what Task manager is doing is not documented.
In Windows 8 it does not group child processes, it only organizes based on a process having a window or by being "special".
How does Task Manager categorize processes as App, Background Process, or Windows Process?:
These are terms that Task Manager simply made up. The system itself doesn’t really care what kind of processes they are.
If the process has a visible window, then Task Manager calls it an “App”.
If the process is marked as critical, then Task Manager calls it a “Windows Process”.
Otherwise, Task Manager calls it a “Background Process”.
(I don't believe this is 100% accurate, it clearly knows about services and I suspect it might hard-code some names)
In Windows 10 it tries harder to group things together but I don't exactly know what it is doing.
It is often (but not always) able to tie the conhost.exe child to its parent console application.
The new fancy store/packaged versions of Notepad and Paint have all their processes in a single group. The same does not happen with Notepad2 even though it has a Application Model ID set. Neither does it apply to Wordpad (even when one is a child of the other). I also tried setting an AMUI in a little test application and neither process wide AMUI nor per-HWND AMUI seems to trigger the grouping.
A job object does not seem to enable grouping.
Depending on your version, Edge might use a special API to tell Task manager about its processes.
In conclusion, I don't know what exactly what it is looking for but Packaged applications and App Containers seem to often trigger it.
To make it visible as a child process in the task manager, you have to set the CreateNoWindow property to false and UseShellExecute property to false in the StartInfo property before starting the process.
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
This tells the process to create a new window for the child process and to use the command line to start the process instead of using the shell to start the process.
This will now create the child process as a new visible process in the task manager, and it will be shown under the tree of the main process.

Creating a command interpreter process and writing to it

I have been trying to create a cmd.exe from a c++-program and then being able to execute commands from the program inside the cmd.exe, so I can view the results of the operation in the cmd.exe. CreateProcessA() is the function I used:
_STARTUPINFOA CMD_StartupInfo = {};
CMD_StartupInfo.cb = sizeof(_STARTUPINFOA);
_PROCESS_INFORMATION CMD_ProcessInfo = {};
CreateProcessA("C:\\Windows\\System32\\cmd.exe",
0,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&CMD_StartupInfo,
&CMD_ProcessInfo);
The cmd.exe opened and it looked to be working. Then I thought I'd use PostMessage() to pass WM_KEYDOWN messages to the cmd.exe to execute a command. For this function I needed a window handle and it turns out that the process of getting a window handle from a process/thread ID which is obtained from the CreateProcessA() is f***ing complicated. You have to loop through all active windows with EnumWindows() that requires a callback function which needs to check whether or not the process ID of the current window is the same as the process ID of the process I created.
I did that and it turned out that none of the windows the function iterated over were my window.
So I presume that means that the thread of this process which I created has no windows but I can see a beautiful-looking cmd.exe right there. What is going on? Is this not considered a window? If not, how do I pass messages to the cmd.exe and if yes, how do I get the window handle?
Any help is appreciated (also ideas to do the entire cmd-thing differently).

How can I create a elevated child process with inherited handles from his parent

I'm trying to create a child process with elevated rights in Windows (by trigerring a UAC prompt) and get this child process to inherit his parent STDOUT handle to be able to output back to the parent's console.
Additionnal Infos:
Win32 APIs
Im currently using ShellExecuteEx() with a ShellExecuteInformation that specifies a "runas" verb to trigger the UAC but any other solution that can create a elevated copy of the current running program (The child process is the same executable as the parent) but beeing able to output back to the parent's console.
When you use ShellExecuteEx with runas - The function CreateProcessAsUserW is called not from your process, but from svchost.exe -k netsvcs where appinfo.dll is running. And bInheritHandles is hardcoded to be FALSE - so
your process does not inherit any handles in this case. for making handles inheritable the only way is a direct call to CreateProcessAsUserW with a primary elevated token. But from where you got this token ? ok, you can run self exe second time as elevated (with runas) and duplicate elevated token back to original not-elevated process. but anyway (even with elevated token) - you need SE_ASSIGNPRIMARYTOKEN_NAME privilege for call CreateProcessAsUser. but you have no this privilege.
Otherwise you could if you found some exploit.
The process handle, returned by ShellExecuteEx have no PROCESS_DUP_HANDLE access - So you can't use it for manual handle duplication later.
So in general - This is impossible by design

CreateProcess with new console window, but override some std i/o handles

If you use CreateProcess with the flag CREATE_NEW_CONSOLE, the new process has its standard input, output, and error handles directed to the new console window. If you want to override the I/O streams, you can do so by setting the handles in STARTUPINFO fields hStdOutput, hStdInput, and hStdError and setting the flag STARTF_USESTDHANDLES.
But what if you want to override only one of the handles? For example, I might want to redirect stderr to a file while leaving the stdout and stdin connected to the new console window.
The STARTF_USESTDHANDLES flag tells CreateProcess to replace all of the handles instead of connecting them to the ones for the new console window. So it seems we must provide all three handles. Obviously I can set hStdError to the handle of the log file, but what values should be used for hStdInput and hStdOutput?
I tried using NULL, which seems to work on Windows 8.1, but it doesn't work on Windows 7.
I also thought about creating a console window first, and then calling CreateProcess with handles to the new console window's buffers (and omitting the CREATE_NEW_CONSOLE flag). Unfortunately, the parent process is also a console application, and it seems a console application cannot create a second console window.
According to this MSDN Support article:
How to spawn console processes with redirected standard handles
If the parent process only wishes to redirect one or two standard handles, specifying GetStdHandle() for the specific handles causes the child to create the standard handle as it normally would without redirection. For example, if the parent process only needs to redirect the standard output and error of the child process, then the hStdInput member of the STARTUPINFO structure is filled as follows:
hStdInput = GetStdHandle(STD_INPUT_HANDLE);
According to the GetStdHandle() documentation:
STD_INPUT_HANDLE
(DWORD)-10
The standard input device. Initially, this is the console input buffer, CONIN$.
STD_OUTPUT_HANDLE
(DWORD)-11
The standard output device. Initially, this is the active console screen buffer, CONOUT$.
STD_ERROR_HANDLE
(DWORD)-12
The standard error device. Initially, this is the active console screen buffer, CONOUT$.
...
The standard handles of a process may be redirected by a call to SetStdHandle, in which case GetStdHandle returns the redirected handle. If the standard handles have been redirected, you can specify the CONIN$ value in a call to the CreateFile function to get a handle to a console's input buffer. Similarly, you can specify the CONOUT$ value to get a handle to a console's active screen buffer.
Attach/detach behavior
When attaching to a new console, standard handles are always replaced with console handles unless STARTF_USESTDHANDLES was specified during process creation.
If the existing value of the standard handle is NULL, or the existing value of the standard handle looks like a console pseudohandle, the handle is replaced with a console handle.
When a parent uses both CREATE_NEW_CONSOLE and STARTF_USESTDHANDLES to create a console process, standard handles will not be replaced unless the existing value of the standard handle is NULL or a console pseudohandle.
So, if the parent process's STDIN has NOT been redirected, GetStdHandle(STD_INPUT_HANDLE) will return either NULL or a pseudo-handle that refers to CONIN$. When that value is passed to the child process via STARTUPINFO, the child process will receive a console handle for the STDIN of whichever console it happens to be running in. On the other hand, if the parent process's STDIN has been redirected, GetStdHandle(STD_INPUT_HANDLE) will return an actual handle to a real file/pipe/etc, which the child will inherit and access.
The same applies to STDOUT and STDERR.
So, if you want to redirect the child's STDIN/OUT/ERR handles, you have to set hStdInput/Output/Error to your own handles. If you want the child to receive default handles, use GetStdHandle() and let CreateProcess() decide what kind of handles the child receives based on whether the parent is itself being redirected or not.
On Windows 7, at least, the documentation here (as quoted by the existing answers) is misleading. The recommended approach only works if the parent process does not have redirected input.
This is the actual behaviour, as observed on my machine:
When a process opens a handle to CONOUT$ (for example) the returned handle always has the same numeric value (on my machine, the handle for CONOUT$ is always 7) unless that handle already exists.
So if you are a console process, and you were not launched with redirected output, your standard output handle is 7. If you open another handle to CONOUT$, it will have a different value. If you close handle 7, and then open a handle to CONOUT$, you'll get 7 again.
When a console process is launched, a handle to CONOUT$ with the magic value of 7 is usually present in the child process (regardless of redirection). If you have used STARTF_USESTDHANDLES, then the child's standard output will go to the console if and only if you specify 7 as the standard output handle. If you specify a handle to CONOUT$ that has any other value, it won't work.
Sometimes when a console process is launched with standard output redirected, there is no handle to CONOUT$ present in the child process. In particular, this happens when cmd.exe launches a console process with standard output redirected. So far, I have been unable to figure out what combination of parameters to use with CreateProcess to make this happen.
The behaviour of CONIN$ is analogous; on my machine, the magic value for CONIN$ is 3. (I haven't investigated how standard error works.)
The upshot is that I do not believe it is safe to make use of this behaviour unless you have complete control over the way the parent process was launched, because unless the standard handles already point to the console there is no reliable way of obtaining a handle to the console with the correct magic value.
Instead, launch a proxy process with CREATE_NEW_CONSOLE and without STARTF_USESTDHANDLES. From that process, since you know how it was launched, you know the standard handles will have the correct magic values, so it is safe to specify them as the handles for a child process.
If i interpreted this documentation correctly, you should use GetStdHandle(STD_INPUT_HANDLE) and GetStdHandle(STD_OUTPUT_HANDLE) for the other two handles.