Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Запуск программы из сервиса с правами администратора


Страницы:   1 2

Almaz ©   (2012-02-19 02:42:00) 

Добрый день, уважаемые.

Имеется сервис, который общается с пользователем посредством программы-сателлита. Сателлит запускается в контексте каждого пользователя непосредственно сервисом. Сателлит, запущенный под аккаунтом администратора может управлять настройками сервиса, которые хранятся в реестре в HKLM. Все было замечательно, пока не настала Vista с ее UAC. При включенном UAC сателлит запускается под админом, но прав на изменение HKLM естественно не имеет.
Вопрос - можно ли (и если да, то как) запустить из сервиса сателлит с "высоким обязательным уровнем" ?


  (2012-10-06 03:17:00) [1]

Удалено модератором


  (2012-10-06 03:17:00) [2]

Удалено модератором


  (2012-10-06 03:16:00) [3]

Удалено модератором


  (2012-10-06 03:16:00) [4]

Удалено модератором


  (2012-10-06 03:16:00) [5]

Удалено модератором


Almaz   (2012-02-21 14:27:00) [6]

Добрый день, уважаемые!

Спасибо всем за помощь - проблема решена - ларчик открывался очень просто :)
После тщательного изучения MSDN, нашел следующее - можно получить unrestricted токен вызвав GetTokenInformation с параметром TokenLinkedToken для restricted токена. Для этого необходимо иметь привилегию SeTcbPrivilege. Которая у сервиса есть по умолчанию.

Удачи.


Eraser   (2012-02-21 00:47:00) [7]

ссылко http://sourceforge.net/projects/jedi-apilib/


Eraser   (2012-02-21 00:46:00) [8]

кстати, имеет смысл глянут сюда, а конкретнее на JWSCL (security library). Мощная вешь.


DVM   (2012-02-20 23:19:00) [9]


> p ©   (20.02.12 23:01) [22]

В коде бы все эти жуткие гирлянды if заменить на Win32Check. Всем бы жить проще стало :). И ошибки будут под присмотром.


p   (2012-02-20 23:01:00) [10]

Под Vista может копать в сторону CreateProcessWithToken, как то так

uses AccCtrl, AclAPI;

type
 PStartupInfoW = ^TStartupInfoW;
 _STARTUPINFOW = record
   cb: DWORD;
   lpReserved: PWideChar;
   lpDesktop: PWideChar;
   lpTitle: PWideChar;
   dwX: DWORD;
   dwY: DWORD;
   dwXSize: DWORD;
   dwYSize: DWORD;
   dwXCountChars: DWORD;
   dwYCountChars: DWORD;
   dwFillAttribute: DWORD;
   dwFlags: DWORD;
   wShowWindow: Word;
   cbReserved2: Word;
   lpReserved2: PByte;
   hStdInput: THandle;
   hStdOutput: THandle;
   hStdError: THandle;
 end;
 _STARTUPINFO = _STARTUPINFOW;
 TStartupInfoW = _STARTUPINFOW;

const
 LOW_INTEGRITY_SID: PWideChar = 'S-1-16-4096';
 MEDIUM_INTEGRITY_SID: PWideChar = 'S-1-16-8192';
 HIGH_INTEGRITY_SID: PWideChar = 'S-1-16-12288';
 SYSTEM_INTEGRITY_SID: PWideChar = 'S-1-16-16384';

 SE_GROUP_INTEGRITY = $00000020;

