CreateProcessAsUser doesn't work in windows service - c++

I wanted to make a windows service in c++ to start my programs as administrator every time the user log in without pop up UAC window
As it's the first time for me to do it I used the project from here :
https://code.msdn.microsoft.com/windowsapps/CppWindowsService-cacf4948/view/SourceCode
I edited line 74 in CppWindowsService.cpp to this :
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_AUTO_START, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
0, // Service running account
SERVICE_PASSWORD // Password of the account
);
and added some code to the worker thread in SampleService.cpp line 101 to become like this :
void CSampleService::ServiceWorkerThread(void)
{
// Periodically check if the service is stopping.
while (!m_fStopping)
{
// Perform main service function here...
HANDLE hToken = NULL, dToken;
WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken);
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, 0, SecurityIdentification, TokenPrimary, &dToken);
STARTUPINFO stinfo = { 0 };
PROCESS_INFORMATION pinfo = { 0 };
stinfo.cb = sizeof(stinfo);
stinfo.lpDesktop = L"winsta0\\default";
LPVOID pEnv = 0;
CreateEnvironmentBlock(&pEnv, dToken, 0);
CreateProcessAsUserW(dToken, L"F:\\test.exe",0, 0, 0, 0,CREATE_NEW_CONSOLE, pEnv, 0, &stinfo, &pinfo);
::Sleep(2000); // Simulate some lengthy operations.
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
the test.exe located in F drive shows a message box but when I try to run it from a service nothing happen although I can easily use CreateProcess to execute it from ordinary programs
Edit : I also tried put a command line in CreateProcessAsUser but nothing happens

I used CREATE_UNICODE_ENVIRONMENT as a creation flag in the function and the problem was solved

Related

CreateProcessWithLogon returns Function not supported

I'm trying to start a program that starts another program as another user with the Win32 function CreateProcessWithLogon on a Windows 7 system but it returns error 120 which stands for function not supported.
If I run the program in the command it works correctly. If I on the other hand start the program with ShellExecute then I get the error.
Cmd Line -> Start Program A -> Program A executes CreateProcessWithLogon. OK
32 bit program -> Start Program A -> Program A executes CreateProcessWithLogn. ERROR
if (!CreateProcessWithLogonW(L"username", L"domain", L"password",
LOGON_NETCREDENTIALS_ONLY, L"C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe", L"iexplore",
NULL, NULL, NULL,
&si, &pi))
DisplayError(L"CreateProcessWithLogonW");
if you want starts program as another user not use LOGON_NETCREDENTIALS_ONLY flag - use LOGON_WITH_PROFILE instead.
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessWithLogonW(L"username", L"domain", L"password", LOGON_WITH_PROFILE,
L"C:\\windows\\notepad.exe", L"notepad.exe", 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
if you use flag LOGON_NETCREDENTIALS_ONLY
The system does not validate the specified credentials.
so really you not need provide real name or password. because system not do real login but
The new process uses the same token as the caller, but the system
creates a new logon session within LSA
so this logon type clone caller current token but specify new logon session in it. your program will be run as same user (SID , groups, privileges) but in separate logon session
only one trick need, not documented - lpUsername must be in UPN format - containing # symbol.
so code must be like this:
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessWithLogonW(L"#", 0, 0, LOGON_NETCREDENTIALS_ONLY,
L"C:\\windows\\notepad.exe", L"notepad.exe", 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
as result will be created new LogonSession with NewCredentials as SECURITY_LOGON_TYPE , AuthenticationPackage == Negotiate

windows service start a interactive process(MFC application) on Windows XP

now I have a windows service, and I want the service create a GUI process(MFC application). And I referenced the code:Launch your application in Vista under the local system account without the UAC popup .
And the important is :
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL bResult = FALSE;
DWORD dwSessionId,winlogonPid;
HANDLE hUserToken,hUserTokenDup,hPToken,hProcess;
DWORD dwCreationFlags;
// Log the client on to the local computer.
dwSessionId = WTSGetActiveConsoleSessionId();
WTSQueryUserToken(dwSessionId,&hUserToken);
dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = "winsta0\\default";
ZeroMemory(&pi, sizeof(pi));
TOKEN_PRIVILEGES tp;
LUID luid;
hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,GetCurrentProcessId();
if(!::OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
|TOKEN_READ|TOKEN_WRITE,&hPToken))
{
int abcd = GetLastError();
printf("Process token open Error: %u\n",GetLastError());
}
if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
{
printf("Lookup Privilege value Error: %u\n",GetLastError());
}
tp.PrivilegeCount =1;
tp.Privileges[0].Luid =luid;
tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;
DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);
int dup = GetLastError();
//Adjust Token privilege
SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)dwSessionId,sizeof(DWORD));
if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,NULL))
{
int abc =GetLastError();
printf("Adjust Privilege value Error: %u\n",GetLastError());
}
if (GetLastError()== ERROR_NOT_ALL_ASSIGNED)
{
printf("Token does not have the provilege\n");
}
LPVOID pEnv =NULL;
if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
{
dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;
}
else
pEnv=NULL;
// Launch the process in the client's logon session.
bResult = CreateProcessAsUser(
hUserTokenDup, // client's access token
_T("TestDialog.exe"), // file to execute
NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
// End impersonation of client.
//GetLastError Shud be 0
int iResultOfCreateProcessAsUser = GetLastError();
//Perform All the Close Handles task
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);
return 0;
Now the MFC application can be started after the service stared, it works well. And the process user name is System.
But when I logoff my account(the GUI application is running while click the windows system logoff button), the MFC application have a VC++ crash, the crash point the MFC internal function afxactivationwndproc .
My MFC application is just created by the VS2010 IDE without modify.
My test environment is on Windows XP.
If I double click the application to run it, and logoff with the application running, no error occurs.
So I want to know:
When user logoff, will system force kill my MFC application process? (Note: The process user name is System)
As mentioned, if I double click the application to run it, it is OK. So is there any problem with the service starting method? Or we should add addition necessary operation to the MFC application.
Is there any other possible reason for this error?
Thank you very much!

