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

Вниз

Запуск приложений из сервиса с правами не LocalService   Найти похожие ветки 

 
Шуршик   (2005-03-04 03:35) [0]

Есть системный сервис, работающий под правами LOCAL SERVICE, необходимо через определённые промежутки времени запускать из него внешнюю программу.

Если запускать её как обычно (CreateProcess, все настройки по-умолчанию), то запускается дочерний к сервису процесс с пользователем LOCAL SERVICE. А мне надо бы, чтобы он запускался для пользователя, который работает в данный момент в системе. Как запустить процесс для текущего пользователя?

Я нашёл функции CreateProcessAsUser и LogonUser, но они требуют пары ИмяЮзера/Пароль. В тоже время некоторые системные сервисы умеют запускать процессы для уже залогиненых пользователей в системе. Они же не спрашивают пароля? Не могу никак найти, как же сделать так, чтобы, не спрашивая пароля для учётной записи пользователя, запустить из службы другой процесс с правами некоторого пользователя, с учётом того, что он уже вошёл в систему???


 
Игорь Шевченко ©   (2005-03-04 10:46) [1]


>  Не могу никак найти, как же сделать так, чтобы, не спрашивая
> пароля для учётной записи пользователя, запустить из службы
> другой процесс с правами некоторого пользователя, с учётом
> того, что он уже вошёл в систему???


Эти сервисы используют CreateProcessAsUser, узнавая Token необходимого пользователя.


 
Шуршик   (2005-03-05 00:45) [2]

Я не покажусь слишком назойливым, если спрошу, как это сделать? Дело в том, что мне самому это в голову пришло, но никак не могу найти "волшебную" последовательность вызовов функций.


 
Alex_Petr ©   (2005-03-05 14:09) [3]

Шуршик   (05.03.05 0:45) [2]
ИХМО, жмем F1 на CreateService и читаем
lpServiceStartName
[in] Pointer to a null-terminated string that specifies the name of the account under which the service should run. If the service type is SERVICE_WIN32_OWN_PROCESS, use an account name in the form DomainName\UserName. The service process will be logged on as this user. If the account belongs to the built-in domain, you can specify .\UserName.
Это то, что нужно ?


 
BiN ©   (2005-03-05 15:42) [4]

Шуршик   (04.03.05 03:35)  

Последовательность примерно такая:
function CreateProcessAsUser_(const Domain, UserName, Password, CommandLine: String):BOOL;
var
 hUserToken: DWORD;
 ...
