Автор: Qraizer
Дата сообщения: 06.09.2008 16:24
Ну дык понятно, терминальные сессии - они ж не локальные, а удалённые, хоть и интерактивные.
Хороший я как-то пример написал. Уже не первый раз пригождается. Это полный пример сервиса. Вырезать из него нужное труда составить не должно.[more]
Код:
/****************************************************************************************\
|* *|
|* Пример для интерактивного сервиса *|
|* ВНИМАНИЕ! Интерактивные сервисы не рекомендуются. *|
|* *|
\****************************************************************************************/
#define _WIN32_WINNT 0x0501
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
#include <process.h>
#include <vector>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <string>
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
std::vector<HANDLE> events;
HANDLE hStart=NULL, hStop=NULL, hThread=NULL;
TOKEN_PRIVILEGES newToken, oldToken={0};
HANDLE tokenProcess=NULL;
namespace std
{
typedef std::basic_string <TCHAR> tstring;
typedef std::basic_ostringstream<TCHAR> tostringstream;
typedef std::basic_ifstream <TCHAR> tifstream;
}
// Как-то проявляемся на десктопе
int startGUI()
{
OPENFILENAME fileName;
std::vector<TCHAR> buffer(256);
fileName.lStructSize = sizeof(fileName);
fileName.pvReserved = fileName.lpstrCustomFilter = fileName.lpstrFileTitle = NULL;
fileName.hwndOwner = NULL;
fileName.lpstrInitialDir= fileName.lpstrDefExt = NULL;
fileName.lpstrFilter = _T("Log files (*.log)\0*.log\0Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0\0");
fileName.nFilterIndex = fileName.dwReserved = fileName.FlagsEx = 0;
fileName.lpstrFile =&buffer[0];
fileName.nMaxFile = buffer.size();
fileName.lpstrTitle = _T("NAV DB log file results");
fileName.Flags = OFN_DONTADDTORECENT | OFN_FORCESHOWHIDDEN;
return GetSaveFileName((fileName.lpstrTitle=static_cast<std::tostringstream&>
(std::tostringstream() << WTSGetActiveConsoleSessionId()
<< _T(" NAV DB log file results")
).str().c_str(), &fileName
));
}
// Рабочая нитка
unsigned __stdcall threadFunc(void*)
{
DWORD res;
HANDLE hpToken, htToken;
// Получить токен процесса...
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &hpToken))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - OpenProcessToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
return -res;
}
// ... полутить из него вторичный токен для имперсонации...
if(!DuplicateToken(hpToken, SecurityImpersonation, &htToken))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - DuplicateToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
CloseHandle(hpToken);
return -res;
}
// ...и имперсонировать им токен нитки
if(!SetThreadToken(NULL, htToken))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - SetThreadToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
CloseHandle(htToken);
CloseHandle(hpToken);
return -res;
}
// Зачистка
CloseHandle(hpToken);
CloseHandle(htToken);
// Ждём сигнала и циклим, пока не произойдёт ошибка
while((res=WaitForMultipleObjects(events.size(), &events[0], FALSE, INFINITE)) == WAIT_OBJECT_0+1)
{
// Подстроиться под текущий логин
// Получить текущий сеанс.
// Это для WinXP с её Fast User Switch. В реальной терминалсерверной среде лучше оперировать
// иными критериями, например, можно перечислить все сеансы функцией WTSEnumerateSessions()
DWORD sessionID=WTSGetActiveConsoleSessionId();
HANDLE ppToken;
// Взять имперсонированный токен
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID | TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, FALSE, &hpToken))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - OpenThreadToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
SetEvent(hStart);
continue;
}
// Подменить в нём ID сеанса...
if(!SetTokenInformation(hpToken, TokenSessionId, &sessionID, sizeof(sessionID)))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - SetTokenInformation() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
CloseHandle(hpToken);
SetEvent(hStart);
continue;
}
// ...получить первичный токен
if(!DuplicateTokenEx(hpToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &ppToken))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - DuplicateTokenEx() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
if(!ImpersonateSelf(SecurityImpersonation))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - ImpersonateSelf() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
}
CloseHandle(hpToken);
SetEvent(hStart);
continue;
}
// Подготовка к старту процесса
STARTUPINFO startInfo = {sizeof(startInfo), NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL};
PROCESS_INFORMATION processInfo;
std::tstring commandLine(GetCommandLine());
commandLine+=_T(" startGUI\0");
commandLine+=_T('\0');
// Строковые операции кончились. К сожалению, WinAPI хочет неконстантный указатель,
// поэтому придётся строку заменить на вектор.
std::vector<std::tstring::value_type> buffer(commandLine.begin(), commandLine.end());
if(!CreateProcessAsUser(ppToken, NULL, &buffer[0], NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL,
&startInfo, &processInfo))
{
DWORD res=GetLastError();
// Старт себя с параметром
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - CreateProcessAsUser() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
CloseHandle(ppToken);
if(!ImpersonateSelf(SecurityImpersonation))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - ImpersonateSelf() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
}
CloseHandle(hpToken);
SetEvent(hStart);
continue;
}
// Подчищаем за собой
CloseHandle(hpToken);
CloseHandle(ppToken);
CloseHandle(processInfo.hProcess);
// Вернуться к имперсонированный токену
if(!ImpersonateSelf(SecurityImpersonation))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - ImpersonateSelf() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
}
// Ждём завершения запущенного себя
WaitForSingleObject(processInfo.hThread, INFINITE);
CloseHandle(processInfo.hThread);
// Сигналим главной нити
SetEvent(hStart);
}
// Вернуть токен нитки к токену процесса.
// Т.к. нитка всё равно уже завершается, то это не обязательно. Но знать об этом полезно.
RevertToSelf();
return res==WAIT_OBJECT_0;
}
// Control handler function
VOID WINAPI ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
SetEvent(hStop);
AdjustTokenPrivileges(tokenProcess, FALSE, &oldToken, 0, NULL, NULL);
CloseHandle(tokenProcess);
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
}
// Service initialization
bool InitService()
{
unsigned threadID;
hStart = CreateEvent(NULL, FALSE, FALSE, NULL);
hStop = CreateEvent(NULL, FALSE, FALSE, NULL);
events.push_back(hStop);
events.push_back(hStart);
hThread=(HANDLE)_beginthreadex(NULL, 0, threadFunc, NULL, 0, &threadID);
return hStart!=NULL && hStop!=NULL && hThread!=NULL;
}
VOID WINAPI ServiceMain(DWORD, LPTSTR*)
{
DWORD retSize;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
if ((hStatus=RegisterServiceCtrlHandler(_T("RehostStatus"), ControlHandler)) == 0)
// Registering Control Handler failed
return;
// Подправить свой токен
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &tokenProcess))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - OpenProcessToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
else
{
// Найти в системе привелегию работы в режиме операционной системы
if(!LookupPrivilegeValue(NULL, SE_TCB_NAME, &newToken.Privileges[0].Luid))
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - LookupPrivilegeValue() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
else
{
// Запросить разрешение этой привелегии
newToken.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
newToken.PrivilegeCount=1;
SetLastError(ERROR_SUCCESS);
// Применить привелегии к токену
if(!AdjustTokenPrivileges(tokenProcess, FALSE, &newToken, sizeof(oldToken),
&oldToken, &retSize) || GetLastError()==ERROR_NOT_ALL_ASSIGNED)
{
DWORD res=GetLastError();
MessageBox(NULL, static_cast<std::tostringstream&>(std::tostringstream()<<res<<_T(" - AdjustTokenPrivileges() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK);
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
}
}
if (!InitService())
{
// Initialization failed
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
}
else
{ // Ok.
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
// Работаен, пока не выключат
while(WaitForSingleObject(hThread, 1000)==WAIT_TIMEOUT) // Периодически проверяем...
if(std::tifstream("c:\\temp\\check.txt").is_open()) // ...что-то
SetEvent(hStart), // и при случае сигналим нитке
WaitForSingleObject(hStart, INFINITE); // и ждём её реакции
}
std::for_each(events.begin(), events.end(), CloseHandle);
CloseHandle(hThread);
}
int _tmain(int argn, TCHAR *argv[])
{
if(argn==2 && std::tstring(argv[1]) == _T("startGUI")) return startGUI();
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = _T("RehostStatus");
ServiceTable[0].lpServiceProc = ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
// Start the control dispatcher thread for our service
StartServiceCtrlDispatcher(ServiceTable);
}