запуск процесса с повышенными правами UAC из неинтерактивной службы (win32 /.net/powershell)
Я использую службу thrid party Windows, которая обрабатывает некоторые задачи автоматизации, выполняя скрипты и исполняемые файлы с помощью CreateProcessAsUser(). Я сталкиваюсь с проблемами в Windows Server 2008 из-за UAC и способа обработки высоты LUA через API.
служба работает как LocalSystem и не имеет" взаимодействовать с рабочим столом " включен. Процессы выполняются как пользователи в группе администраторы, но не учетная запись администратора (которая освобождается от многих UAC ограничения.) Все настройки UAC по умолчанию на месте.
Я могу передать произвольные команды или код powershell службе, но я не могу "вырваться" из не повышенного, неинтерактивного процесса, который запускается службой.
суть проблемы заключается в том, что единственным (общедоступным) вариантом API для запуска повышенного процесса является shellexecute() с глаголом "runas", но, насколько я могу судить, это не может быть вызвано из неинтерактивной службы или вы получаете ошибки типа "эта операция требует интерактивного оконного терминала".
единственный обходной путь, который я нашел, упоминается здесь: http://www.eggheadcafe.com/software/aspnet/29620442/how-to-proper-use-sendinp.aspx
в Vista, официальный документированный способ для повышения эффективности процесса используется только Shell API ShellExecute(Ex) (нет CreateProcess или CreateProcessAsUser). Поэтому ваше приложение должно вызвать ShellExecute (Ex) для запуска помощника повышен для вызова SendInput. Кроме того, из-за сессии 0 изоляция, служба может использовать только Вызовы createprocessasuser или CreateProcessWithLogonW (не может использовать ShellExecute (Ex)) для указания интерактивный рабочий стол.
..Я думаю, что нет прямого пути к spawn повышенный процесс от a служба Windows. Мы можем только сначала использовать Вызовы createprocessasuser или CreateProcessWithLogonW на нерест non-повышенный процесс в потребителя сеанс (интерактивный рабочий стол). Затем в этот без повышенных процесс, он может использовать ShellExecute (Ex) для нереста повышенного процесс для реальной задачи.
чтобы сделать это из кода .net/powershell, похоже, мне придется сделать некоторые сложные P/Invoke вещи для вызова CreateProcessAsUser или CreateProcessWithLogonW с Системы .Net.Диагностика.ProcessStartInfo не имеет эквивалента lpDesktop, который я мог бы установить в "winsta0default". И я не совсем понимаю, имеет ли LocalSystem права вызывать CreateProcessAsUser или CreateProcessWithLogonW.
Я также посмотрел на http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx и
2 ответов
"официальный" способ разорвать нулевую изоляцию сеанса-использовать комбинацию API служб терминалов и CreateProcessAsUser()
для запуска процесса в сеансе пользователя. На моей старой работе мы сделали именно это, поскольку нам нужно было отобразить диалоговое окно для пользователя из службы до установки загруженного обновления, поэтому я знаю, что он работает, по крайней мере, на WinXP, Win2K3, Vista и Win7, но я не ожидаю, что Win 2K8 будет слишком отличаться. В основном, процесс идет как следует:
- вызов
WTSGetActiveConsoleSessionId()
чтобы получить активный идентификатор сеанса консоли (очень важно, так как интерактивный сеанс не всегда сеанс 1, даже в клиентских системах). Этот API также вернет -1 если активный пользователь не вошел в интерактивный сеанс (то есть вошел локально на физическую машину, в отличие от использования RDP). - передайте идентификатор сеанса из предыдущего вызова API в
WTSQueryUserToken()
С открытой маркера reprents пользователь вошел в консоль. - вызов
DuplicateTokenEx()
для преобразования маркера олицетворения (отWTSQueryUserToken
) в основной маркер. - вызов
CreateEnvironmentBlock()
чтобы создать новую среду для процесса (необязательно, но если вы этого не сделаете, процесс не будет иметь ее). - передайте основной токен с шага #3 в вызов
CreateProccessAsUser()
вместе с командной строки для исполняемого файла. Если вы создали блок среды из шага #4, Вы должны передатьCREATE_UNICODE_ENVIRONMENT
флаг также (всегда.) Это может показаться глупым, но API терпит неудачу ужасно, если вы этого не делаете (сERROR_INVALID_PARAMTER
). - если вы создали блок среды, то вам необходимо позвонить
DestroyEnvironmentBlock
, в противном случае вы создадите утечку памяти. Процесс получает отдельную копию блока среды при его запуске, поэтому вы уничтожаете только локальные данные.
и вуаля! Windows делает некоторую внутреннюю магию, и вы видите запуск приложения. Однако, хотя это будет запуск и интерактивный процесс от службы, я не уверен, будет ли он обходить UAC (но не цитируйте меня об этом). Другими словами, он не может запускаться как повышенный процесс, если реестр или Внутренний манифест не скажут об этом, и даже тогда вы все равно можете получить приглашение UAC. Если токен, который вы получаете от шага #3, является ограниченным токеном, вы можете использовать AdjustTokenPrivileges()
чтобы восстановить повышенный (полный) токен,но не цитируйте меня на этом. Однако, как указано в документах MSDN, обратите внимание, что это невозможно "добавить" привилегии на маркер, который еще не имел их (например, вы не можете превратить маркер ограниченного пользователя в администратора с помощью AdjustTokenPrivileges
; для начала базовый пользователь должен быть администратором).
технически возможно сделать все это от Win2K вперед. Однако это действительно возможно только начиная с WinXP, так как Win2K не хватает WTSGetActiveConsoleSessionId()
и WTSQueryUserToken()
API (вместе с WTSEnumerateProcesses()
для Win2K Pro). Вы можете жестко кодировать 0 как идентификатор сеанса (так как это всегда случай в Win2K), и я полагаю, вы могли бы получить токен пользователя, перечислив запущенные процессы и дублируя один из их токенов (он должен быть тем, у которого есть интерактивный SID). Несмотря ни на что,CreateProcessAsUser()
будет вести себя так же при передаче интерактивного токена пользователя, даже если вы не выберете "взаимодействовать с рабочим столом" в настройках службы. Это также более безопасно, чем запуск непосредственно из службы в любом случае, так как процесс не унаследует божественный LocalSystem
маркер доступа.
теперь я не знаю, делает ли ваше стороннее приложение что-либо из этого, когда оно запускает скрипт/процесс, но если вы хотите сделать это из службы, вот как (и с Vista или Win7, это единственный способ преодолеть изоляцию сеанса 0).
в зависимости от вашего варианта использования вы можете делать то, что я делаю. Я выслеживаю процесс winlogon для активного сеанса и краду его токен. Если нет активного сеанса (API вернул -1), используйте 1, Если WINVER >= 6 в противном случае 0. Это приводит к системе на активном сеансе.