如何从Windows服务启动进程到当前login用户的会话

我需要从Windows服务启动一个程序。 该程序是一个用户UI应用程序。 而且,应用程序应该在特定的用户帐户下启动。

问题是在会话#0运行一个窗口服务,但login的用户会话是1,2等。

所以问题是:如何从窗口服务启动一个进程,使其运行在当前login的用户会话中?

我强调,这个问题不是关于如何在特定帐户下启动一个进程(这很明显 – Process.Start(new ProcessStartInfo(“..”){UserName = ..,Password = ..}))。 即使我安装我的Windows以当前用户帐户运行,服务将在会话#0运行。 设置“允许服务与桌面交互”不起作用。

我的Windows服务是基于.net的。

更新 :首先,.NET在这里没有任何事情,它实际上是纯粹的Win32的东西。 这是我正在做的。 下面的代码是在我的Windows服务(C#使用Win32函数通过P / Inkove,我跳过导入签名,他们都在这里 – http://www.pinvoke.net/default.aspx/advapi32/CreateProcessWithLogonW.html ):

var startupInfo = new StartupInfo() { lpDesktop = "WinSta0\\Default", cb = Marshal.SizeOf(typeof(StartupInfo)), }; var processInfo = new ProcessInformation(); string command = @"c:\windows\Notepad.exe"; string user = "Administrator"; string password = "password"; string currentDirectory = System.IO.Directory.GetCurrentDirectory(); try { bool bRes = CreateProcessWithLogonW(user, null, password, 0, command, command, 0, Convert.ToUInt32(0), currentDirectory, ref startupInfo, out processInfo); if (!bRes) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } catch (Exception ex) { writeToEventLog(ex); return; } WaitForSingleObject(processInfo.hProcess, Convert.ToUInt32(0xFFFFFFF)); UInt32 exitCode = Convert.ToUInt32(123456); GetExitCodeProcess(processInfo.hProcess, ref exitCode); writeToEventLog("Notepad has been started by WatchdogService. Exitcode: " + exitCode); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); 

代码转到“记事本已经由WatchdogService启动。Exitcode:”+ exitCode。 Exitcode是3221225794.并没有任何新的记事本启动。 我错在哪里?

Shrike的答案的问题是,它不适用于通过RDP连接的用户。
这是我的解决scheme,它在创build过程之前正确地确定当前用户的会话。 它已经过testing,可以在XP和7上运行。

https://github.com/murrayju/CreateProcessAsUser

你需要的一切都用一个静态方法包装成一个.NET类:

 public static bool StartProcessAsCurrentUser(string appPath, string cmdLine, string workDir, bool visible) 

我find了解决scheme
在Vista / 7的windows服务交互式会话中启动一个新的进程是非常有用的。

这样做不是个好主意。 虽然可能不是完全不可能的,但是微软做了所有事情,尽可能地做到这一点,因为它实现了所谓的“ 粉碎攻击” 。 看看Larry Osterman 在2005年写了些什么:

这是一个糟糕的主意,主要原因是交互式服务使一类被称为“Shatter”攻击的威胁(因为它们“粉碎窗口”,我相信)。

如果您search“破解攻击”,您可以看到这些安全威胁如何工作的一些细节。 微软还发布了知识库文章327618 ,扩展了有关交互式服务的文档,迈克尔·霍华德为MSDN图书馆撰写了一篇关于交互式服务的文章。 最初,破解攻击发生在具有背景窗口消息泵(已经固定)的Windows组件之后,但是它们也被用来攻击popupUI的第三方服务。

第二个原因是一个坏主意是SERVICE_INTERACTIVE_PROCESS标志不能正常工作。 系统会话中popup服务UI(通常是会话0)。 另一方面,如果用户正在另一个会话中运行,则用户从不会看到该UI。 在另一个会话中有两个主要场景有用户连接 – terminal服务和快速用户切换。 TS并不常见,但在有多人使用单台计算机的家庭情况下,FUS经常启用(例如,我们有4人几乎都在我们厨房的计算机上login)。

交互式服务是一个坏主意的第三个原因是交互式服务无法保证与Windows Vista兼容:)作为进入Windows Vista的安全强化过程的一部分,交互式用户login到系统会话以外的会话 – 第一个交互式用户在会话1中运行,而不是在会话0中运行。这具有完全切断膝盖碎裂攻击的效果 – 用户应用不能与运行在服务中的高权限窗口交互。

build议的解决方法是在用户的系统托盘中使用应用程序。

如果您可以放心地忽略上述问题和警告,则可以按照以下说明进行操作:

Windows开发:会话0隔离

这是我如何实现它。 它将尝试以当前login的用户(来自服务)启动一个进程。 这是基于几个来源放在一起工作的来源。

实际上,它确实是纯粹的WIN32 / C ++,因此可能不会对原始问题有100%的帮助。 但是我希望有一段时间能find类似的东西来挽救别人。

它需要Windows XP / 2003(不适用于Windows 2000)。 您必须链接到Wtsapi32.lib

 #define WINVER 0x0501 #define _WIN32_WINNT 0x0501 #include <Windows.h> #include <WtsApi32.h> bool StartInteractiveProcess(LPTSTR cmd, LPCTSTR cmdDir) { STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.lpDesktop = TEXT("winsta0\\default"); // Use the default desktop for GUIs PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); HANDLE token; DWORD sessionId = ::WTSGetActiveConsoleSessionId(); if (sessionId==0xffffffff) // Noone is logged-in return false; // This only works if the current user is the system-account (we are probably a Windows-Service) HANDLE dummy; if (::WTSQueryUserToken(sessionId, &dummy)) { if (!::DuplicateTokenEx(dummy, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &token)) { ::CloseHandle(dummy); return false; } ::CloseHandle(dummy); // Create process for user with desktop if (!::CreateProcessAsUser(token, NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi)) { // The "new console" is necessary. Otherwise the process can hang our main process ::CloseHandle(token); return false; } ::CloseHandle(token); } // Create process for current user else if (!::CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi)) // The "new console" is necessary. Otherwise the process can hang our main process return false; // The following commented lines can be used to wait for the process to exit and terminate it //::WaitForSingleObject(pi.hProcess, INFINITE); //::TerminateProcess(pi.hProcess, 0); ::CloseHandle(pi.hProcess); ::CloseHandle(pi.hThread); return true; } 

我在这里find了解决scheme:

http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite

我认为这是一个很好的链接。

我不知道如何在.NET中这样做,但是一般情况下,您需要使用Win32 API的CreateProcessAsUser()函数(或者它的.NET equivilent),指定所需的用户访问令牌和桌面名称。 这是我在我的C ++服务中使用,它工作正常。

在W10上实现了@murrayju代码到Windows服务中。 从Program Files运行可执行文件总是在VS中抛出Error -2。 我相信这是由于Service的开始path设置为System32。 指定workDir没有解决问题,直到我在StartProcessAsCurrentUser中的 CreateProcessAsUser之前添加以下行:

 if (workDir != null) Directory.SetCurrentDirectory(workDir); 

PS:这应该是一个评论,而不是一个答案,但我没有所需的声望呢。 花了我一会儿去debugging,希望能节省一些时间。