begin
 if not LogonUser(Pchar(UserName), Pchar(Domain), Pchar(Password),
                 LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                 hUserToken)
   then RaiseLastOSError;
 Result:=CreateProcessAsUser(hUserToken, nil, Pchar(CommandLine), ....а дальше все то же, что и при обычном CreateProcess
 ...
 CloseHandle(hUserToken);
end;


Но сначала требуется знать, вообще залогинен ли кто-нибудь, и если да, то кто. Как вариант, можно попробовать найти shell-процесс (обычно это explorer.exe), и определить в контексте безопасности чьей учетной записи он работает. Также следует помнить, что в системах с сервером терминалов активных пользователей может быть несколько.


 
Шуршик   (2005-03-06 00:27) [5]

Вообще-то я спросил, как можно получить hUserToken от (процесса ли или ещё как) уже залогиненного пользователя.
Я пробовал взять токен из OpenProcessToken для explorer.exe и пихнуть его в CreateProcessAsUser, на что она отвечала ошибкой В доступе отказано.


 
Anatoly Podgoretsky ©   (2005-03-06 00:31) [6]

Какого именно из залогинившихся?


 
Набережных С. ©   (2005-03-06 06:23) [7]


> Шуршик   (06.03.05 00:27) [5]

Во-первых, непонятно, кто ответил отказом - OpenProcessToken или CreateProcessAsUser. Если вторая, то не тот флаг требуемого доступа выставил в первой. Во-вторых, для XP нужно действовать иначе. В ней свитчинг реализован через терминальные сессии, а у них весьма развитый API, особенно в ХР, это упрощает задачу. А для NT/2000 я приводил здесь пример года полтора-два назад, если хочешь, поищи в архивах по WinApi.


 
Шуршик   (2005-03-07 00:50) [8]

Доступ брался типа "ALL ACCESS", а AccessDenied отвечала CreateProcessAsUser. У меня стоит XP, но программа реально должна работать под Win2003. Чем иначе действовать под XP? И не подойдёт ли способ от 2000?

З.Ы. а в архиве щас пощелю...


 
Шуршик   (2005-03-07 01:08) [9]

Гыыы... в архивах я передумал щелить, уж больно много там качать...
Дайте хотя бы ссылочки на что-нибудь по теме


 
kami ©   (2005-03-07 01:14) [10]

например,  смотри соседнюю ветку
http://delphimaster.net/view/4-1110144139/

но - если верить Сергею Николаевичу (а неверить ему не вижу оснований), то енто только для XP-ей.

---
Аксиома:У программ нет глюков. Они просто содержат неизвестные тебе функции.


 
Набережных С. ©   (2005-03-07 07:29) [11]

Да ну, что ты, какие ссылки? Могу только предположить, по косвенным уликам, что это было в первой половине 2003-го.

А ты от какого пользователя и в какой станции пытаешься запустить процесс? Может эта учетка не имеет доступа к этой станции? Запускать-то надо в его оконной станции, либо дать ему доступ к целевой станции.


 
Набережных С. ©   (2005-03-07 09:38) [12]


> Шуршик   (07.03.05 00:50) [8]

Не внимательно прочитал сразу. Принцип-то везде одинаковый - получить маркер интерактивного пользователя и создать процесс в его оконной станции на активном рабочем столе. Просто до XP это всегда была WinSta0, а в ХР локально залогинившихся может быть несколько, в этом и разница. Как в 2003 не знаю, не доходят руки до него:(


 
Шуршик   (2005-03-08 06:10) [13]

Я был единственным вошедшим в систему и пытался запустить процесс из сервиса для самого себя. Сейчас пытаюсь разобраться с Windows Terminal Services. В связи с этим: нет ли у кого каких-нибудь ссылочек (о5 ссылочки :) ) на что-нибудь почитать по этой теме (не на форумах).


 
Набережных С. ©   (2005-03-08 09:04) [14]


> Шуршик   (08.03.05 06:10) [13]

Где запустить-то пытался? В какой сессии, в какой оконной станции, на каком столе? Суть не в том, сколько в данный момент пользователей залогинено, а в том, что сервис твой работает в одной оконной станции, а юзер в другой, да еще и в собственной сессии. Причем каждый юзер в собственной сессии с собственной оконной станцией. И все они друг от друга изолированы. Код бы лучше показал. А про сессии - MSDN, читать не перечитать:)


 
Шуршик   (2005-03-09 01:46) [15]

Код сеанса у всех - 0, создавать пробовал со станциями и столом по-умолчанию (nil в оба параметра) и следующим макаром (код взят из Jedi Code Library):

hWindowStation := GetProcessWindowStation;
WinStaName := GetUserObjectName (hWindowStation);
if WinStaName = "" then
WinStaName := CreateProcDEFWINSTATION;
if not SetUserObjectFullAccess (hWindowStation) then
begin
CloseHandle(hUserToken);
raise EJclWin32Error.CreateResRecFmt (@RsCreateProcSetStationSecurityError, [WinStaName]);
end;
hDesktop := GetThreadDesktop (GetCurrentThreadId);
DesktopName := GetUserObjectName (hDesktop);
if DesktopName = "" then
DesktopName := CreateProcDEFDESKTOP;
if not SetUserObjectFullAccess (hDesktop) then
begin
CloseHandle (hUserToken);
raise EJclWin32Error.CreateResRecFmt (@RsCreateProcSetDesktopSecurityError, [DesktopName]);
end;

