CTRL_BREAK_EVENT to a GUI application will compromise parent process - c++

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.

Related

Terminating Process in Windows 10 OS

Background Information: I'm developing an Windows 10 app. Within my app, some outputs are displayed onto a window console requiring a new process.
Code Snippet:
PROCESS_INFORMATION pi;
STARTUPINFO si;
// CreateProcess is defined in processthreadsapi.h
BOOL newProcess = CreateProcess (
0, pszCmdLine, 0, 0, FALSE, DEBUG_THIS_ONLY_PROCESS, 0, 0, &si, &pi
);
In the above code snippet, pszCmdLine can be "cmd.exe /c dir", "cmd.exe /c ipconfig", or essentially any Windows terminal cmd.
Problem: On the front end, a new cmd console is generated and the user is able to terminate it. However, it's kept alive in the background. I've attached snips below:
Before Process Accumulating:
In the above snip, it shows "cmd.exe /c dir" being executed and outputted into a cmd console.
After Processes Accumulates:
In the above snip, it shows how I launched the same cmd 4 times and closed it 3 times (i.e. the counter is "(4)" including the main process).
Real World Problem: This is essentially consuming memory and has the ability to degrade performance.
Initial Curiosity: Shouldn't the Windows OS handle the killing of this process when the user clicks the "x"?
Has anyone faced a similar issue before?
Solution:
Change DEBUG_ONLY_THIS_PROCESS to 0 or CREATE_NEW_CONSOLE.
My Thoughts:
I suppose the idea behind DEBUG_ONLY_THIS_PROCESS is to leave the window handle open for the user to observe the output whereas CREATE_NEW_CONSOLE allocates and frees the memory as needed.

How to start an application as a child of a newly created explorer process?

I'm working on an application that resembles a kiosk. After the application starts, it creates a new desktop with limited capabilities. Using a key combo I can move back and forth between desktops. In order to inform the user about the desktop it's currently using, or any other information I've created an application, which is displaying balloon messages is System Tray area.
In the newly created desktop, I start an explorer.exe using CreateProcess function, and providing the new desktop thru a STARTUPINFO structure, and I'm returning a handle for the process in a PROCESS_INFORMATION structure.
Using the same technique I'm trying to start the icon tray application in the new desktop , providing the new desktop in the STARTUPINFO structure. The trouble is that, according to the task manager, the application is running, but the tray icon is not displayed.
My intuition says that in the new desktop, the icon is not shown because it's not a child of the new explorer.exe process, the procexp application from live.systernals is showing these two processes, on the same level in a tree representation.
Is there a way to provide an argument to CreateProcess, maybe the explorer process handle, so that the icon tray application starts as a child of this process?
L.E.: Here is the code that I use to create start the explorer.exe and the icon tray processes:
STARTUPINFO sInfoNT; /// startupinfo for the explorer.exe
PROCESS_INFORMATION pInfoNT; /// process infromation for the explorer.exe
ZeroMemory(&sInfoNT, sizeof(sInfoNT));
sInfoNT.lpDesktop = L"threadDesktop"; /// setting the desktop for the process
pInfoNT = startProcess(sInfoNT, L"C:\\Windows\\explorer.exe"); /// starting the process
if (!pInfoNT.hProcess)
LOG(ERROR) << "Unable to start the new explorer process";
else
LOG(INFO) << "Started the new explorer process";
STARTUPINFO sInfoTITD; /// doing the same thing for the tray icon application
PROCESS_INFORMATION pInfoTITD;
ZeroMemory(&sInfoTITD, sizeof(sInfoTITD));
sInfoTITD.lpDesktop = L"threadDesktop";
pInfoTITD = startProcess(sInfoTITD, L"DesktopTrayIcon.exe");
if (!pInfoTITD.hProcess)
LOG(ERROR) << "Unable to start the tray icon for the new desktop";
else
LOG(INFO) << "Started the tray icon for the new desktop";
And this is the startProcess function:
PROCESS_INFORMATION KioskLauncher::startProcess(STARTUPINFO startUpInfo, LPCTSTR lpApplicationName)
{
PROCESS_INFORMATION processInformation;
ZeroMemory(&processInformation, sizeof(processInformation));
if (!CreateProcess(lpApplicationName, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startUpInfo, &processInformation))
MessageBox(0, L"Unable to start the process!\nThe path is broken!", L"Path Error!", MB_ICONERROR);
return processInformation;
}
If your really want to make the new Process a child of that other process, you have to use code injection. A search for CreateRemoteThread will give you plenty of reading material. The biggest problem is, that your process has to be the same bit-ness as the target. There are 3 ALTERNATIVE ways of using it:
Dll injection (standard)
Inject actual shellcode: Assembler code that will resolve all dependencies itself. (Will not work with EMET enabled)
Copy a block of code from your application and fix the imports (Tricky)