type
 _TOKEN_MANDATORY_LABEL = record
   Label_: SID_AND_ATTRIBUTES;
 end;

 TOKEN_MANDATORY_LABEL = _TOKEN_MANDATORY_LABEL;
 PTOKEN_MANDATORY_LABEL = ^TOKEN_MANDATORY_LABEL;

 TTokenMandatoryLabel = _TOKEN_MANDATORY_LABEL;
 PTokenMandatoryLabel = ^TTokenMandatoryLabel;

 TConvertStringSidToSidW = function(StringSid: PWideChar; var Sid: PSID): BOOL; stdcall;
 TCreateProcessWithTokenW = function(hToken: THandle;
   dwLogonFlags: DWORD;
   lpApplicationName: PWideChar;
   lpCommandLine: PWideChar;
   dwCreationFlags: DWORD;
   lpEnvironment: Pointer;
   lpCurrentDirectory: PWideChar;
   lpStartupInfo: PStartupInfoW;
   lpProcessInformation: PProcessInformation): BOOL; stdcall;

 TGetTokenInformation = function(TokenHandle: THandle;
   TokenInformationClass: TTokenInformationClass; TokenInformation: Pointer;
   TokenInformationLength: DWORD; var ReturnLength: DWORD): BOOL; stdcall;

 TSetTokenInformation = function(TokenHandle: THandle;
   TokenInformationClass: TTokenInformationClass; TokenInformation: Pointer;
   TokenInformationLength: DWORD): BOOL; stdcall;

 TCreateProcessAsUserW = function(hToken: THandle; lpApplicationName: PWideChar;
   lpCommandLine: PWideChar; lpProcessAttributes: PSecurityAttributes;
   lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL;
   dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PWideChar;
   const lpStartupInfo: TStartupInfoW; var lpProcessInformation: TProcessInformation): BOOL; stdcall;

var
 ConvertStringSidToSidW: TConvertStringSidToSidW;
 CreateProcessWithTokenW: TCreateProcessWithTokenW;
 GetTokenInformation: TGetTokenInformation;
 SetTokenInformation: TSetTokenInformation;
 CreateProcessAsUserW: TCreateProcessAsUserW;

function CreateProcessAsSystemW_Vista(
 ApplicationName: PWideChar;
 CommandLine: PWideChar;
 CreationFlags: DWORD;
 Environment: Pointer;
 CurrentDirectory: PWideChar;
 StartupInfo: TStartupInfoW;
 var ProcessInformation: TProcessInformation):
 Boolean;
var
 ProcessHandle, TokenHandle, ImpersonateToken: THandle;
 Sid: PSID;
 MandatoryLabel: PTOKEN_MANDATORY_LABEL;
 ReturnLength: DWORD;
 PIntegrityLevel: PWideChar;
begin
 Result := False;
 if (@CreateProcessWithTokenW = nil) then
   Exit;
 try
   ProcessHandle := OpenProcess(MAXIMUM_ALLOWED, False, 744{Winlogon.exe});
   if ProcessHandle <> 0 then
   begin
     try
       if OpenProcessToken(ProcessHandle, MAXIMUM_ALLOWED, TokenHandle) then
       begin
         try
           if DuplicateTokenEx(TokenHandle, MAXIMUM_ALLOWED, nil, SecurityImpersonation, TokenPrimary, ImpersonateToken) then
           begin
             try
               New(Sid);
               if (not GetTokenInformation(ImpersonateToken, TTokenInformationClass(TokenIntegrityLevel), MandatoryLabel, 0, ReturnLength)) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
               begin
                 MandatoryLabel := nil;
                 GetMem(MandatoryLabel, ReturnLength);
                 if MandatoryLabel <> nil then
                 begin
                   try
                     if GetTokenInformation(ImpersonateToken, TTokenInformationClass(TokenIntegrityLevel), MandatoryLabel, ReturnLength, ReturnLength) then
                     begin
                       PIntegrityLevel := HIGH_INTEGRITY_SID;
                       if ConvertStringSidToSidW(PIntegrityLevel, Sid) then
                       begin
                         MandatoryLabel.Label_.Sid := Sid;
                         MandatoryLabel.Label_.Attributes := SE_GROUP_INTEGRITY;
                         if SetTokenInformation(ImpersonateToken, TTokenInformationClass(TokenIntegrityLevel), MandatoryLabel, SizeOf(TOKEN_MANDATORY_LABEL) + GetLengthSid(Sid)) then
                         begin
                           Result := CreateProcessWithTokenW(ImpersonateToken, 0, ApplicationName, CommandLine, CreationFlags, Environment, CurrentDirectory, @StartupInfo, @ProcessInformation);
                           SetLastError(0);
                         end;
                       end;
                     end;
                   finally
                     FreeMem(MandatoryLabel);
                   end;
                 end;
               end;
             finally
               CloseHandle(ImpersonateToken);
             end;
           end;
         finally
           CloseHandle(TokenHandle);
         end;
       end;
     finally
       CloseHandle(ProcessHandle);
     end;
   end;
 except
 end;
end;  

