2010-03-11 106 views
14

我正在使用第三方Windows服务,它通过使用CreateProcessAsUser()运行脚本和可执行文件来处理一些自动化任务。由于UAC,我遇到了Windows Server 2008上的问题,并且通过API处理LUA提升的方式。从非交互式服务(win32/.net/powershell)启动UAC提升进程

该服务作为LocalSystem运行,但未启用“与桌面交互”。这些进程以管理员组中的用户身份运行,但不是管理员帐户(它受到许多UAC限制的豁免)。所有的UAC默认设置都已到位。

我可以将任意命令或PowerShell代码传递给该服务,但似乎无法“爆发”由服务启动的非提升非交互式进程。

问题的症结似乎是启动升级过程的唯一(公共)API选项是带有'runas'动词的ShellExecute(),但据我所知,不能从一个非交互式的服务,或者你会得到像“这个操作需要一个交互式窗口站”的错误。

我发现的唯一的解决方法就是这里所说的: http://www.eggheadcafe.com/software/aspnet/29620442/how-to-proper-use-sendinp.aspx

在Vista中,官方记录的方式 提升处理仅使用 外壳API的ShellExecute(防爆)(未 的CreateProcess或CreateProcessAsUser)。 因此,您的应用程序必须调用 ShellExecute(Ex)才能启动一个帮助程序 以提升调用SendInput。 此外,由于会话0 隔离,服务只能使用 CreateProcessAsUser或 CreateProcessWithLogonW(不能用 的ShellExecute(实施例))来指定 交互式桌面。

..我认为没有直接的方式来 产生从 窗口服务的升级过程。我们只能首先使用 CreateProcessAsUser或 CreateProcessWithLogonW将 未提升进程产生到用户会话(交互式桌面) 会话中。然后在 的非升级过程中,可能会使用ShellExecute(Ex) 为实际任务产生提升的 进程。

从.NET/PowerShell代码做到这一点,它看起来像我不得不做一些精细的P/Invoke东西调用CreateProcessAsUser或CreateProcessWithLogonW由于.NET System.Diagnostics.ProcessStartInfo不具有相当于我可以设置为“winsta0 \ default”的lpDesktop。我并不清楚LocalSystem是否有权调用CreateProcessAsUser或CreateProcessWithLogonW。

我也看了 http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspxProcess.Start with different credentials with UAC on

基于这一切,我到达的结论是,有这样做没有直接的方法。我错过了什么吗?这似乎并不像它应该那么难。感觉就像UAC从来没有设计过处理非交互式用例。

如果有任何微软的人最终读到这个,我注意到ShellExecute内部处理提升的方式是通过调用Application Information Service(AIS)。为什么对于通过一些Win32或.NET API可用的AIS进行相同的调用? http://msdn.microsoft.com/en-us/library/bb756945.aspx

对不起,跑了一会儿。感谢您的任何想法。

+0

反而说服务器核心不支持UAC。似乎证实我的评估。 http://blogs.technet.com/server_core/archive/2009/01/19/user-account-control-uac-and-server-core.aspx – 2010-03-11 16:37:01

+0

看到我的帖子在这里解释如何,特别是看看LinkedToken部分:http://brianbondy.com/blog/id/100/understanding-windows-at-a-deeper-level-sessions-window-stations-and-desktops – 2010-10-22 14:49:03

回答

16