Newly created desktop doesn't receive keyboard events

I have created a small program which launches itself in a new desktop.
HDESK hDesktop = ::CreateDesktop(strDesktopName.c_str(),
NULL, // Reserved
NULL, // Reserved
0, // DF_ALLOWOTHERACCOUNTHOOK
GENERIC_ALL,
NULL); // lpSecurity
::SetThreadDesktop(hDesktop);
Later on, started another application on that desktop using the following lines:
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
si.lpDesktop = &strDesktop[0];
if (FALSE == ::CreateProcess(pathModuleName.file_string().c_str(), L"abc def", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
return false;
DWORD dwWaitRes = ::WaitForSingleObject(pi.hProcess, INFINITE);
pathModuleName is a self location obtained by GetModuleFileName(NULL).
The newly created application obtains a HWND to another window and sends window messages using the following commands:
// bring window to front
::SetForegroundWindow(hwnd);
// set focus so keyboard inputs will be caught
::SetFocus(hwnd);
::keybd_event(VK_MENU, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
...
So basically application A on desktop DEFAULT is starting application B on desktop X, which obtains an HWND to another application C started on the same desktop X.
My problem is that keyboard events coming from application B on desktop X are not being triggered in application C. Only if I use SwitchDesktop(B), then events are triggered and code is executed properly.
What am I missing?
You are trying to simulate user input on a desktop that is not active on the physical console (screen, mouse, keyboard), which is not likely to work, and why SwitchDesktop() makes it work. According to the documentation:
SwitchDesktop function
Makes the specified desktop visible and activates it. This enables the desktop to receive input from the user.
keybd_event(), mouse_event(), SendInput(), they all simply generate and store input messages into the same input queue that the physical mouse/keyboard post their messages to. The input system does not know the difference between user input and synthesized input when dispatching the input messages to applications.
Raymond Chen touched on that in his blog:
How do I simulate input without SendInput?
SendInput operates at the bottom level of the input stack. It is just a backdoor into the same input mechanism that the keyboard and mouse drivers use to tell the window manager that the user has generated input. The SendInput function doesn't know what will happen to the input. That is handled by much higher levels of the window manager, like the components which hit-test mouse input to see which window the message should initially be delivered to.
He also posted a nice little diagram in another blog article showing where SendInput() sits in relation to the input queue:
When something gets added to a queue, it takes time for it to come out the front of the queue

Windows -- inherit console file handle in child process

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...)

Combining CreateProcess and AllowSetForegroundWindow on WIN32

I have an application that is essentially a "helper" application that wraps another app.
The app that the user interacts with is a process that is created by the "helper" app like so:
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
memset(&processInfo, 0, sizeof(processInfo));
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.cb = sizeof(startupInfo);
int retval = CreateProcess(cmd, cmdLine, NULL, NULL, false,
CREATE_NO_WINDOW, NULL, NULL, &startupInfo,
&processInfo);
This process is an executable that I do not have the source code to and cannot make changes to.
The "helper" application does a few things based mainly on network traffic from the other app. At one point I want to display a file browse dialog from the helper app based on something the user does in the started UI process.
When I show the file dialog from the helper app, it's shown behind the UI of the process that was created, which isn't ideal. I tried calling SetForegroundWindow() from the helper app but it fails the criteria specified for SetForegroundWindow in the MSDN docs ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms633539%28v=vs.85%29.aspx ), namely:
The process is not the foreground process.
The process was not started by the foreground process.
(even though the process created the foreground process).
Is there a way to call CreateProcess() with a flag or setting that works like calling AllowSetForegroundWindow() from that process? Or a flag that can be used to make Windows think the started process is "the same" as the process that started it for purposes of SetForegroundWindow permissions?
Or is there another way I can show the dialog generated by the helper app on top of the created process' dialogs?
The only solution I could think of off the top of my head would be to do remote thread injection into the child process, and then have your injected thread call AllowSetForegroundWindow with the appropriate parameters to allow the parent process to steal foreground-ness back.
I have not tested this though.