从服务启动用户会话中的进程

时间:2010-06-27 16:40:25

标签: c# vb.net windows winapi windows-services

在Windows Vista / 7/2008 / 2008R2中,是否可以通过服务在用户会话中启动进程?具体来说,本地会话最有用。

我一直在阅读的所有内容似乎都说这是不可能的,但我想在完全放弃之前我会问这里。

我在VB.NET中编码,但会接受任何建议。

5 个答案:

答案 0 :(得分:21)

真的有可能。您遇到的主要问题是Windows应被视为终端服务器,用户会话应视为远程会话。您的服务应该能够启动在远程会话中运行的进程属于用户。

顺便说一句,如果您编写的服务在Windows XP下运行但未添加到域中并且快速用户切换已激活,则可能会遇到相同的问题,无法在第二个上运行该进程(第三个和等等)登录用户桌面。

我希望您拥有一个用户令牌,例如您在模拟时收到的用户令牌,或者您有dwSessionId个会话。如果您没有它,您可以尝试使用一些WTS功能(远程桌面服务API http://msdn.microsoft.com/en-us/library/aa383464.aspx,例如WTSEnumerateProcessesWTSGetActiveConsoleSessionId)或LSA-API来查找相应的用户会话(LsaEnumerateLogonSessionshttp://msdn.microsoft.com/en-us/library/aa378275.aspxLsaGetLogonSessionDatahttp://msdn.microsoft.com/en-us/library/aa378290.aspx)或ProcessIdToSessionId(见http://msdn.microsoft.com/en-us/library/aa382990.aspx)。

如果您知道用户令牌{{},则可以使用GetTokenInformation函数和参数TokenSessionId(请参阅http://msdn.microsoft.com/en-us/library/aa446671.aspx)来接收用户会话的会话ID dwSessionId 1}}。

hClient

此代码只是一个架构。 BOOL bSuccess; HANDLE hProcessToken = NULL, hNewProcessToken = NULL; DWORD dwSessionId, cbReturnLength; bSuccess = GetTokenInformation (hClient, TokenSessionId, &dwSessionId, sizeof(DWORD), &cbReturnLength); bSuccess = OpenProcessToken (GetCurrentProcess(), MAXIMUM_ALLOWED, &hProcessToken); bSuccess = DuplicateTokenEx (hProcessToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hNewProcessToken); EnablePrivilege (SE_TCB_NAME); bSuccess = SetTokenInformation (hNewProcessToken, TokenSessionId, &dwSessionId, sizeof(DWORD)); bSuccess = CreateProcessAsUser (hNewProcessToken, NULL, szCommandToExecute, ...); 是一个使用EnablePrivilege来启用AdjustTokenPrivileges权限的简单函数(请参阅http://msdn.microsoft.com/en-us/library/aa446619.aspx作为模板)。重要的是,您启动流程的流程具有TCB权限,但如果您的服务在本地系统下运行,则您拥有足够的权限。顺便说一句,以下代码片段不仅适用于本地系统帐户,而且帐户必须具有SE_TCB_NAME权限才能切换当前终端服务器会话。

还有一句话。在上面的代码中,我们使用与当前流程相同的帐户启动新流程(例如本地系统)。您更改代码以更改使用其他帐户,例如用户令牌SE_TCB_NAME。拥有hClient非常重要。如果您有模拟令牌,则可以将其转换为主令牌,就像上面的代码一样。

primary token中使用的STARTUPINFO结构中,您应该使用CreateProcessAsUser WinSta0 \ Default“。

根据您的要求,还可能需要使用lpDesktop =来创建您将传递给新流程的新环境块。

我建议您阅读How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms?,其中我将介绍如何强制在用户桌面上启动进程。

答案 1 :(得分:4)

如果有帮助,我遇到了类似的问题,但想要一个纯粹的PowerShell解决方案。

我拼凑了其他网站上的一些内容并想出了这个:

function Invoke-CommandInSession 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ScriptBlock] $expression
    )

    $commandLine = “powershell“

    ## Convert the command into an encoded command for PowerShell
    $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($expression)
    $encodedCommand = [Convert]::ToBase64String($commandBytes)
    $args = “-Output XML -EncodedCommand $encodedCommand”


    $action = New-ScheduledTaskAction -Execute $commandLine -Argument $args
    $setting = New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter ([Timespan]::Zero)
    $trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date) + ([Timespan]::FromSeconds(5)))
    $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $setting
    Register-ScheduledTask "Invoke-CommandInSession - $([Guid]::NewGuid())" -InputObject $task
}

是的,它有点奇怪,但它确实有效 - 最重要的是你可以在ps遥控器中调用它

答案 2 :(得分:3)

是的,使用CreateProcessAsUser即可完成此操作。 MSDN有样本文章,但有一些警告。

答案 3 :(得分:3)

可以这样做,但服务与用户会话直接交互并不是一种好的做法,因为它可能会造成严重的安全漏洞。

http://support.microsoft.com/kb/327618

更好的方法是创建2个程序,后端服务和前端客户端UI程序。服务后端一直运行并使用WCF(例如)公开其操作。客户端程序可以在用户会话启动时运行。

答案 4 :(得分:0)

Subverting Vista UAC in Both 32 and 64 bit Architectures

提供的代码针对Vista,但也适用于Win7和Win10