Конференция "WinAPI" » запуск программы не из под SYSTEM
 
  • Sinister (04.12.12 10:39) [0]
    Есть сервис запускаемый от имени пользователя SYSTEM (так нужно). В определенный момент времени сервис запускает программу-установщик (setup.exe).

    Проблема: программа-установщик тоже запускается от имени пользователя SYSTEM. Из-за этого скрывается само диалоговое окно установщика.

    Как решить эту проблему? Можно ли как-нибудь запускать setup.exe от имени другого пользователя?
  • Cobalt © (04.12.12 10:57) [1]
    RunProcessAsUser
    А еще можно предусмотреть фоновую программу-клиент, которая по команде от сервиса будет выполнять нужные действия.
  • Dimka Maslov © (04.12.12 11:23) [2]
    Во первых не RunProcessAsUser, а CreateProcessAsUser, а во вторых не всё так просто. Программа, запущенная сервисом из-под усера всё равно получает кастрированные права. Чтобы этого избежать надо делать примерно так

    BOOL SetUserObjectFullAccess(HANDLE UserObject)
    {
           PSECURITY_DESCRIPTOR Sd;
           SECURITY_INFORMATION Si;
           Sd = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
           InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION);
           SetSecurityDescriptorDacl(Sd, TRUE, NULL, FALSE);
           Si = DACL_SECURITY_INFORMATION;
           BOOL Result = SetUserObjectSecurity(UserObject, &Si, Sd);
           LocalFree((HLOCAL)Sd);
           return Result;
    }

    DWORD StartProcess(char *CmdLine, char *User, char *Domain, char *Pass)
    {

           HANDLE Token;
           PROCESS_INFORMATION ProcessInfo;
           STARTUPINFO StartupInfo;

           if ( ! LogonUser(User, Domain, Pass,
                   LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &Token) )
                   return GetLastError();
           ImpersonateLoggedOnUser(Token);
           SetUserObjectFullAccess(GetThreadDesktop(GetCurrentThreadId()));
           SetUserObjectFullAccess(GetProcessWindowStation());
           memset(&StartupInfo, 0, sizeof(StartupInfo));
           StartupInfo.cb = sizeof(StartupInfo);
           StartupInfo.wShowWindow = SW_SHOW;
           DWORD Code = 0;
           if (! CreateProcessAsUser(Token, NULL, CmdLine, NULL, NULL,
                   FALSE, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, NULL, Path, &StartupInfo, &ProcessInfo) )
                   Code = GetLastError();
           RevertToSelf();
           CloseHandle(Token);
           return Code;
    }
  • Sinister (04.12.12 13:16) [3]
    А можно ли как-нибудь обойтись без указания пароля пользователя, например, как в XP: Запуск от имени другого пользователя -> Учетную запись текущего пользователя.
  • Rouse_ © (04.12.12 13:24) [4]
    Вот тут посмотри вариант: http://rouse.drkb.ru/winapi.php#servicenotifyer
  • Sinister (04.12.12 13:45) [5]

    > Вот тут посмотри вариант: http://rouse.drkb.ru/winapi.php#servicenotifyer
    >
    >


    hInteractiveToken

    всегда приходит равным
    INVALID_HANDLE_VALUE

    (
    function ShowNotify(const Value: string): DWORD;

    )
  • Rouse_ © (04.12.12 13:50) [6]
    Система какая? Что GetLastError говорит?
  • Sinister (04.12.12 13:57) [7]

    > Система какая?


    XP


    > Что GetLastError говорит?


    возвращает ошибку 1314 -
    ERROR_PRIVILEGE_NOT_HELD



    Что не так?
  • Rouse_ © (04.12.12 14:05) [8]
    Похоже что отключена привилегия SE_TCB_NAME
  • Sinister (04.12.12 14:19) [9]
    не понимаю как в вашем коде добавить привилегию SE_TCB_NAME для токена, можете показать?

    для проверки, этот код я добавил к себе в сервис:

    type
     TWTSQueryUserToken = function(
       SessionID: DWORD; var Token: THandle): BOOL; stdcall;

      function WTSGetActiveConsoleSessionId: DWORD; stdcall;
       external 'kernel32.dll';

    //  Êàëëáýê âûçûâàåìûé â ñëó÷àå çàïóñêà ñåðâèñà ïîä W2K.
    //  Åãî çàäà÷à ïîëó÷èòü òîêåí ëþáîãî äîñòóïíîãî ïðîöåññà,
    //  îêíà êîòîðîãî íàéäåíû â ðàìêàõ çàäàííîãî ïðè ïåðå÷èñëåíèè äåñêòîïà.
    // =============================================================================
    function EnumDesktopWindowsCallback(
     WndHandle: THandle; Param: LPARAM): BOOL; stdcall;
    var
     ProcessID: DWORD;
     ProcessHandle, UserToken: THandle;
    begin
     Result := True;
     GetWindowThreadProcessId(WndHandle, ProcessID);
     ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessID);
     if ProcessHandle <> 0 then
     try
       if OpenProcessToken(ProcessHandle, TOKEN_ALL_ACCESS, UserToken) then
       begin
         PDWORD(Param)^ := UserToken;
         Result := False;
       end;
     finally
       CloseHandle(ProcessHandle);
     end;
    end;

  • Sinister (04.12.12 14:19) [10]

    //  &#205;&#229;&#239;&#238;&#241;&#240;&#229;&#228;&#241;&#242;&#226;&#229;&#237;&# 237;&#238; &#231;&#224;&#239;&#243;&#241;&#234; &#243;&#226;&#229;&#228;&#238;&#236;&#235;&#255;&#254;&#249;&#229;&#227;&#238; &#239;&#240;&#232;&#235;&#238;&#230;&#229;&#237;&#232;&#255;,
    //  &#226; &#234;&#238;&#237;&#242;&#229;&#234;&#241;&#242;&#229; &#232;&#237;&#242;&#229;&#240;&#224;&#234;&#242;&#232;&#226;&#237;&#238;&#227;&# 238; &#228;&#229;&#241;&#234;&#242;&#238;&#239;&#224;
    // =============================================================================
    function ShowNotify(const Value: string): DWORD;
    const
     WINDOW_STATION_NAME = 'Winsta0';
     APPLICATION_DESKTOP_NAME = 'Default';
    var
     hLib: THandle;
     hCurrentWinStation, hInteractiveWorkstation: HWINSTA;
     hDefaultDesktop: HDESK;
     SI: TStartupInfo;
     PI: TProcessInformation;
     SessionId: DWORD;
     hInteractiveToken: THandle;
     WTSQueryUserToken: TWTSQueryUserToken;
    begin
     Result := NO_ERROR;
     hInteractiveToken := INVALID_HANDLE_VALUE;
     if (Win32MajorVersion = 5) and (Win32MinorVersion = 0) then
     begin
       // &#194; &#241;&#235;&#243;&#247;&#224;&#229; W2K
       hCurrentWinStation := GetProcessWindowStation;
       // &#206;&#242;&#234;&#240;&#251;&#226;&#224;&#229;&#236; &#240;&#224;&#225;&#238;&#247;&#243;&#254; &#241;&#242;&#224;&#237;&#246;&#232;&#254; &#239;&#238;&#235;&#252;&#231;&#238;&#226;&#224;&#242;&#229;&#235;&#255;
       hInteractiveWorkstation := OpenWindowStation(
         PChar(WINDOW_STATION_NAME), False, MAXIMUM_ALLOWED);
       if hInteractiveWorkstation = 0 then Exit;
       try
         // &#207;&#238;&#228;&#234;&#235;&#254;&#247;&#224;&#229;&#236; &#234; &#237;&#229;&#233; &#237;&#224;&#248; &#239;&#240;&#238;&#246;&#229;&#241;&#241;
         if not SetProcessWindowStation(hInteractiveWorkstation) then Exit;
         try
           // &#206;&#242;&#234;&#240;&#251;&#226;&#224;&#229;&#236; &#232;&#237;&#242;&#229;&#240;&#224;&#234;&#242;&#232;&#226;&#237;&#251;&#233; &#228;&#229;&#241;&#234;&#242;&#238;&#239;
           hDefaultDesktop := OpenDesktop(PChar(APPLICATION_DESKTOP_NAME),
             0, False, MAXIMUM_ALLOWED);
           if hDefaultDesktop = 0 then Exit;
           try
             // &#207;&#229;&#240;&#229;&#247;&#232;&#241;&#235;&#255;&#229;&#236; &#238;&#234;&#237;&#224; &#228;&#229;&#241;&#234;&#242;&#238;&#239;&#224; &#241; &#246;&#229;&#235;&#252;&#254; &#232;&#231;&#226;&#235;&#229;&#247;&#252;
             // &#242;&#238;&#234;&#229;&#237; &#231;&#224;&#235;&#238;&#227;&#232;&#237;&#229;&#237;&#237;&#238;&#227;&#238; &#239;&#238;&#235;&#252;&#231;&#238;&#226;&#224;&#242;&#229;&#235;&#255;
             EnumDesktopWindows(hDefaultDesktop, @EnumDesktopWindowsCallback,
               Integer(@hInteractiveToken));
           finally
             CloseDesktop(hDefaultDesktop);
           end;
         finally
           SetProcessWindowStation(hCurrentWinStation);
         end;
       finally
         CloseWindowStation(hInteractiveWorkstation);
       end;
     end
     else
     begin
       // &#194; &#241;&#235;&#243;&#247;&#224;&#229; Windows &#213;&#208; &#232; &#226;&#251;&#248;&#229; &#239;&#238;&#228;&#227;&#240;&#243;&#230;&#224;&#229;&#236; &#225;&#232;&#225;&#235;&#232;&#238;&#242;&#229;&#234;&#243;
       hLib := LoadLibrary('Wtsapi32.dll');
       if hLib > HINSTANCE_ERROR then
       begin
         // &#207;&#238;&#235;&#243;&#247;&#224;&#229;&#236; &#224;&#228;&#240;&#229;&#241; &#244;&#243;&#237;&#234;&#246;&#232;&#232; WTSQueryUserToken
         @WTSQueryUserToken := GetProcAddress(hLib, 'WTSQueryUserToken');
         if Assigned(@WTSQueryUserToken) then
         begin
           // &#207;&#238;&#235;&#243;&#247;&#224;&#229;&#236; ID &#241;&#229;&#241;&#241;&#232;&#232; &#226; &#240;&#224;&#236;&#234;&#224;&#245; &#234;&#238;&#242;&#238;&#240;&#238;&#233;
           // &#226;&#229;&#228;&#229;&#242; &#240;&#224;&#225;&#238;&#242;&#243; &#231;&#224;&#235;&#238;&#227;&#232;&#237;&#229;&#237;&#251;&#233; &#239;&#238;&#235;&#252;&#231;&#238;&#226;&#224;&#242;&#229;&#235;&#252;
           SessionID := WTSGetActiveConsoleSessionId;
           // &#207;&#238;&#235;&#243;&#247;&#224;&#229;&#236; &#242;&#238;&#234;&#229;&#237; &#239;&#238;&#235;&#252;&#231;&#238;&#226;&#224;&#242;&#229;&#235;&#255;
           WTSQueryUserToken(SessionID, hInteractiveToken);
         end;
       end;
     end;
     if hInteractiveToken = INVALID_HANDLE_VALUE then
     begin
       Result := GetLastError;
       Exit;
     end;
     // &#207;&#238;&#241;&#235;&#229; &#242;&#238;&#227;&#238; &#234;&#224;&#234; &#242;&#238;&#234;&#229;&#237; &#239;&#238;&#235;&#243;&#247;&#229;&#237; - &#239;&#240;&#238;&#232;&#231;&#226;&#238;&#228;&#232;&#236; &#231;&#224;&#239;&#243;&#241;&#234; &#241;&#224;&#236;&#238;&#227;&#238; &#241;&#229;&#225;&#255;
     // &#241; &#239;&#224;&#240;&#224;&#236;&#229;&#242;&#240;&#238;&#236; notify &#232; &#239;&#224;&#240;&#224;&#236;&#229;&#242;&#240;&#224;&#236;&#232;, &#234;&#238;&#242;&#238;&#240;&#251;&#229; &#237;&#229;&#238;&#225;&#245;&#238;&#228;&#232;&#236;&#238; &#238;&#242;&#238;&#225;&#240;&#224;&#231;&#232;&#242;&#252;
     try
       ZeroMemory(@SI, SizeOf(TStartupInfo));
       SI.cb := SizeOf(TStartupInfo);
       SI.lpDesktop := PChar(WINDOW_STATION_NAME + '\' +
         APPLICATION_DESKTOP_NAME);
       if not CreateProcessAsUser(hInteractiveToken,
         PChar(ParamStr(0)),
         PChar('\"' + ParamStr(0) + '\" -notify ' + Value), nil, nil, False,
         NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
         Result := GetLastError;
     finally
       CloseHandle(hInteractiveToken);
     end;
    end;

  • Rouse_ © (04.12.12 14:26) [11]
    Выставить вот так, потом перезагрузиться нужно: http://www.sql.ru/forum/actualthread.aspx?tid=85311

    И кстати, под ХР EnumDesktopWindowsCallback выполнятся не должен
  • Sinister (04.12.12 14:45) [12]

    > Rouse_ ©  


    все заработало, спасибо.
  • han_malign (04.12.12 16:38) [13]

    > Выставить вот так, потом перезагрузиться нужно: http://www.sql.ru/forum/actualthread.aspx?tid=85311

    - в общем - бредовенько, у учетки SYSTEM уже есть привилегия SE_TCB_NAME, её нужно просто включить для жетона процесса, как показано там же:
    http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=85311&msg=626866
    - без всяких перезагрузок.
    Вот если AdjustTokenPrivileges скажет ERROR_NOT_ALL_ASSIGNED - тады ой...
    (и ошибка, по идее, должна быть не 1314, а 1313 - ERROR_NO_SUCH_PRIVILEGE)
  • Rouse_ © (04.12.12 16:48) [14]
    Ну по логике на дефолтовой системе вообще никаких танцев с бубном не нужно, однакож встречались ситуевины
  • han_malign (05.12.12 08:24) [15]

    > Ну по логике на дефолтовой системе вообще никаких танцев
    > с бубном не нужно, однакож встречались ситуевины

    - не - начиная с XP SP2 кажется(лень статью искать, давно это было) - почти все привилегии по умолчанию отключены...
    Также - настойчиво рекомендуется отключать их сразу после "использования"...
  • Rouse_ © (05.12.12 10:40) [16]
    Не должны ибо код по моей ссылке тестировался на XP SP3 тестовой (чистая ось, специально для тестов)
  • Sinister (06.12.12 15:41) [17]
    Большое спасибо Rouse_за помощь, допилил код, тестил на XP, 7 и на 8, вроде все работает.

    Задача была следующая: сервис, запущенный из под SYSTEM в определенный момент времени проверяет скачивалось ли обновление для программы. Если скачивалось, то:

    (1). Если есть залогиненный пользователь, то программа установки обновления запускается от его имени. Это нужно для того, чтобы можно было управлять процессом установки обновления. Была проблема, когда был запущен перезаписываемый файл. Из-за того, что программа установки была запущена из-под SYSTEM не выводился диалог, предлагающий закрыть этот файл и повторить перезапись. В итоге все висло.

    (2). Если пользователь не залогиннился (система ожидает входа пользователя, но сервис работает), то программа установки обновления запускается из под SYSTEM.

    Посмотрите, все ли OK с кодом, может что-то не предусмотрел (поддержку Win2K не включал)?


    procedure RunAs(const FileName, Params: string);
    const
     WND_STATION_NAME = 'Winsta0';
     APP_DESKTOP_NAME = 'Default';
    resourcestring
     CANT_CREATE_PROCESS_WITH_USER_TOKEN = 'Can''t create process with user token (error code = %d).';
     INVALID_USER_TOKEN = 'Invalid user token (error code: %d).';
    var
     UserTokenHandle: THandle;
     PI: TProcessInformation;
     SI: TStartupInfo;
    begin
     if WTSQueryUserToken(WTSGetActiveConsoleSessionId, UserTokenHandle) then
     begin
       try
         ZeroMemory(@SI, SizeOf(TStartupInfo));
         SI.cb := SizeOf(TStartupInfo);
         SI.lpDesktop := PChar(WND_STATION_NAME + '\' + APP_DESKTOP_NAME);
         if not CreateProcessAsUser(UserTokenHandle, nil,
           PChar('\"' + FileName + '\" ' + Params), nil, nil, False,
           NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
           raise Exception.CreateResFmt(@CANT_CREATE_PROCESS_WITH_USER_TOKEN,
             [GetLastError]);
       finally
         CloseHandle(UserTokenHandle);
       end;
     end
     else
     begin
       if GetLastError = ERROR_NO_TOKEN then
         ShellExecute(0, nil, PChar(FileName), PChar(Params), nil, SW_SHOW)
       else
         raise Exception.CreateResFmt(@INVALID_USER_TOKEN, [GetLastError]);
     end;
    end;

  • Eraser © (06.12.12 23:58) [18]

    > Sinister   (06.12.12 15:41) [17]

    небольшое уточнение. нельзя путать запуск от имени какого-то пользователя (в т.ч. SYSTEM) и запуск в определенной терминальной сессии. Вполне возможно запустить программу от имени SYSTEM, но в терминальной сессии другого пользователя, в т.ч. консольного.
  • Sinister (07.12.12 12:40) [19]
    В инете встречаю решения аналогичной задачи, там помимо
    WTSQueryUserToken

    и
    CreateProcessAsUser

    еще используюся фунции
    DuplicateTokenEx

    и
    CreateEnvironmentBlock

    и при успешном
    CreateProcessAsUser

    идет закрытие хендлов:
    CloseHandle(PI.hProcess)

    и
    CloseHandle(PI.hThread);

    . В коде Rouse_ этого нет. Это нужно? Вроде без этого и так работает.

    Такая последовательность:

    if not WTSQueryUserToken(SessionId, hToken) then
            begin
              dwReturned := GetLastError;
              if dwReturned = ERROR_NOT_LOGGED_ON then
                Exit;
              AppendLog('Error #16 @' + IntToStr(GetLastError));
              Exit;
            end;

          try
            if not DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nil, SecurityIdentification,
              TokenPrimary, hNewToken) then
            begin
              AppendLog('Error #17 @' + IntToStr(GetLastError));
              Exit;
            end;
            if not CreateEnvironmentBlock(lpEnvironment, hNewToken, false) then
            begin
              AppendLog('Error #18 @' + IntToStr(GetLastError));
              Exit;
            end;
            // Запуск процесса.
            si.lpDesktop := 'winsta0\default';
            if CreateProcessAsUser(hNewToken, nil, PWideChar(APath),
              nil, nil, false, CREATE_UNICODE_ENVIRONMENT,
              lpEnvironment, nil, si, pi) then
            begin
              CloseHandle(pi.hProcess);
              CloseHandle(pi.hThread);
            end
            else
            begin
              AppendLog('Error #19 @' + IntToStr(GetLastError));
              Exit;
            end;
          finally
            if hToken <> 0 then
              CloseHandle(hToken);
            if hNewToken <> 0 then
              CloseHandle(hNewToken);
            if lpEnvironment <> nil then
              DestroyEnvironmentBlock(lpEnvironment);
          end;

 
Конференция "WinAPI" » запуск программы не из под SYSTEM
Есть новые Нет новых   [134430   +2][b:0][p:0.013]