procedure TForm1.FormCreate(Sender: TObject);
var
 LibraryHandle: HMODULE;
begin
   LibraryHandle := LoadLibrary('Advapi32.dll');
   if LibraryHandle <> 0 then
   begin
     @ConvertStringSidToSidW := GetProcAddress(LibraryHandle, 'ConvertStringSidToSidW');
     @CreateProcessWithTokenW := GetProcAddress(LibraryHandle, 'CreateProcessWithTokenW');
     @GetTokenInformation := GetProcAddress(LibraryHandle, 'GetTokenInformation');
     @SetTokenInformation := GetProcAddress(LibraryHandle, 'SetTokenInformation');
     @CreateProcessAsUserW := GetProcAddress(LibraryHandle, 'CreateProcessAsUserW');
     FreeLibrary(LibraryHandle);
     LibraryHandle := 0;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 StartupInfo: TStartupInfoW;
 ProcessInformation: TProcessInformation;
begin
 ZeroMemory(@StartupInfo, SizeOf(TStartupInfoW));
 FillChar(StartupInfo, SizeOf(TStartupInfoW), 0);
 StartupInfo.cb := SizeOf(TStartupInfoW);
 StartupInfo.lpDesktop := 'WinSta0\Default';
 if CreateProcessAsSystemW_Vista(
   PWideChar(WideString(Edit1.Text)),
   PWideChar(WideString(Edit1.Text + ' -commandline')),
   NORMAL_PRIORITY_CLASS,
   nil,
   nil,
   StartupInfo,
   ProcessInformation) then
 begin
   CloseHandle(ProcessInformation.hThread);
   CloseHandle(ProcessInformation.hProcess);
 end;
end;


Eraser   (2012-02-20 22:16:00) [11]

> [20] Almaz ©   (20.02.12 21:22)


> Кстати, а какой смысл в создании EnvironmentBlock в вашем
> примере?

уже не помню, но по-моему без этого не работало.


Almaz   (2012-02-20 21:22:00) [12]


> тогда странны эти телодвижения с SetProcessWindowStation,
>  ImpersonateLoggedOnUser.

Эти телодвижения наследие 2000 винды до SP2 - сейчас в процессе экспериментов упрощено до  простого вызова CreateProcessAsUser. Кстати, а какой смысл в создании EnvironmentBlock в вашем примере?


> проверенной 100% информации нет, но править токен надо с
> пом. Get/SetTokenInformation, я бы посмотрел на параметры
> TokenElevation, TokenHasRestrictions и т.п. Возможно единицу
> на нолик поправить и все заработает.

