@Darren Rose, Following is the code that will start a process in a standard user's interactive session running as an Administrator when it is called from a Windows Service running as SYSTEM.A couple of things to note :
- The code assumes that UAC is active.
- The profile of the Administrator account being used is loaded. This is needed for user environment variables and HKCU use by the started process.
- The unicode (UTF-16LE) version of Windows API functions is used. Any command line string passed to the function must be writable to avoid an possible access violation.
- Upon a successful return from CreateProcessAsUser the code will wait for the started process to terminate. This is to give it an opportunity to unload the user profile after it is no longer in use by the started process.
- Since the code will wait indefinitely for the started process to terminate you may want to use a dedicated thread in your Windows Service or otherwise advise a threadpool that the function will not return quickly.
Following is the code -
#include <Windows.h>
#include <UserEnv.h>
#include <WtsApi32.h>
typedef BOOL(WINAPI* LOGONUSEREXEXW)( // LogonUserExExW
_In_ LPCWSTR lpszUsername,
_In_opt_ LPCWSTR lpszDomain,
_In_opt_ LPCWSTR lpszPassword,
_In_ DWORD dwLogonType,
_In_ DWORD dwLogonProvider,
_In_opt_ PTOKEN_GROUPS pTokenGroups,
_Out_opt_ PHANDLE phToken,
_Out_opt_ PSID* ppLogonSid,
_Out_opt_ PVOID* ppProfileBuffer,
_Out_opt_ LPDWORD pdwProfileLength,
_Out_opt_ PQUOTA_LIMITS pQuotaLimits
);
typedef struct {
DWORD GroupCount;
SID_AND_ATTRIBUTES Groups[3];
} LGEXGROUPS, * PLGEXGROUPS;
DWORD GetInteractiveSession()
{
DWORD dwCount{}, dwSession{(DWORD) -1};
PWTS_SESSION_INFOW pwsi{};
if (WTSEnumerateSessionsW(WTS_CURRENT_SERVER, 0, 1, &pwsi, &dwCount))
{
for (DWORD i = 0; i < dwCount; i++)
{
if (pwsi[i].State == WTSActive)
{
dwSession = pwsi[i].SessionId;
break;
}
}
WTSFreeMemory(pwsi);
}
return dwSession;
}
BOOL LogonWithGroups( LPCWSTR lpUser,
LPCWSTR lpDomain,
LPCWSTR lpPass,
PTOKEN_GROUPS plogonsid,
PHANDLE ptoken)
{
BOOL ret{ FALSE };
LOGONUSEREXEXW LogonUserExEx{};
static HMODULE hMod = NULL;
BYTE localSid[SECURITY_MAX_SID_SIZE]{};
DWORD cblocalSid{ ARRAYSIZE(localSid) };
BYTE localAccountSid[SECURITY_MAX_SID_SIZE]{};
DWORD cblocalAccountSid{ ARRAYSIZE(localAccountSid) };
if (!hMod)
hMod = LoadLibraryW(L"advapi32.dll");
if (hMod)
{
LogonUserExEx = (LOGONUSEREXEXW)GetProcAddress(hMod, "LogonUserExExW");
if (LogonUserExEx)
{
if (CreateWellKnownSid(WinLocalSid, NULL, localSid, &cblocalSid) &&
CreateWellKnownSid(WinLocalAccountSid, NULL, localAccountSid, &cblocalAccountSid))
{
LGEXGROUPS tg = {
ARRAYSIZE(tg.Groups),
{ {localSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
{localAccountSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
{plogonsid->Groups[0].Sid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
}
};
ret = LogonUserExEx(lpUser, lpDomain, lpPass,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, (PTOKEN_GROUPS) &tg,
ptoken, NULL, NULL, NULL, NULL);
}
}
}
return ret;
}
DWORD StartAdminUser( LPCWSTR lpUser,
LPCWSTR lpDomain,
LPCWSTR lpPass,
LPCWSTR lpApp,
LPWSTR lpCmdline,
LPCWSTR lpDir,
int nShow)
{
STARTUPINFOW si{ sizeof si };
DWORD dwSession{}, err{ ERROR_SUCCESS };
PROCESS_INFORMATION pi{};
HANDLE hToken{}, hCurrentUser{};
TOKEN_LINKED_TOKEN tlt{};
PROFILEINFOW pInfo{ sizeof pInfo };
LPVOID pEnvironment{};
BOOL bImpersonating{ FALSE };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = nShow;
pInfo.lpUserName = (LPWSTR)lpUser;
__try {
dwSession = GetInteractiveSession();
if (dwSession == (DWORD)-1)
{
err = GetLastError();
__leave;
}
if (!WTSQueryUserToken(dwSession, &hCurrentUser))
{
err = GetLastError();
__leave;
}
DWORD dwLen{};
BYTE logonsid[sizeof(TOKEN_GROUPS) + SECURITY_MAX_SID_SIZE]{};
DWORD cblogonsid{ ARRAYSIZE(logonsid) };
if (!GetTokenInformation(hCurrentUser, TokenLogonSid, logonsid, cblogonsid, &dwLen))
{
err = GetLastError();
__leave;
}
if (!LogonWithGroups(lpUser, lpDomain, lpPass, (PTOKEN_GROUPS)logonsid, &hToken))
{
err = GetLastError();
__leave;
}
if (!GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof tlt, &dwLen))
{
err = GetLastError();
__leave;
}
if (!SetTokenInformation(tlt.LinkedToken, TokenSessionId, &dwSession, sizeof(DWORD)))
{
err = GetLastError();
__leave;
}
if (!LoadUserProfileW(tlt.LinkedToken, &pInfo))
{
err = GetLastError();
__leave;
}
if (!CreateEnvironmentBlock(&pEnvironment, tlt.LinkedToken, FALSE))
{
err = GetLastError();
__leave;
}
bImpersonating = ImpersonateLoggedOnUser(tlt.LinkedToken);
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApp, lpCmdline, nullptr, nullptr,
FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, lpDir, &si, &pi))
{
err = GetLastError();
__leave;
}
if (bImpersonating)
{
RevertToSelf();
bImpersonating = FALSE;
}
WaitForSingleObject(pi.hProcess, INFINITE);
}
__finally {
if (bImpersonating)
RevertToSelf();
if (hCurrentUser)
CloseHandle(hCurrentUser);
if (hToken)
CloseHandle(hToken);
if(pInfo.hProfile)
UnloadUserProfile(tlt.LinkedToken, pInfo.hProfile);
if (tlt.LinkedToken)
CloseHandle(tlt.LinkedToken);
if (pEnvironment)
DestroyEnvironmentBlock(pEnvironment);
if (pi.hThread)
CloseHandle(pi.hThread);
if (pi.hProcess)
CloseHandle(pi.hProcess);
}
return err;
}