打破会话零隔离的“官方”方法是使用终端服务API和CreateProcessAsUser()的组合来在用户会话中启动进程。在我以前的工作中,我们这样做了,因为我们需要在安装下载的更新之前通过服务向用户显示一个对话框所以,至少在WinXP,Win2K3,Vista和Win7上我知道它可行,但我不要指望Win 2K8会有太大的不同。基本上,过程如下:

  1. 呼叫WTSGetActiveConsoleSessionId()获取活动控制台会话ID(非常重要,因为交互式会话是总是会话1,即使在客户端系统)。如果没有活动用户登录到交互式会话(即,本地登录到物理机器,而不是使用RDP),则此API还会返回-1
  2. 将来自先前API调用的会话标识传递给WTSQueryUserToken()以获取表示登录到控制台的用户的开放标记。
  3. 致电DuplicateTokenEx()将模拟令牌(从WTSQueryUserToken)转换为主令牌。
  4. 致电CreateEnvironmentBlock()为流程创建一个新环境(可选,但如果您不这样做,流程将不会有)。
  5. 将第3步中的主令牌传递给CreateProccessAsUser()的调用以及可执行文件的命令行。如果您从步骤#4创建了一个环境块,则您也必须通过CREATE_UNICODE_ENVIRONMENT标志(始终)。这可能看起来很愚蠢,但如果你不这样做,API会失败(使用ERROR_INVALID_PARAMTER)。
  6. 如果你创建了一个环境块,那么你需要调用DestroyEnvironmentBlock,否则你会产生内存泄漏。该过程在启动时会分别获得环境块的副本,因此您只会销毁本地数据。

瞧! Windows有一些内在的魔力,你会看到应用程序的启动。不过,虽然这将从服务启动和互动过程,但我不确定它是否会绕过UAC(但不要引用我的意思)。换句话说,除非注册管理机构或内部清单声明要这样做,否则它可能不会启动为升级过程,即使如此,您仍然可能会得到UAC提示。如果您从第3步获得的令牌是受限令牌,则可以使用AdjustTokenPrivileges()来恢复提升(完整)令牌,但不要在此引用我的话。但是,正如MSDN文档中所述,请注意,无法对尚未拥有它们的令牌“添加”权限(例如,您不能通过使用AdjustTokenPrivileges将受限用户令牌转变为管理员;底层用户必须是管理员才能开始)。

在技术上可以从Win2K转发所有这些。但是,从WinXP开始,Win2K缺少WTSGetActiveConsoleSessionId()WTSQueryUserToken() API(以及用于Win2K Pro的WTSEnumerateProcesses()),所以它才真正可行。你可以硬编码0作为会话ID(因为在Win2K中情况总是如此),我想你可以通过枚举正在运行的进程并复制其中一个令牌来获得用户令牌(它应该是一个拥有交互式SID存在)。无论如何,即使您未从服务设置中选择“与桌面交互”,CreateProcessAsUser()在传递交互式用户令牌时的行为方式也是如此。它也比直接从服务直接启动更安全,因为该过程不会继承神圣的LocalSystem访问令牌。

现在,我不知道您的第三方应用程序在运行脚本/进程时是否执行了任何操作,但是如果您想从服务中执行此操作,那么(与Vista或Win7一样)克服会话0隔离的唯一方法)。

+3

没有必要调用'DuplicateTokenEx'。 'CreateProcessAsUser'会运行从'WTSQueryUserToken'获得的令牌的过程。您的回答非常详细,但不回答“如何从服务启动升级过程?(启用UAC)” – Ajay 2011-08-23 10:40:54

+1

确实没有。这会给你一个受限的令牌。 – Joshua 2013-09-19 03:41:17

+0

有以下几点需要注意:1)由于@Ajay提到'WTSQueryUserToken'已经返回一个主令牌,所以你可以跳过步骤3. 2)启用UAC后,你实际上需要在步骤2之后得到所谓的链接令牌,你可以通过'TokenLinkedToken'信息类来调用'GetTokenInformation',但只有在查询'TokenElevationType'时说明海拔是有限的。 3)如果你没有创建一个环境块,新的进程将具有创建过程的环境,这是你的服务 – 2017-12-07 21:09:43

1

根据您的使用情况,您可以做我所做的事情。我追踪活动会话的winlogon进程并窃取它的标记。如果没有活动会话(API返回-1),则在WINVER> = 6时使用1,否则为0.这将在活动会话中产生SYSTEM。