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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3128017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:43:27  来源:igfitidea点击:

Launching a process in user’s session from a service

c#vb.netwindowswinapiwindows-services

提问by Brad

In Windows Vista/7/2008/2008R2, is it at all possible to launch a process in a user's session from a service? Specifically, the local session would be most useful.

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

Everything I've been reading seems to say this isn't possible, but I figured I'd ask here before giving up completely.

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

I'm coding in VB.NET, but will take suggestions in anything.

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

回答by Oleg

It is really possible. The main problem which you have is that Windows should be seen as a terminal server and a users session as a remote session. Your service should be able to start a process which run in the remote session belongs to the user.

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

By the way, if you write a service which run under Windows XP which is not added to a domain and the fast user switching is activated, you can have the same problems to start a process on running on the second (third and so on) logged users desktop.

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

I hope you have a user token, which you receive for example with respect of impersonation or you have a dwSessionIdof session. If you don't have it you can try use some WTS-function (Remote Desktop Services API http://msdn.microsoft.com/en-us/library/aa383464.aspx, for example WTSEnumerateProcessesor WTSGetActiveConsoleSessionId) or LSA-API to find out the corresponding users session (LsaEnumerateLogonSessionssee http://msdn.microsoft.com/en-us/library/aa378275.aspxand LsaGetLogonSessionDatasee http://msdn.microsoft.com/en-us/library/aa378290.aspx) or ProcessIdToSessionId(see http://msdn.microsoft.com/en-us/library/aa382990.aspx).

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

You can use GetTokenInformationfunction with the parameter TokenSessionId(see http://msdn.microsoft.com/en-us/library/aa446671.aspx) to receive the session id dwSessionIdof the users session if you knows the users token hClient.

如果您知道用户令牌,则可以使用GetTokenInformation带有参数的函数TokenSessionId(请参阅http://msdn.microsoft.com/en-us/library/aa446671.aspx)来接收dwSessionId用户会话的会话 ID 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, ...);

This code only a schema. EnablePrivilegeis a simple function used AdjustTokenPrivilegesto enable SE_TCB_NAMEprivilege (see http://msdn.microsoft.com/en-us/library/aa446619.aspxas a template). It is important that the process from which you are start a process you have TCB privilege, but if your service run under the Local System you have enough permissions. By the way, following code fragment work with not only Local System account, but the account must have SE_TCB_NAMEprivilege to be able to switch current terminal server session.

这段代码只是一个模式。EnablePrivilege是一个用于AdjustTokenPrivileges启用SE_TCB_NAME特权的简单函数(参见http://msdn.microsoft.com/en-us/library/aa446619.aspx作为模板)。重要的是,您启动的进程具有 TCB 权限,但如果您的服务在本地系统下运行,则您有足够的权限。顺便说一句,以下代码片段不仅适用于本地系统帐户,而且该帐户必须具有SE_TCB_NAME能够切换当前终端服务器会话的权限。

One more remark. In the code above we start new process with the same account as the current process have (for example Local System). You change change a code to use another account for example the users token hClient. It is only important to have a primary token. If you have an impersonation token you can convert it to the primary token exactly like in the code above.

再说一句。在上面的代码中,我们使用与当前进程相同的帐户(例如本地系统)启动新进程。您更改更改代码以使用另一个帐户,例如用户令牌hClient。只有拥有一个primary token. 如果您有一个模拟令牌,您可以像上面的代码一样将其转换为主要令牌。

In the STARTUPINFOstructure used in CreateProcessAsUseryou should use lpDesktop =WinSta0\Default".

在您使用的STARTUPINFO结构中,CreateProcessAsUser您应该使用lpDesktop =WinSta0\Default”。

Depend on your requirements it could be also needed to use CreateEnvironmentBlockto create an new environment block that you will be passing to the new process.

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

I recommend you also to read How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms?where I describe how to force that the process will be started in foreground on the users desktop.

我还建议您阅读如何确保 Process.Start(ProcessStartInfo) 启动的进程窗口具有所有表单的焦点?我在这里描述了如何强制该进程在用户桌面的前台启动。

回答by Steve

If it helps, i was faced with a similar problem, but wanted a pure powershell solution.

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

I cobbled together bits from other websites and came up with this:

我从其他网站拼凑了一些东西,然后想出了这个:

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
}

Yes, its a little weird, but it works - and most importantly you can call it from within ps remoting

是的,它有点奇怪,但它有效 - 最重要的是你可以从 ps remoting 中调用它

回答by holtavolt

Yes, using CreateProcessAsUseryou can do this. MSDN has sample articles, and there are some caveats.

是的,使用CreateProcessAsUser您可以做到这一点。MSDN 有示例文章,但有一些注意事项。

回答by Bear Monkey

It can be done but its not considered good practice for services to interact directly with users' sessions as it can create serious security holes.

可以这样做,但对于服务直接与用户的会话进行交互,这不被认为是一种良好的做法,因为它会产生严重的安全漏洞。

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

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

A better approach is to create 2 programs, a backend service and a front end client UI program. The service backend runs all the time and exposes its operations using WCF (for instance). The client program could be run at startup of a user's session.

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

回答by tchelidze

Subverting Vista UAC in Both 32 and 64 bit Architectures

在 32 位和 64 位架构中颠覆 Vista UAC

Code provided is targeted to Vista, but works on Win7 and Win10 too

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