З.Ы. в MSDN инфы действительно море, но после самостоятельного изучения DirectX по MSDN отпадает всякое желание изучать по нему ещё хоть что-то :)  Да, справочник это незаменимый, функцию какую посмотреть или ещё что - это всегда можно. Но вот так о чём-то просто почитать - это конечно не самый лучший источник информации. Ладно, всё равно спасибо.


 
Набережных С. ©   (2005-03-09 14:06) [16]

Я тут, роясь в MSDN, наткнулся на одну штуку, должно работать в W2k, XP и выше, но нужен локальный СОМ-сервер. В 2000 проверил, а XP сейчас не доступен, вечером проверю.
Делаем COM сервер, в нем объект, реализующий следующий интерфейс(это для примера, можно любой):

 IExecutor = interface(IDispatch)
   ["{5EF8BF3A-697D-4CBA-B6FF-9D9F74D7AF21}"]
   function Execute(const AppName: WideString): HResult; stdcall;
 end;

Реализация метода Execute:

function TExecutor.Execute(const AppName: WideString): HResult;
var
 H: Cardinal;
begin
 H:=ShellExecuteW(0, nil, PWideChar(AppName), nil, nil, SW_SHOWNORMAL);
 if H > 32 then Result:=S_OK else Result:=H;
end;

Хотя конечно можно CreateProcess, так для быстроты.
Ну а в сервисе вызываем вот такую функцию:

function ExecuteAsIU_XP2k(SessionID: Cardinal; const AppName: WideString;
                            out Executor: IExecutor): HResult;
var
 Bind: IBindCtx;
 Moniker: IMoniker;
 Fact: IClassFactory;
 Eater: integer;
 ws: WideString;
const
 s_Exec = "6D7760E2-165E-4D3A-850E-1F9A23A7A36B"; // Class identifier
label
 final_;
begin
 ws:=Format("Session:%d!clsid:%s", [SessionID, s_Exec]);
 CoInitialize(nil);
 try
   Result:=CreateBindCtx(0, Bind);
   if Result <> S_OK then GoTo final_;

   Result:=MkParseDisplayName(Bind, @ws[1], Eater, Moniker);
   if Result <> S_OK then GoTo final_;

   Result:=Moniker.BindToObject(Bind, nil, IClassFactory, Fact);
   if Result <> S_OK then GoTo final_;

   Result:=Fact.CreateInstance(nil, IID_IExecutor, Executor);
   if Result <> S_OK then GoTo final_;

   if AppName = "" then GoTo final_;

   Result:=Executor.Execute(AppName);

final_:
 finally
   CoUninitialize;
 end;
end;

Здесь s_Exec - GUID класса, реализующего интерфейс IExecutor, специально вынес в объявления. Да, сервер должен быть настроен на запуск под интерактивным пользователем, ну и права конечно на его запуск нужно дать.

Вызывать можно так:

var
 Exec: IExecutor;

ExecuteAsIU_XP2k(0, "Notepad.exe", Exec);

Первый параметр - идентификатор сессии, должен быть валидным, но 0-й всегда есть.
Собственно для чего:
если есть желающие, неплохо было бы проверить под разными системами и сообщить результат.


 
ламер_   (2005-03-09 14:41) [17]

Желающие есть. Com-server - где он? если в службах, то понятно. Если нет, то где его включать ?


 
Набережных С. ©   (2005-03-09 17:34) [18]


> ламер_   (09.03.05 14:41) [17]

