![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Запуск программы из сервиса с правами администратора
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