Re[10]: Запуск процесса из сервиса
От: SergH Россия  
Дата: 15.07.03 22:44
Оценка: 31 (3) :)
Здравствуйте, Аноним, Вы писали:

А>>ЛЕГКО — но пока сам не пробовал

SH>Но раз уж такое дело, сегодня вечером попробую, о результатах доложу.

Очень коротко о результатах: работает.

Коротко о результатах:
0. Написал некое UI
примерно такая основная рабочая функция:
void Work::Run()
{
    if (!IsInteractive())
    {
        MessageBox(NULL, TEXT("Not interactive!"), TEXT("TrayService"), MB_OK | MB_SERVICE_NOTIFICATION);

        if (!MakeInteractive())
        {
            MessageBox(NULL, TEXT("Sevice yet is not interactive! It can not work normal."), TEXT("TrayService"), MB_OK | MB_SERVICE_NOTIFICATION);

            StdServFunc::FatalError(NO_ERROR, 0);
            return;
        }
    }

    HWND hwnd = CreateDialog(
                    GetModuleHandle(NULL), 
                    MAKEINTRESOURCE(IDD_DIALOG),
                    NULL,
                    DlgProc);

    ShowWindow(hwnd, SW_SHOWNORMAL);

    for (;;)
    {
        DWORD res = MsgWaitForMultipleObjects(1, &hEndEvent, FALSE, INFINITE, QS_ALLEVENTS);

        if (res == WAIT_OBJECT_0)
        {
            // событие hEndEvent - кончаем работать
            PostMessage(hwnd, WM_COMMAND, IDCANCEL, 0);
        }

        MSG msg;
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT) break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    StdServFunc::FatalError(NO_ERROR, 0);
}


и такая диалоговая функция

BOOL CALLBACK Work::DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int time = 130;
    NOTIFYICONDATA data = {0};

    data.cbSize = sizeof(data);
    data.hWnd   = hwnd;
    data.uID    = 1;
    
    switch (uMsg)
    {
    case WM_INITDIALOG:

        data.uFlags = NIF_ICON | NIF_MESSAGE;
        data.uCallbackMessage = WM_USER + 200;
        data.hIcon = LoadIcon(NULL, IDI_HAND);

        Shell_NotifyIcon(NIM_ADD, &data);
        break;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hwnd, 0);
            Shell_NotifyIcon(NIM_DELETE, &data);
            return TRUE;
        }
        break;

    }

    return FALSE;
}


1. написал функцию IsInteractive. Без проверок на ошибки она выглядит так:
bool IsInteractive()
{
    SC_HANDLE hSCM;
    hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

    SC_HANDLE hService;    
    hService = OpenService(hSCM, ServiceName, SERVICE_QUERY_CONFIG);
    
    DWORD need = 0;
    QueryServiceConfig(hService, NULL, 0, &need);

    QUERY_SERVICE_CONFIG* pConfig = (QUERY_SERVICE_CONFIG*) malloc(need);

    QueryServiceConfig(hService, pConfig, need, &need);

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCM);

    DWORD type = pConfig->dwServiceType;

    free(pConfig);

    return (type & SERVICE_INTERACTIVE_PROCESS);
}


2. Написал функцию MakeInteractive. Сначала она выглядела так:
bool MakeInteractive()
{
    HWINSTA hWinSta0 = OpenWindowStation(L"WinSta0", FALSE, WINSTA_READSCREEN);

    if (!hWinSta0)
    {
        return false;
    }

    SetProcessWindowStation(hWinSta0);
    CloseWindowStation(hWinSta0);

    HDESK hDefault = OpenDesktop(L"Default", 0, FALSE, DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU);

    if (!hDefault)
    {
        return false;
    }

    SetThreadDesktop(hDefault);

    CloseDesktop(hDefault);
    return true;
}


3. Неинтерактивная служба, но запущенная под LocalSystem пробивается к десктопу на ура.

4. Неинтерактивная служба, запущенная под админом не может открыть оконную станцию.

5. Немного подумав переписал MakeInteractive так:
bool MakeInteractive()
{
    HWINSTA hWinSta0 = OpenWindowStation(L"WinSta0", FALSE, WINSTA_READSCREEN);

    if (!hWinSta0)
    {
        if (GetLastError() != ERROR_ACCESS_DENIED)
        {
            return false;
        }

        // Включаем привелегию SE_TAKE_OWNERSHIP_NAME 
        EnableTakeOwnershipName();

        hWinSta0 = OpenWindowStation(L"WinSta0", FALSE, WRITE_OWNER);

        if (hWinSta0)
        {
            // Делаем текущего пользователя владельцем. Этот этап
            // не критичен, критичен следующий, поэтому здесь
            // ошибки не проверяем.
            SetUserObjectOwner(hWinSta0);
            CloseWindowStation(hWinSta0);
        }

        hWinSta0 = OpenWindowStation(L"WinSta0", FALSE, WRITE_DAC | READ_CONTROL);

        if (!hWinSta0)
        {
            return false;
        }
        
        // Даём текущему пользователю доступ. Я поступил радикально - обнулил DACL :)
        if (!SetUserObjectAccess(hWinSta0))
        {
            CloseWindowStation(hWinSta0);
            return false;
        }

        CloseWindowStation(hWinSta0);

        hWinSta0 = OpenWindowStation(L"WinSta0", FALSE, WINSTA_READSCREEN);

        if (!hWinSta0)
        {
            return false;
        }
    }

    SetProcessWindowStation(hWinSta0);
    CloseWindowStation(hWinSta0);

    HDESK hDefault = OpenDesktop(L"Default", 0, FALSE, DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU);

    if (!hDefault)
    {
        return false;
    }

    SetThreadDesktop(hDefault);

    CloseDesktop(hDefault);
    return true;
}


Код функций
bool EnableTakeOwnershipName();
bool SetUserObjectOwner(HANDLE hUser);
bool SetUserObjectAccess(HANDLE hUser);

не привожу, он довольно прост. Из под админа теперь работает.

Ну что я могу сказать — спасибо тебе, добрый человек! Наконец-то я начал потихоньку трахаться с security...
Делай что должно, и будь что будет
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.