СОМ сервер надо смому написать и зарегистрировать. А мне казалось, что я понятно изложил:( Цитата:

> Делаем COM сервер, в нем объект, реализующий следующий интерфейс

Т.е. надо создать новый проект приложения(не библиотеки!), добавить в него Automation object, назвать его Executor(чтобы меньше править приведенный выше код), и в его интерфейс добавить метод Execute. Откомпилировать и запустить один раз. Затем запустить DcomCnfg, найти в списке этот свой сервер и установить ему запуск от имени интерактивного пользователя. А в локальную константу записать ClsId этого созданного тобой объекта.

Суть этого дела в том, что мы создаем существующий в системах от W2k и выше моникер, с помощью биндера связываем его с нашим, написанным нами объекта, получаем интерфейс фабрики классов этого объекта и через него создаем уже наш объект. Вся схема предназначена для активации COM-серверов в контексте пользователя. А так как мы определили в нашем объекте метод Execute, то мы можем использовать его для запуска в контексте пользователя любого приложения.

Я уже проверил это дело в XP Pro SP2, работает как часы. Желательно бы еше в Home и в 2003.


 
ламер_   (2005-03-09 19:31) [19]

2  [18] Набережных С. ©   (09.03.05 17:34)


> А мне казалось, что я понятно изложил:

Прошу прощения, невнимательно прочитал - времени было мало. Ухватил только "но нужен локальный СОМ-сервер"Теперь, с последними пояснениями - почти кристально ясно. Осталась пара вопросов :
1)@ws[1] :Pointer to a zero-terminated wide character string (two bytes per character) containing the display name to be parsed - почему указатель на первый символ? или все дело в zero-terminated, так что длина строки не нужна ?
2) Ткните, пожалуйста, что называется носом в правила формирования строки ws - что-то в объявлении MkParseDisplayName я не нашел параметров Session и CLSID
3) Корректно ли вызывать CoUninitialize, или же, как пишет контора -  Release
Заранее спасибо.

Оффтопик : как Вы это все находите ???


 
Набережных С. ©   (2005-03-09 20:09) [20]

1) MkParseDisplayName принимает указатель на первый символ строки из Wide Chars, заканчивающийся двумя нулями. Мы и даем ей такой адрес. Почти то же самое получится, если написать PWideChar(ws), но я предпочитаю такую запись, если точно известно, что строка не пустая. Передавать длину нет нужды, признаком конца служат два завершающих нуля.

2) В описании MkParseDisplayName этого, разумеется, нет. Это строка служит для инициализации моникера и ее соднржимое полность определяется задачей. Грубо говоря, она служит для связывания моникера с объектом и с данными, которыми его следует инициализировать, или определяет место расположения объекта, или, как в данном случае, контекст активации объекта. В общем, прими за данность:) Тема моникеров очень сложная, пожалуй, самая сложная в COM. Не объяснишь ни в двух, ни в двадцати словах, слишком издалека надо начинать.
Здесь эта строка просто указывает моникеру какой объект нас интересует и в каком пользовательском контексте его следует активировать.

3) Не понял, о каком ты Release толкуешь. А про CoInitialize/CoUninitialize почитай обязательно. Если тебя COM интересует. А если не интересует, то просто оставь как есть:)

Оффтопик : Бог помогает(надеюсь):))


 
ламер_   (2005-03-09 20:36) [21]


> Не понял, о каком ты Release толкуешь.

Я про тот же MKParseDisplayName&CreateBindCtx и их последний параметр :
ppmk
[out] Address of IMoniker* pointer variable that.... When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. Ну и для остальных функций - то же самое, т.е.если я правильно понял, то MSDN предлагает вызвать Release для Moniker и Bind.


> А про CoInitialize/CoUninitialize почитай обязательно

Почитал, перед тем как писать [19]. Возник вопрос - а ежели (буде прибавится навыков и знаний) модифицируется код - (моить, появятся другие COM-объекты) ее нужно будет заменять? как я понял, она сам поток-то не закрывает, просто обрубает все "контакты"?

А смутило в основном:Only the CoUninitialize call corresponding to the CoInitialize or CoInitializeEx call that initialized the library can close it.


 
Набережных С. ©   (2005-03-09 21:07) [22]

CoInitialize/CoUninitialize производит инициализацию/деинициалиазацию подсистемы COM для данного потока, там много чего делается. НО! действие оказывает только самая внешняя пара, все остальные, вложенные, завершатся ошибкой, однако, для соблюдения баланса, для каждого вызова CoInitialize должен быть сделан парный вызов CoUninitialize, вне зависимости от успешности/неуспешности CoInitialize. Здесь, в принципе, фрейм должен быть внешний, вне функции. Но так как такой фрейм есть и внутри, то ничего страшного не произойдет, даже если снаружи инициализации не будет, просто полученный из функции интерфейс нельзя будет использовать.

> IUnknown::Release

Это совсем из другой оперы. Delphi сама делает вызовы AddRef и Release по мере надобности, так что вызывать их вручную требуется только в редких, особых случаях. Здесь все интерфейсы кроме выходного будут автоматически освобождены при выходе из функции.

Мы с тобой совсем уже ушли от темы ветки, давай-ка закругляться, брат:) Если интересно, создавай ветки, я не единственный на этом форуме, кому все это известно, можешь быть уверен:))
Да и спать мне уже пора. Спокойной ночи:)


 
ламер_   (2005-03-09 21:31) [23]

Спасибо !!! Все понял, что надо учиться. Спокойной ночи !!!


 
Шуршик   (2005-03-10 03:27) [24]

Ничего-ничего, правильный разговор завели :))))

Спасибо всем огромное! Узнал много нового...

З.Ы. а хитрый, однако, способ :)


 
Набережных С. ©   (2005-03-12 16:14) [25]

Нашел для XP. Ведь разбирался когда-то, и помню же, что совсем просто должно быть, а вот ведь как заклинило, а:( В общем, схема такая:

function ExecuteOnSession(SessionID: DWORD; const AppName: string): THandle;
var
 H: THandle;
 SI: TStartupInfo;
 PI: TProcessInformation;
begin
 Result:=0;
 if not WTSQueryUserToken(SessionID, H) then Exit;

 try

   FillChar(SI, SizeOf(SI), 0);
   SI.cb:=SizeOf(SI);
   if CreateProcessAsUser(H, nil, "Notepad.exe", nil, nil, false,
                    CREATE_NEW_CONSOLE,  nil, nil, SI, PI) then
   begin
     CloseHandle(PI.hThread);
     Result:=PI.hProcess;
   end;

 finally
   CloseHandle(H);
 end;
end;

function ExecuteAsIU_XP(const AppName: string): THandle;
var
 SessionID: DWORD;
begin
 SessionID:=WTSGetActiveConsoleSessionId;
 Result:=ExecuteOnSession(SessionID, AppName);
end;

Проверено на XP Pro SP2.

P.S. Да здравствует Пиво - главная движущая сила IT-индустрии!:)))


 
Andr-04   (2005-04-14 20:25) [26]

А ты, Набережных С., где откапал процедуру WTSGetActiveConsoleSessionId? В WTSApi.dll ничего подобного я не нашёл...


 
mgcr ©   (2005-04-14 20:52) [27]

Andr-04   (14.04.05 20:25) [26]

kernel32.dll


 
kami ©   (2005-04-14 21:08) [28]

2 [26] Andr-04   (14.04.05 20:25)
In first, не ты а Вы. Смотреть надо хотя бы на значек ©
In second Гюльчатай, покажи личико? ибо User Anonimus. Access denied
Ну и в-третьих Велика и могучи русская языка!


 
Andr-04   (2005-04-16 13:52) [29]

