Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2017.03.19;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.002 c
1-1329604920
Almaz
2012-02-19 02:42
2017.03.19
Запуск программы из сервиса с правами администратора


2-1436168887
Арт
2015-07-06 10:48
2017.03.19
Вопрос по базе Access


2-1436212529
Дмитрий С
2015-07-06 22:55
2017.03.19
Сравнение Double


2-1436599591
Alex118
2015-07-11 10:26
2017.03.19
Закрыть текущую вкладку в другой программе


1-1348636173
Дмитрий Белькевич
2012-09-26 09:09
2017.03.19
Как включить scroollbars у TShellTreeView?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский