Следующая функция, которую я написал, должна вызываться службой Windows, чтобы запустить дочерний процесс (как пользователь), чтобы дочерний процесс мог взаимодействовать с пользователем, то есть иметь пользовательский интерфейс. Он отлично работает на рабочем столе Windows, но не работает должным образом на Windows Server 2012. Пользовательский интерфейс не отображается (даже не в окне консоли), и горячая клавиша, которую мы регистрируем для удаления службы, также не работает.
Следующий код:
void WINAPI Run(DWORD dwTargetSessionId, int desktop, LPTSTR lpszCmdLine)
{
wprintf(_T("Run client start"));
if (hPrevAppProcess != NULL)
{
TerminateProcess(hPrevAppProcess, 0);
WaitForSingleObject(hPrevAppProcess, INFINITE);
}
HANDLE hToken = 0;
WTS_SESSION_INFO *si;
DWORD cnt = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &si, &cnt);
for (int i = 0; i < (int)cnt; i++)
{
if (si[i].SessionId == 0)continue;
wprintf(_T("Trying session id %i (%s) user admin token"), si[i].SessionId, si[i].pWinStationName);
HANDLE userToken;
if (WTSQueryUserToken(si[i].SessionId, &userToken))
{
wprintf(_T("WTSQueryUserToken succeced"));
TOKEN_LINKED_TOKEN admin;
DWORD len;
if (GetTokenInformation(userToken, TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &len))
{
wprintf(_T("Success using user admin token"));
hToken = admin.LinkedToken;
break;
}
else
wprintf(L"GetTokenInformation() failed");
CloseHandle(userToken);
}
else
{
DWORD error = GetLastError();
if (error)
{
LPVOID lpMsgBuf;
DWORD bufLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
utils::WriteLogFile(L"Error '%s'\n", lpMsgBuf);
}
}
}
if (hToken == 0)
{
HANDLE systemToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &systemToken);
DuplicateTokenEx(systemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hToken);
CloseHandle(systemToken);
int i;
for (i = 0; i < (int)cnt; i++)
{
if (si[i].SessionId == 0)continue;
if (SetTokenInformation(hToken, TokenSessionId, &si[i].SessionId, sizeof(DWORD)))
{
wprintf(_T("Success using system token with set user session id %i"), si[i].SessionId);
break;
}
}
if (i == cnt)
wprintf(_T("No success to get user admin token nor system token with set user session id"));
}
WTSFreeMemory(si);
STARTUPINFO startupInfo = {};
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.lpDesktop = _T("winsta0\\default");
LPVOID pEnv = NULL;
CreateEnvironmentBlock(&pEnv, hToken, TRUE);
PROCESS_INFORMATION processInfo = {};
PROCESS_INFORMATION processInfo32 = {};
TCHAR szCurModule[MAX_PATH] = { 0 };
GetModuleFileName(NULL, szCurModule, MAX_PATH);
BOOL bRes = FALSE;
std::wstring commandLine;
commandLine.reserve(1024);
commandLine += L"\"";
commandLine += szCurModule;
commandLine += L"\" \"";
commandLine += SERVICE_COMMAND_LUNCHER;
commandLine += L"\"";
wprintf(_T("launch SG_WinService with CreateProcessAsUser ... %s"), commandLine.c_str());
bRes = CreateProcessAsUserW(hToken, NULL, &commandLine[0], NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE, pEnv,
NULL, &startupInfo, &processInfo);
if (bRes == FALSE)
{
DWORD dwLastError = ::GetLastError();
TCHAR lpBuffer[256] = _T("?");
if (dwLastError != 0) // Don't want to see a "operation done successfully" error ;-)
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, // It´s a system error
NULL, // No string to be formatted needed
dwLastError, // Hey Windows: Please explain this error!
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Do it in the standard language
lpBuffer, // Put the message here
255, // Number of bytes to store the message
NULL);
(_T("CreateProcessAsUser(SG_WinService) failed - Error : %s (%i)"), lpBuffer, dwLastError);
}
else
{
wprintf(_T("CreateProcessAsUser(SG_WinService) success. New process ID: %i"), processInfo.dwProcessId);
}
}
возвращает следующий вывод:
05.06.2019 04:25 5856: Попытка идентификатора администратора сеанса 1 (Консоль) пользователя
05.06.2019 04:25 5856: Попытка идентификатора администратора 2 сеанса (RDP-Tcp # 27) для пользователя admin
05.06.2019 04:25 5856: Попытка идентификатора сеанса 65536 (RDP-Tcp) токен администратора пользователя
05.06.2019 04:25 5856: Успешное использование системного токена с установленным идентификатором сеанса пользователя 1
05.06.2019 04:25 5856: запустить SG_WinService с CreateProcessAsUser ... "C: \ myservice \ SG_WinService.exe" "ServiceIsLuncher"
05.06.2019 04:25 5856: CreateProcessAsUser (SG_WinService) успешно завершен. ID нового процесса: 5580
Обновление:
После добавления вызова GetLastError () я получаю:
09.06.2019 04:55 6008: пробный сеанс с идентификатором 1, токен администратора пользователя
09.06.2019 04:55 6008: Ошибка 'Была сделана попытка сослаться на несуществующий токен.
09.06.2019 04:55 6008: Попытка идентификатора администратора 3 сеанса пользователя
09.06.2019 04:55 6008: Попытка идентификатора сеанса 65536 токена администратора пользователя
09.06.2019 04:55 6008: Ошибка 'Система не может найти указанный файл.
09.06.2019 04:55 6008: Успешное использование системного токена с установленным идентификатором сеанса пользователя 1
09.06.2019 04:55 6008: запустить SG_WinService с CreateProcessAsUser ...
"C: \ Users \ Администратор \ Desktop \ src \ SpyfiWebApp \ SG_WinService.exe" "ServiceIsLuncher"
09.06.2019 04:55 6008: CreateProcessAsUser (SG_WinService) успешно завершен. ID нового процесса: 7536
Таким образом, вместо запуска дочернего процесса в сеансе 1, он запускается как "SYSTEM".