TerminateProcess not suceeding on Windows 10

Our C++ app launches a separate .exe ( which may start its own sub-processes) using CreateProcess as below.
BOOL started = ::CreateProcess(NULL, // application
p, // parameters
NULL, // process security
NULL, // thread security
TRUE, // inherit handles flag
0, // flags
NULL, // inherit environment
dirLP, // inherit directory
&startup, // STARTUPINFO
&procinfo); // PROCESS_INFORMATIO
In case we need cancel the "job" we use CreateToolhelp32Snapshot to iterate through the process list to find any child processes of the one we launched.
static BOOL TerminateProcessTree (HANDLE parentProcess,UINT exitCode)
{
BOOL result=TRUE;
HANDLE hProcessSnap = NULL;
PROCESSENTRY32 pe32 = {0};
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return (FALSE);
pe32.dwSize = sizeof(PROCESSENTRY32);
// Walk the snapshot of the processes
DWORD parentID=GetProcessId(parentProcess);
if(parentID==0){
PrintLastError("GetProcessId");
return FALSE;
}
if (Process32First(hProcessSnap, &pe32)) {
do{
if(pe32.th32ParentProcessID==parentID){
HANDLE hProcess = OpenProcess (PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
if(hProcess!=NULL){
BOOL terminateChildren=TerminateProcessTree(hProcess,exitCode);
BOOL terminatedChild = TerminateProcess(hProcess, exitCode);
if (!terminatedChild){
PrintLastError("TerminateProcess");
}
CloseHandle(hProcess);
if(!terminatedChild || !terminateChildren){
result=FALSE;
break;
}
} else{
PrintLastError("OpenProcess");
}
}
}while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle (hProcessSnap);
DWORD checkCode=0;
BOOL terminated;
if(GetExitCodeProcess(parentProcess,&checkCode) && checkCode==STILL_ACTIVE){
terminated=TerminateProcess(parentProcess,exitCode);
if (!terminated){
PrintLastError("TerminateProcess");
result= FALSE;
}
}
return result;
}
As noted, this works fine on Windows 7. Windows 10 fails with "Access Denied" on the first call "TerminateProcess". Clearly something has changed in the Windows security model when it comes to processes.
The robust way to deal with controlling a process tree of objects is to use Job objects as noted in the comment threads.
The one thing to keep in mind is that a process can only belong to a single job, and by default any EXE that the OS determines needs appcompat help is put into a job object automatically managed by the "Program Compatibility Assistant". This makes using a Job object to manage arbitrary 3rd party EXEs a little bit more complicated (i.e. AssignProcessToJobObject fails for these processes).
If you are using CreateProcess you can make use of the flag CREATE_BREAKAWAY_FROM_JOB. See this blog post. This works fine as long as the target EXE has the same rights as the calling object.
For a Standard User EXE to run an EXE that might need Administrator rights (i.e. they contain a manifest which marks it as requireAdministrator), you have to use ShellExecute or ShellExecuteEx as calling CreateProcess in this case will fail. If your target EXEs are all using the proper manifest elements then it won't be put into a PCA Job object.. You can use the trick of passing SEE_MASK_FLAG_NO_UI which will have the side-effect of avoiding the PCA job behavior. If you are launching arbitrary 3rd party EXEs, you should use ShellExecuteEx and not CreateProcess.
bool SpawnProcess( const WCHAR* szExePath, const WCHAR* szExeArgs )
{
if( !szExePath )
return false;
// NOTE: szExeArgs can be nullptr.
// Get working directory from executable path.
WCHAR szDirectory[MAX_PATH] = {0};
wcscpy_s( szDirectory, szExePath );
PathRemoveFileSpec( szDirectory );
// ShellExecute or ShellExecuteEx must be used instead of CreateProcess
// to permit the shell to display a UAC prompt asking for consent to
// elevate when the target executable's manifest specifies a run level
// of "requireAdministrator".
//
// You can only use CreateProcess if you know that the application you
// are spawning will be at the same run level as the current process.
// Otherwise, you will receive ERROR_ACCESS_DENIED if the elevation
// consent could not be obtained.
SHELLEXECUTEINFO info = {};
info.cbSize = sizeof( info );
info.lpVerb = L"open";
info.fMask = SEE_MASK_FLAG_NO_UI;
info.lpFile = szExePath;
info.lpParameters = szExeArgs;
info.lpDirectory = szDirectory;
info.nShow = SW_SHOW;
if( !ShellExecuteEx( &info ) )
return false;
return true;
}
Note that if you want to wait until this process exits, you can use:
bool SpawnProcessAndWait( const WCHAR *szExePath, const WCHAR *szExeArgs, DWORD *pdwExitCode )
{
if( !szExePath )
return false;
// NOTE: szExeArgs and pExitCode can be nullptr.
// Get working directory from executable path.
WCHAR szDirectory[MAX_PATH] = {0};
wcscpy_s( szDirectory, szExePath );
PathRemoveFileSpec( szDirectory );
// See SpawnProcess for information why ShellExecute or ShellExecuteEx
// must be used instead of CreateProcess.
SHELLEXECUTEINFO info = {};
info.cbSize = sizeof( info );
info.lpVerb = L"open";
info.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
info.lpFile = szExePath;
info.lpParameters = szExeArgs;
info.lpDirectory = szDirectory;
info.nShow = SW_SHOW;
if( !ShellExecuteEx( &info ) )
return false;
// Wait for process to finish.
WaitForSingleObject( info.hProcess, INFINITE );
// Return exit code from process, if requested by caller.
if( pdwExitCode )
GetExitCodeProcess( info.hProcess, pdwExitCode );
CloseHandle( info.hProcess );
return true;
}
You haven't noted if your "master" application here is using administrator or Standard User rights. Ideally it is using Standard User rights per the User Account Control guidelines dating back to Windows Vista. If your app is running as Standard User, then it's likely that your code no longer works due to security improvements to keep non-Administrator malware from doing this kind of thing to other processes.
For general appcompat questions about Windows 10, be sure to read the Windows and Windows Server compatibility cookbook and look back at the Windows 8.0 and Windows 8.1 material if you are jumping directly from Windows 7 to Windows 10.

CreateProcessAsUser generating error 5

I've tried mixing the code here and here to run an GUI exe from a service that was initialized through QtService, but whenever I run the code bellow I get an error 5 from the CreateProcessAsUser.
Also, I saw the answer to a similar question here on StackOverflow but couldn't figure out how the DACL is related with the problem, and can't use the answer by Harry Johnson because I wouldn't have the logon info from the users.
So, can someone help me understand why am I receiving the error 5 (Acess Denied) from the code below?
if(initUiWin())
log->write("InitUiWin executed.");
else {
QString errorNumber = QString::number(GetLastError());
log->write("InitUiWin error: " + errorNumber);
}
-
bool initUiWin()
{
// obtain the currently active session id; every logged on
// User in the system has a unique session id
uint dwSessionId = WTSGetActiveConsoleSessionId();
// obtain the process id of the winlogon process that
// is running within the currently active session
QString processName("winlogon.exe");
DWORD winlogonPID = FindProcessId(processName.toStdWString(),dwSessionId);
if( winlogonPID != 0 ) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
HANDLE hToken;
OpenProcessToken(hProcess,TOKEN_READ,&hToken);
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
SECURITY_ATTRIBUTES sa;
sa.nLength = static_cast<DWORD>(sizeof(SECURITY_ATTRIBUTES));
// copy the access token of the winlogon process;
// the newly created token will be a primary token
HANDLE hUserTokenDup;
if (!DuplicateTokenEx(hToken,MAXIMUM_ALLOWED,&sa,
SecurityIdentification,TokenPrimary,&hUserTokenDup)) {
CloseHandle(hProcess);
CloseHandle(hToken);
return false;
}
// Get Handle to the interactive window station
HWINSTA hwinsta = NULL;
hwinsta = OpenWindowStation(
_T(L"winsta0"), // the interactive window station
FALSE, // handle is not inheritable
READ_CONTROL | WRITE_DAC); // rights to read/write the DACL
if(hwinsta == NULL)
return false;
// To get the correct default desktop, set the caller's
// window station to the interactive window station.
if (!SetProcessWindowStation(hwinsta))
return false;
// Get a handle to the interactive desktop.
HDESK hdesk = NULL;
hdesk = OpenDesktop(
_T(L"default"), // the interactive window station
0, // no interaction with other desktop processes
FALSE, // handle is not inheritable
READ_CONTROL | // request the rights to read and write the DACL
WRITE_DAC |
DESKTOP_WRITEOBJECTS |
DESKTOP_READOBJECTS);
if(hdesk == NULL)
return false;
// Get the SID for the client's logon session.
PSID pSid = NULL;
if (!GetLogonSID(hUserTokenDup, &pSid))
return false;
// Allow logon SID full access to interactive window station.
if (!AddAceToWindowStation(hwinsta, pSid) )
return false;
// Allow logon SID full access to interactive desktop.
if (!AddAceToDesktop(hdesk, pSid) )
return false;
// Impersonate client to ensure access to executable file.
if (!ImpersonateLoggedOnUser(hUserTokenDup) )
return false;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = static_cast<DWORD>(sizeof(STARTUPINFO));
// interactive window station parameter; basically this indicates
// that the process created can display a GUI on the desktop
wchar_t auxBuffer[16] = L"winsta0\\default";
si.lpDesktop = auxBuffer;
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB;
// create a new process in the current User's logon session
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
L"test-ui-systray.exe", // file to execute
NULL, // command line
&sa, // pointer to process SECURITY_ATTRIBUTES
&sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
if (pSid)
FreeLogonSID(&pSid);
if (hdesk)
CloseDesktop(hdesk);
if (hwinsta)
CloseWindowStation(hwinsta);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return result;
}
return false;
}
Putting it here for visibility. HarryJohnston answered in the comments. The problem was the flags in the OpenProcessToken, I just changed
OpenProcessToken(hProcess,TOKEN_READ,&hToken)
to
OpenProcessToken(hProcess,TOKEN_READ|TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,&hToken)

ReadFile works fine inside VS C++ IDE but fails stand-alone

Currently I have two apps, one with a GUI (written using MFC) and the other as a standard executable. The GUI app (parent) triggers the standard app (child) using CreateProcessW call and parent receives messages from its child via an anonymous pipe. The message receiving process works fine when I run the parent inside the VS IDE. However, if I run the parent standalone, parent does not receive any messages from its child (i.e. parent get hang in ReadFile call, waiting for messages).
Any thoughts on this?
Note: After creation of anonymous pipe, all read operations happens inside a separate thread and it does not block either UI or main thread. Some code related to child process creation and used parameters are given below.
// pipe creation code
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 STDOUT.
if ( ! CreatePipe(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0) )
{
m_logger->log( __FILE__, __LINE__, EventSeverity::WARNING, "Pipe cannot be created, will not receive meassages from child processes" );
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
{
m_logger->log( __FILE__, __LINE__, EventSeverity::WARNING, "Could not make the read handler of the anonymous pipe not-inheritable" );
}
SetStdHandle(STD_OUTPUT_HANDLE, m_hChildStd_OUT_Wr);
SetStdHandle(STD_ERROR_HANDLE, m_hChildStd_OUT_Wr);
//Child process creation code
m_startupInfo.lpDesktop = NULL;
m_startupInfo.lpReserved = NULL;
m_startupInfo.lpReserved2 = NULL;
m_startupInfo.lpTitle = NULL;
m_startupInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
m_startupInfo.cb = sizeof( m_startupInfo );
// launch the executable
m_isExecuting = CreateProcessW( app.exe, // lpApplicationName
m_pwszParam, // lpCommandLine
0, // lpProcessAttributes
0, // lpThreadAttributes
TRUE, // bInheritHandles
CREATE_NEW_PROCESS_GROUP, // dwCreationFlags
NULL, // lpEnvironment
curent working directory // lpCurrentDirectory
&m_startupInfo, // lpStartupInfo
&m_processInfo // lpProcessInformation
);
managed to solve this issue. Previously I was updating parent's output and error handlers to newly created handlers in order to retrieve them when creating a child process. However, this doesn't seem to be working. When I modify the code to pass the pipe handlers via a pointer or a reference then things started to work fine.
However this does not explain the reason for running inside IDE and failing it in stand-alone mode.
//set the handler
SetStdHandle(STD_OUTPUT_HANDLE, m_hChildStd_OUT_Wr);
SetStdHandle(STD_ERROR_HANDLE, m_hChildStd_OUT_Wr);
//get the handler
m_startupInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);