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

Bra*_*rad 13 c# vb.net windows winapi windows-services

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

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

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

Ole*_*leg 21

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

顺便说一句,如果你编写一个在Windows XP下运行但没有添加到域并且快速用户切换被激活的服务,你可以遇到同样的问题来启动第二个运行的进程(第三个等等)登录用户桌面.

我希望你有一个用户令牌,你收到的例如关于模仿或你有一个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).

如果您知道用户令牌,则可以使用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, ...);
Run Code Online (Sandbox Code Playgroud)

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

再说一遍.在上面的代码中,我们使用与当前流程相同的帐户启动新流程(例如本地系统).您更改代码以更改使用其他帐户,例如用户令牌hClient.拥有一个是非常重要的primary token.如果您有模拟令牌,则可以将其转换为主令牌,与上面的代码完全相同.

在您使用的STARTUPINFO结构中CreateProcessAsUser应使用lpDesktop =WinSta0\Default".

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

我建议您还阅读如何确保Process.Start(ProcessStartInfo)启动的流程窗口具有所有Forms的焦点?我在哪里描述如何强制进程将在用户桌面上的前台启动.


Ste*_*eve 5

如果有帮助,我遇到了类似的问题,但想要一个纯粹的 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
}
Run Code Online (Sandbox Code Playgroud)

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