Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2017.03.19;
Скачать: CL | DM;

Вниз

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

 
Almaz ©   (2012-02-19 02:42) [0]

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

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


 
Almaz ©   (2012-02-19 02:44) [1]

Код запуска сателлита:

function RunAs(Token: THandle; CommandLine: String): BOOL;
var
 Desktop: HDESK;
 WindowStation, WindowStationSave: THandle;
 LogonSID: PSID;
 PI: PROCESS_INFORMATION;
 SI: STARTUPINFO;
begin
 try
   WindowStationSave := OSErrorCheck(GetProcessWindowStation());
   WindowStation := OSErrorCheck(OpenWindowStation("winsta0", FALSE, READ_CONTROL or WRITE_DAC));
   try
     OSErrorCheck(SetProcessWindowStation(WindowStation));
     try
       Desktop := OSErrorCheck(OpenDesktop("default", 0, FALSE,
         READ_CONTROL or WRITE_DAC or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS));
       try
         OSErrorCheck(SetProcessWindowStation(WindowStationSave));
         WindowStationSave := 0;
         OSErrorCheck(GetLogonSID(Token, LogonSID));
         try
           OSErrorCheck(AddAceToWindowStation(WindowStation, LogonSID));
           OSErrorCheck(AddAceToDesktop(Desktop, LogonSID));
           OSErrorCheck(ImpersonateLoggedOnUser(Token));
           try
             ZeroMemory(@SI, SizeOf(STARTUPINFO));
             SI.cb := sizeof(STARTUPINFO);
             SI.lpDesktop := "winsta0\default";
             Result := CreateProcessAsUser(Token, PChar(CommandLine), nil, nil, nil,
               FALSE, NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE, nil, nil, SI, PI);
           finally
             RevertToSelf();
           end;
           if Result then
           begin
             if PI.hProcess <> INVALID_HANDLE_VALUE then
               CloseHandle(PI.hProcess);
             if PI.hThread <> INVALID_HANDLE_VALUE then
               CloseHandle(pi.hThread);
           end;
         finally
           FreeLogonSID(LogonSID);
         end;
       finally
         CloseDesktop(Desktop);
       end;
     finally
       if WindowStationSave <> 0 then
         SetProcessWindowStation(WindowStationSave);
     end;
   finally
     CloseHandle(WindowStation);
   end;
 except
   Result := False;
 end;
end;


 
Rouse_ ©   (2012-02-19 10:39) [2]

Попробуй вот этот пример, там немного по другому запускается.
http://rouse.drkb.ru/winapi.php#servicenotifyer


 
sniknik ©   (2012-02-19 11:45) [3]

> запускается под админом, но прав на изменение HKLM естественно не имеет.
а если в манифесте этого сателлита прописать права админа, реального админа. не поможет?


 
Rouse_ ©   (2012-02-19 12:19) [4]

Тогда, если я правильно помню, нельзя будет стартануть. Ошибка что-то там с ELEVATE будет связано.


 
DVM ©   (2012-02-19 14:38) [5]

Сдается мне, что тот экзешник, что запускают с правами админа, даже если внедрить туда манифест соответствующий, потребует еще цифровой подписи.


 
Almaz ©   (2012-02-19 15:24) [6]


> Rouse_ ©   (19.02.12 10:39) [2]

Сейчас попробую, спасибо.


> sniknik ©   (19.02.12 11:45) [3]

Как и сказал Rouse_ - не стартует вообще тогда. Цифровая подпись от VeriSign не помогает.

p.s. Задача точно имеет решение - планировщик заданий умеет запускать ПО от имени админа.


 
DVM ©   (2012-02-19 15:43) [7]


> Almaz ©   (19.02.12 15:24) [6]


>  Задача точно имеет решение - планировщик заданий умеет
> запускать ПО от имени админа.

На каком десктопе?


 
Almaz ©   (2012-02-19 15:48) [8]


> Rouse_ ©   (19.02.12 10:39) [2]

Увы, тот же эффект, но за код спасибо :)


 
Almaz ©   (2012-02-19 15:50) [9]


> На каком десктопе?

На дефолтном.


 
Eraser ©   (2012-02-20 01:21) [10]

> [0] Almaz ©   (19.02.12 02:42)

а устраивает запуск сателита от имени системы (а не админа), но в контексте текущего терминального юзера?


 
Eraser ©   (2012-02-20 01:22) [11]

> [5] DVM ©   (19.02.12 14:38)

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


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


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

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


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


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

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


 
Eraser ©   (2012-02-20 02:49) [14]

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

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

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


 
Eraser ©   (2012-02-20 02:51) [15]

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


 
Rouse_ ©   (2012-02-20 09:54) [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;


 
Almaz ©   (2012-02-20 16:16) [17]


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

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


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

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


 
Eraser ©   (2012-02-20 20:08) [18]

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


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

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


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

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


 
Eraser ©   (2012-02-20 20:10) [19]

   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;


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


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

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


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

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


 
Eraser ©   (2012-02-20 22:16) [21]

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


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

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


 
©   (2012-02-20 23:01) [22]

Под 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;


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


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

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


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

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


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

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


 
Almaz ©   (2012-02-21 14:27) [26]

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

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

Удачи.


 
имя   (2012-10-06 03:16) [27]

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


 
имя   (2012-10-06 03:16) [28]

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


 
имя   (2012-10-06 03:16) [29]

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


 
имя   (2012-10-06 03:17) [30]

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


 
имя   (2012-10-06 03:17) [31]

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



Страницы: 1 вся ветка

Текущий архив: 2017.03.19;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.005 c
2-1436168887
Арт
2015-07-06 10:48
2017.03.19
Вопрос по базе Access


2-1435930281
Степанов Михаил В.
2015-07-03 16:31
2017.03.19
Ошибка структуры БД


2-1436797835
Wadimkas
2015-07-13 17:30
2017.03.19
Не пойму в чем проблема Array and Char


15-1458927964
эндсоувот
2016-03-25 20:46
2017.03.19
???


2-1436523298
Цукор5
2015-07-10 13:14
2017.03.19
открыть папку по некоторому пути (Win7,8)