从服务启动用户会话中的进程
在Windows Vista / 7/2008 / 2008R2中,是否可以通过服务在用户会话中启动进程? 具体来说,本地会话将是最有用的。
我一直在读的东西似乎是说这是不可能的,但我想我会在完全放弃之前问这里。
我在VB.NET编码,但会采取任何build议。
这是真的有可能。 您所遇到的主要问题是Windows应该被视为terminal服务器,用户会话被视为远程会话。 您的服务应该能够启动在远程会话中运行的进程属于用户。
顺便说一句,如果你编写的一个服务在Windows XP下运行,而不是添加到一个域,并且激活了快速的用户切换,你可以有同样的问题启动一个进程在第二个(第三个等)上运行,login用户桌面。
我希望你有一个用户令牌,例如你拿到的模拟方面,或者你有一个dwSessionId
的会话。 如果您没有它,可以尝试使用一些WTSfunction(远程桌面服务API http://msdn.microsoft.com/en-us/library/aa383464.aspx ,例如WTSEnumerateProcesses
或WTSGetActiveConsoleSessionId
)或LSA-API找出相应的用户会话( LsaEnumerateLogonSessions
请参阅http://msdn.microsoft.com/en-us/library/aa378275.aspx和;LsaGetLogonSessionData
请参阅http://msdn.microsoft.com/en-us/library/aa378290。; aspx )或ProcessIdToSessionId
(请参阅http://msdn.microsoft.com/en-us/library/aa382990.aspx )。
如果您知道用户令牌hClient
则可以使用带有参数TokenSessionId
(请参阅http://msdn.microsoft.com/zh-cn/library/aa446671.aspx )的GetTokenInformation
函数来接收用户会话的会话ID dwSessionId
。
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
来启用SE_TCB_NAME
特权(请参阅http://msdn.microsoft.com/en-us/library/aa446619.aspx作为模板)。; 启动一个具有TCB权限的进程是非常重要的,但是如果您的服务在本地系统下运行,则您拥有足够的权限。 顺便说一下,下面的代码片段不仅与本地系统帐户工作,但帐户必须具有SE_TCB_NAME
权限才能切换当前的terminal服务器会话。
还有一句话。 在上面的代码中,我们使用与当前进程相同的帐户(例如本地系统)启动新进程。 你改变一个代码来使用另一个帐户,例如用户令牌hClient
。 有一个primary token
只是重要的。 如果您有模拟令牌,则可以将其转换为主令牌,就像上面的代码一样。
在CreateProcessAsUser
使用的STARTUPINFO
结构中,应该使用lpDesktop =
WinSta0 \ Default“。
根据您的要求,也可能需要使用CreateEnvironmentBlock
创build一个新的环境块,您将传递给新的进程。
我build议你也阅读如何确保由Process.Start(ProcessStartInfo)启动的stream程窗口具有所有窗体的焦点? 在这里我描述了如何强制这个过程将在用户桌面的前台开始。
如果有帮助,我面临着类似的问题,但想要一个纯粹的PowerShell解决scheme。
我拼凑了一些其他网站的一些东西,并提出了这个问题:
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远程调用它
可以这样做,但是它不被认为是服务与用户会话直接交互的良好做法,因为它会造成严重的安全漏洞。
http://support.microsoft.com/kb/327618
更好的方法是创build2个程序,一个后端服务和一个前端客户端UI程序。 服务后端将始终运行,并使用WCF公开其操作(例如)。 客户端程序可以在用户会话启动时运行。
是的,使用CreateProcessAsUser你可以做到这一点。 MSDN有示例文章,并有一些注意事项。
在32位和64位体系结构中颠覆Vista UAC
提供的代码是针对Vista的,但也适用于Win7和Win10