Форум: "Основная";
Текущий архив: 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 в вашем
> примере?
уже не помню, но по-моему без этого не работало.
← →
p © (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