CreateProcessWithLogon returns Function not supported - c++

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

Related

32bit Application run 64bit registry (with WOW6432Node)

I have a 32bit application that must call C:\Windows\System32\regedit.exe, but instead it runs C:\Windows\SysWOW64\regedit.exe. How can I call the regedit in System32?
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe;
szExe = "regedit.exe";
if (CreateProcess("C:\\Windows\\Sysnative\\cmd.exe", szExe.GetBuffer(100), 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
WaitForSingleObject(pi.hProcess, IGNORE);// optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
The Condition returns True but regedit does not run.
Instead of SysNative, I put System32, but it does not work. In szExe, I put the string "C:\Windows\regedit", but it does not work. And so on ...
You are trying to run regedit.exe via cmd.exe, why? Any result you get will be for cmd.exe, regardless of what commands it executes.
Just run the Registry Editor instead of cmd.exe. However, there is a wrinkle.
The 64bit C:\Windows\System32 folder does not have a regedit.exe executable. The 64bit regedit.exe is located in C:\Windows instead. If a 32bit process tries to run that executable directly, the 32bit C:\Windows\SysWOW64\regedit.exe will be run, which you don't want.
In order for a 32bit process to run the 64bit regedit.exe, it needs to run C:\Windows\Sysnative\Regedt32.exe instead, which is a stub that will run the 64bit C:\Windows\regedit.exe:
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe = "C:\\Windows\\Sysnative\\Regedt32.exe";
if (CreateProcess(NULL, szExe.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE); // optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
If CreateProcess() fails with ERROR_ELEVATION_REQUIRED, and you don't want to run your 32bit process elevated, then try using ShellExecute/Ex() with the runas" verb to launch Regedt32.exe elevated.
That being said, you should not hard-code the path to the Windows installation folder. Ask Windows where it is actually installed, such as via GetWindowsDirectory(), SHGetFolderPath(CSIDL_WINDOWS), SHGetKnownFolderPath(FOLDERID_Windows), etc. And then you can append Sysnative\\Regedt32.exe to the end of that path.
To solve this problem I use ShellExecuteEx() and SHELLEXECUTEINFO.
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO Sei;
ZeroMemory(&Sei,sizeof(SHELLEXECUTEINFO));
Sei.cbSize = sizeof(SHELLEXECUTEINFO);
Sei.lpFile = "C:\\windows\\regedit.exe";
Sei.nShow = SW_SHOW;
Sei.fMask = SEE_MASK_INVOKEIDLIST;
Sei.lpVerb = "open";
ShellExecuteEx(&Sei);
if (result == S_OK || result == S_FALSE)
CoUninitialize();
For reference: Launching Applications

start on certain PID c++

I'am trying to start batch file on certain PID, or get PID what program started on. I really have no idea how to do that.
system("start C:\\testing\\vw.bat");
Sleep(2000); //1000 = 1s
After this code is executed I need to close "vw.bat", but not close other batch files that are running.
PROCESS_INFORMATION pi;
STARTUPINFO si{};
si.cb = sizeof(si);
BOOL success = CreateProcess("start C:\\testing\\vw.bat", NULL, NULL, NULL, TRUE, 0, NULL, "C:\\testing\\", &si, &pi);
if (success)
{
int pid = pi.dwProcessId;
}
You can get the PID of your process using system-dependant system calls. For instance, on linux, using pidof. Now, please note using the system() function is not recommended, and other ways of doing what you want exist, like spawning a child process, which will let you kill the process easily.

CreateProcessAsUser fails with ERROR_ELEVATION_REQUIRED for a process with UIAccess="true"

I'm trying to use the following code to run a user-mode process from my service application (running as a local system.)
The requirement for the user-mode process is to run without elevatation, but to have UIAccess="true" in its manifest to be able to display top-most windows correctly under Windows 8.
So I do this (from my service) to run my user-mode process:
//NOTE: Error checking is omitted for readability
//'dwSessionID' = user session ID to run user-mode process in
//'pUserProcPath' = L"C:\\Program Files (x86)\\Company\\Software\\user_process.exe"
HANDLE hToken = NULL;
WTSQueryUserToken(dwSessionID, &hToken);
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
LPVOID pEnvBlock = NULL;
CreateEnvironmentBlock(&pEnvBlock, hToken2, FALSE);
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = L"winsta0\\default";
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
//ImpersonateLoggedOnUser(hToken2); //Not necessary as suggested below
PVOID OldRedir;
Wow64DisableWow64FsRedirection(&OldRedir);
BOOL bSuccess = CreateProcessAsUser(
hToken2, // client's access token
pUserProcPath, // file to execute
NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
pEnvBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
int nOSError = ::GetLastError();
Wow64RevertWow64FsRedirection(OldRedir);
//RevertToSelf(); //Not necessary as suggested below
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DestroyEnvironmentBlock(pEnvBlock);
CloseHandle(hToken2);
CloseHandle(hToken);
This runs fine, if UIAccess="false" in the manifest for the user_process.exe.
But if I do the following:
Enable UIAccess="true" for the user_process.exe:
Sign it with my code-signing certificate and
Place it in the "C:\Program Files (x86)\Company\Software" folder.
What I get is that CreateProcessAsUser fails with the error ERROR_ELEVATION_REQUIRED.
Can someone suggest how to make it work?
PS. I tried adjusting my service's privileges to enable SE_DEBUG_NAME and SE_TCB_NAME, but neither helped.
PS2. If I simply double-click my user_process.exe process compiled with UIAccess="true", code-signed, and placed in the C:\Program Files (x86)\Company\Software folder, it starts & runs just fine (not elevated.).
I found the answer. Here it is for whoever runs into it as well:
Add this after DuplicateTokenEx call:
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
//ONLY if requiring UIAccess!
DWORD dwUIAccess = 1;
::SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess));

CreateProcessAsUser() not working

I am trying to start GUI application using my service. I developed the service on VS2012 and running on windows 7. But CreateProcessAsUser function doesn't start application even though it returns successfully. following is my code:
PHANDLE hToken = NULL;
WTSQueryUserToken (WTSGetActiveConsoleSessionId (), hToken) ;
if( !CreateProcessAsUser( hToken,
NULL, // No module name (use command line)
pPath, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
what could be the issue?
Thanks,
KM.
After retrieving the user token from WTSQueryUserToken(), call DuplicateTokenEx() to convert it into a primary token, and pass that token to CreateProcessAsUser(). You also need to specify the "winsta0\default" (use: "winsta0\\default") desktop via the STARTUPINFO structure. You should also call CreateEnvironmentBlock() using the same token, and pass that environment to CreateProcessAsUser() as well.
There is not enough information to be sure of my answer but that kind of error often happen when the structures are not correctly initialized.
PHANDLE hToken = NULL;
WTSQueryUserToken (WTSGetActiveConsoleSessionId (), hToken) ;
//be sure that the handle is correct ! (can be the issue)
if (!hToken) printf("Token error.\n");
//init here !
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if( !CreateProcessAsUser( hToken,
NULL, // No module name (use command line)
pPath, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
I faced a similar issue while using WTSQueryUserToken in windows 7, but the same function worked in windows 10.
So I took the explorer.exe token, called the DuplicateTokenEx function.
Set the corresponding values for startupinfo structure
si.lpDesktop = "winsta0\\default";
si.wShowWindow = SW_SHOWNORMAL;
si.dwFlags = STARTF_USESHOWWINDOW;
and called createprocessasuser

how to start a program using a windows service?

I created a windows service in c++ using visual studios and now I want the service to run an exe file. The service is set to start every time the computer starts
i know i need to use code to locate the path of the exe like C:\MyDirectory\MyFile.exe but how to I actually run the file from the service?
i read about the process start method here but i am not sure how to use it
You can use createprocess function in your service to run an exe.
TCHAR* path = L"C:\\MyDirectory\\MyFile.exe";
STARTUPINFO info;
PROCESS_INFORMATION processInfo;
ZeroMemory( &info, sizeof(info) );
info.cb = sizeof(info);
ZeroMemory( &processInfo, sizeof(processInfo) );
if (CreateProcess(path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}