Ой, простите за невежливость - забылся, не только здесь писал... Кого можно на ты, а кого надо на Вы, хотя, я лично, на ты называю после получения на то разрешения. Ладно. С процедурой WTSGetActiveConsoleSessionId разобрался (никак не мог понять, почему всё время возвращается 0, но когда запустил программу от имени второго вошедшего в систему пользователя, то вернулась 1 - стало всё понятно). Теперь другая проблема - не могу разобраться с функцией WTSQueryUserToken. Почему-то значение второго параметра не изменяется. Его нужно как var описывать или не обязательно (в описании самой функции)? И ещё: можно ли получить строковое имя пользователя при помощи этой процедуры? Если нет, то какую функцию (процедуру) для этого нужно использовать? Заранее спасибо.


 
Andr-04   (2005-04-16 14:15) [30]

Блин? Что-то я сначала напишу, а потом думаю... GetLastError возвращает 1314. Это значит, что "The caller does not have the SE_TCB_NAME privilege". Соответственно, как эту SE_TCB_NAME привелегию получить?


 
Игорь Шевченко ©   (2005-04-16 14:37) [31]

Andr-04   (16.04.05 14:15) [30]


> The caller does not have the SE_TCB_NAME privilege". Соответственно,
> как эту SE_TCB_NAME привелегию получить?


Соответственно, эта привилегия по умолчанию имеется у учетной записи LocalSystem. У всех другие ее нет, если очень хочется, то надо явно прописывать у учетной записи.


 
Andr-04   (2005-04-16 14:41) [32]

А вообще данная функция даст мне строковое значение пользователя по его SessionID?


 
kami ©   (2005-04-16 14:43) [33]

посмотрите на
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/enabling_and_disabling_privileges_in_ c__.asp

Но если запускаете сервисом, то привилегия имеется:
The LocalSystem account has the following privileges:

SE_ASSIGNPRIMARYTOKEN_NAME
SE_AUDIT_NAME
SE_BACKUP_NAME
SE_CHANGE_NOTIFY_NAME
SE_CREATE_PAGEFILE_NAME
SE_CREATE_PERMANENT_NAME
SE_CREATE_TOKEN_NAME
SE_DEBUG_NAME
SE_INC_BASE_PRIORITY_NAME
SE_INCREASE_QUOTA_NAME
SE_LOAD_DRIVER_NAME
SE_LOCK_MEMORY_NAME
SE_PROF_SINGLE_PROCESS_NAME
SE_RESTORE_NAME
SE_SECURITY_NAME
SE_SHUTDOWN_NAME
SE_SYSTEM_ENVIRONMENT_NAME
SE_SYSTEM_PROFILE_NAME
SE_SYSTEMTIME_NAME
SE_TAKE_OWNERSHIP_NAME
SE_TCB_NAME SE_UNDOCK_NAME

(c)MSDN


 
Andr-04   (2005-04-16 14:44) [34]

Точнее, не самого пользователя, а лишь его имени :)). Если нет, то какая(какие) функция(функции) это может сделать?


 
Andr-04   (2005-04-16 14:45) [35]

Точнее, не самого пользователя, а лишь его имени :)). Если нет, то какая(какие) функция(функции) это может(могут) сделать?


 
kami ©   (2005-04-16 15:21) [36]

WTSQuerySessionInformation(0,WTSGetActiveConsoleSessionId,WTSUserName,@UserName,pr);



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

Форум: "WinAPI";
Текущий архив: 2005.06.06;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.57 MB
Время: 0.014 c
3-1114521903
RodmanDes
2005-04-26 17:25
2005.06.06
ADO


1-1116600883
Alex_Bredin
2005-05-20 18:54
2005.06.06
Как ограничить кол-во выделенных итемов в ListView


14-1116448885
Копир
2005-05-19 00:41
2005.06.06
Россия выиграла кубок УЕФА !!!


1-1116718562
grol
2005-05-22 03:36
2005.06.06
Ввод чисел перед переменными в выражении в строке Edit.


1-1116509769
Object
2005-05-19 17:36
2005.06.06
Как найти запись в XML без аттрибутов?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский