Interactive running of programs from NT Services.

 

First, let's talk about running programs from applications.
In a classic application, we can run another program as a new process: CreateProcess, as ShellExecute or  as script in clause system(), for example system("c:\\diallix.exe")

Single instances of the program can be set to display - whether they should be running in windows mode or in hide mode in the background, as well as setting (passing) arguments and so on.
This is not so easy with NT Services. Unlike classic applications that run under the user's desktop, NT system services run under the System after booting the system. Trying to run a program, such as a console application interactively, that is in a dialog, hides it in the background. So how to run a program in a window under a given user account and desktop?

The answer is CreateProcessAsUser. In order to invoke and run the program, we need the following functions, which cooperate: SetTokenInformation, DuplicateTokenEx, WTSGetActiveConsoleSessionId, WTSQueryUserToken, LookupPrivilegeValue.

So in short, we need to write a function that will run a program with a GUI in interactive mode.
For a more detailed description of the above features, see: https://www.codeproject.com/Articles/18367/Launch-your-application-in-Vista-under-the-local-s

The suggested function could look like this:



#include "WinAPI.h" //https://www.netbot.sk/en/14-blog-headers/44-winapi-en 
bool StartInteractiveProcess(::DWORD sessionId, char* programpath) { ::HANDLE hToken, hProcess, hPToken, hUserTokenDup; ::HDESK hdesk; ::HWINSTA hwinsta; ::HWINSTA hwinstaSave; ::PROCESS_INFORMATION pi; ::PSID pSid; ::STARTUPINFO si; bool bQueryUserToken;
this->hdesk = NULL; this->hwinsta = NULL; this->hwinstaSave = NULL; this->pSid = NULL;
this->bQueryUserToken = false;
sessionId = ::WTSGetActiveConsoleSessionId();
this->bQueryUserToken = ::WTSQueryUserToken(sessionId, &this->hToken);
::ZeroMemory(&this->si, sizeof(STARTUPINFO)); this->si.cb= sizeof(STARTUPINFO); this->si.lpDesktop = TEXT("winsta0\\default");
TOKEN_PRIVILEGES tp; LUID luid2;
::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid2);
tp.PrivilegeCount = 1; tp.Privileges[0].Luid =luid2; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
::DuplicateTokenEx(this->hToken, MAXIMUM_ALLOWED,NULL, SecurityIdentification,
TokenPrimary, &this->hUserTokenDup);
::SetTokenInformation(this->hUserTokenDup, TokenSessionId, (void*)sessionId, sizeof(DWORD));
::CreateProcessAsUser(this->hUserTokenDup, NULL, this->CHAR_To_LPWSTR(programpath),
NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
::CloseHandle(this->hProcess); ::CloseHandle(this->hUserTokenDup); ::CloseHandle(this->hPToken);
return 0; }

 

 

 

 

 

 

 

 

-----------------------------------------------------------------

The whole function is built into header WinAPI.h WinAPI.h


#include "WinAPI.h" //https://www.netbot.sk/en/14-blog-headers/44-winapi-en

int _cdecl main (void) 
{
   ::Diall_WinApi::WinApi::GetInstance()->SystemIntegrity(::Diall_WinApi::Privilege::ENABLE);
::Diall_WinApi::WinApi::GetInstance()->StartInteractiveProcess(0,"c:\\diallix.net");

return 0; }