Сейчас копаю скорее в направлении TokenIntegrityLevel. А вот четкой информации и правда нет, увы :(


Eraser   (2012-02-20 20:10:00) [13]

   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;


Eraser   (2012-02-20 20:08:00) [14]

> [17] Almaz ©   (20.02.12 16:16)


> Собственно, оно у меня так и есть - просто это получение
> происходит вне процедуры запуска - поэтому я его тут и не
> привел.

тогда странны эти телодвижения с SetProcessWindowStation, ImpersonateLoggedOnUser. Это лишнее, imho. И может как раз привести к не работоспособности метода, т.к. не будет хватать прав, попробуйте убрать, просто оставив цепочку WTSQueryUserToken, DuplicateTokenEx, CreateEnvironmentBlock, CreateProcessAsUser в текущем контексте безопасности.


> Вот именно над этим я и бьюсь сейчас - есть ли у вас какая-
> нибудь информация по данной тематике ?

проверенной 100% информации нет, но править токен надо с пом. Get/SetTokenInformation, я бы посмотрел на параметры TokenElevation, TokenHasRestrictions и т.п. Возможно единицу на нолик поправить и все заработает.


Almaz   (2012-02-20 16:16:00) [15]


> Eraser ©   (20.02.12 02:49) [14]
> пробовали брать token, не так как у вас сейчас, а через
> WTSQueryUserToken?
> т.е. цепочка WTSQueryUserToken, DuplicateTokenEx, CreateEnvironmentBlock,
>  CreateProcessAsUser.

Собственно, оно у меня так и есть - просто это получение происходит вне процедуры запуска - поэтому я его тут и не привел.


> если напрямую не получится избавиться от UAC, то возможно
> можно вручную подкорректировать токен, подправив restricted
> sid.

Вот именно над этим я и бьюсь сейчас - есть ли у вас какая-нибудь информация по данной тематике ?


Rouse_   (2012-02-20 09:54:00) [16]


> планировщик заданий умеет

Так может тогда и запускать планировщиком?
Там код в принципе простой. вот кусок в качестве примера:

function InstallTask(const Value: TRemoteInstallParam): DWORD;
var
 SchedulerAgent: ITaskScheduler;
 ppUnk: IUnknown;
 Task: ITask;
 PersistFile: IPersistFile;
 SharePath: string;
begin
 Result := S_OK;
 try
   OleCheck(CoInitialize(nil));
   // Получаем интерфейс планировщика задач
   OleCheck(CoCreateInstance(CLSID_CSchedulingAgent, nil, CLSCTX_INPROC_SERVER,
     IID_ITaskScheduler, SchedulerAgent));
   // Подключаемся к удаленному компьютеру
   OleCheck(SchedulerAgent.SetTargetComputer(
     StringToOleStr(Value.Workstation)));
   // Получаем интерфейс задачи
   if SchedulerAgent.Activate(StringToOleStr(Value.TaskName),
     IID_ITask, ppUnk) <> S_OK then
     // Если интерфес не получаен, то создаем новый
     OleCheck(SchedulerAgent.NewWorkItem(StringToOleStr(Value.TaskName),
       CLSID_CTask, IID_IScheduledWorkItem, ppUnk));
   OleCheck(ppUnk.QueryInterface(ITask, Task));
//    SchedulerAgent.Delete(StringToOleStr(Value.TaskName));
//    Exit;
   // Указываем путь, откуда будет производится
   // запуск устанавливаемого приложения
   SharePath := ExpangShareName(Value.ShareName, Value.InstallSource);
   if SharePath = '' then
   begin
     Result := GetLastError;
     Exit;
   end;
   OleCheck(Task.SetApplicationName(StringToOleStr(SharePath)));
   // Устанавливаем логин/пароль
   if Value.UserName = '' then
     OleCheck(Task.SetAccountInformation('', nil))
   else
     OleCheck(Task.SetAccountInformation(StringToOleStr(Value.UserName),
       StringToOleStr(Value.Password)));
   // Выставляем флаги "удалять задачу по завершении" и "скрыть задачу"
   OleCheck(Task.SetFlags(TASK_FLAG_DELETE_WHEN_DONE{ or TASK_FLAG_HIDDEN}));
   // Получаем интерфейс для сохранения задачи
   OleCheck(Task.QueryInterface(IID_IPersistFile, PersistFile));
   // Сохраняем задачу
   OleCheck(PersistFile.Save(nil, True));
   // Запускаем задачу
   OleCheck(Task.Run);
 except
   on E: EOleException do
     Result := E.ErrorCode;
 end;
end;


Eraser   (2012-02-20 02:51:00) [17]

http://blogs.msdn.com/b/itasupport/archive/2010/03/29/uac-bypass-o-meglio-il-modo-supportato-e-by-design-di-aggirare-la-uac.aspx


Eraser   (2012-02-20 02:49:00) [18]

> [12] Almaz ©   (20.02.12 01:51)

пробовали брать token, не так как у вас сейчас, а через WTSQueryUserToken?
т.е. цепочка WTSQueryUserToken, DuplicateTokenEx, CreateEnvironmentBlock, CreateProcessAsUser.

если напрямую не получится избавиться от UAC, то возможно можно вручную подкорректировать токен, подправив restricted sid.


Германн   (2012-02-20 01:53:00) [19]


> цифровая подпись ни на что не влияет, кроме цвета предупреждения
> в окне UAC.

<offtop> Невольно вспомнилась моя старая программа связанная с "охранкой". Она требовала от пользователя (оператора охранной системы) определенных действий в определенной ситуации. Первое сообщение было зелёным, второе желтым, третье красным :) </offtop>
Прошу прощения за оффтоп.


Almaz   (2012-02-20 01:51:00) [20]


> Eraser ©   (20.02.12 01:21) [10]

Сателлит должен быть запущен в контексте каждого вошедшего пользователя. Если пользователь администратор, то сателлит должен иметь HIGH INTEGRITY LEVEL


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо