使用Windows服务的凭据启动进程

我有一个作为mydomain \ userA运行的Windows服务。 我希望能够从服务运行任意.exes。 通常,我使用Process.Start(),它工作正常,但在某些情况下,我想运行可执行文件作为不同的用户(mydomain \ userB)。

如果我更改ProcessStartInfo来启动进程以包含凭据,那么我将会收到错误 – 错误对话框中显示“应用程序未能正确初始化(0xc0000142)。单击确定以终止应用程序”或“访问被拒绝”Win32Exception。 如果我从命令行运行stream程启动代码,而不是在服务中运行它,则stream程将开始使用正确的凭据(我已通过设置ProcessStartInfo来运行whoami.exe并捕获命令行输出)。

我也尝试使用WindowsIdentity.Impersonate()模仿,但是这并没有工作 – 据我所知,模拟只影响当前的线程,并开始一个新的进程inheritance进程的安全描述符,而不是当前的线程。

我在一个孤立的testing域中运行这个,所以userA和userB都是域pipe理员,并且都在域范围内具有“login即服务”权限。

当您使用ProcessStartInfo启动新进程时,该进程将在启动进程的相同窗口站和桌面启动。 如果您使用的是不同的凭据,那么用户通常没有足够的权限在该桌面上运行。 当user32.dll尝试在新进程中初始化并且不能初始化错误时,会导致失败。

要解决这个问题,您必须首先检索与窗口工作站和桌面相关的安全描述符,并为您的用户添加适当的权限到DACL,然后在新凭据下启动您的过程。

编辑:关于如何做到这一点和示例代码的详细说明是在这里有点长,所以我把代码放在一起的文章 。

//The following security adjustments are necessary to give the new //process sufficient permission to run in the service's window station //and desktop. This uses classes from the AsproLock library also from //Asprosys. IntPtr hWinSta = GetProcessWindowStation(); WindowStationSecurity ws = new WindowStationSecurity(hWinSta, System.Security.AccessControl.AccessControlSections.Access); ws.AddAccessRule(new WindowStationAccessRule("LaunchProcessUser", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ws.AcceptChanges(); IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId()); DesktopSecurity ds = new DesktopSecurity(hDesk, System.Security.AccessControl.AccessControlSections.Access); ds.AddAccessRule(new DesktopAccessRule("LaunchProcessUser", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ds.AcceptChanges(); EventLog.WriteEntry("Launching application.", EventLogEntryType.Information); using (Process process = Process.Start(psi)) { } 

根据@StephenMartin的答案 。

使用Process类启动的新进程与启动Process在相同的窗口工作站和桌面上运行。 如果您使用不同凭据运行新进程,则新进程将无权访问窗口站点和桌面。 什么导致错误,如0xC0000142。

以下是允许用户访问当前窗口工作站和桌面的“紧凑型”独立代码。 它不需要AsproLock库。

 public static void GrantAccessToWindowStationAndDesktop(string username) { const int WindowStationAllAccess = 0x000f037f; GrantAccess(username, GetProcessWindowStation(), WindowStationAllAccess); const int DesktopRightsAllAccess = 0x000f01ff; GrantAccess(username, GetThreadDesktop(GetCurrentThreadId()), DesktopRightsAllAccess); } private static void GrantAccess(string username, IntPtr handle, int accessMask) { SafeHandle safeHandle = new NoopSafeHandle(handle); GenericSecurity security = new GenericSecurity( false, ResourceType.WindowObject, safeHandle, AccessControlSections.Access); security.AddAccessRule( new GenericAccessRule( new NTAccount(username), accessMask, AccessControlType.Allow)); security.Persist(safeHandle, AccessControlSections.Access); } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetProcessWindowStation(); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetCurrentThreadId(); // All the code to manipulate a security object is available in .NET framework, // but its API tries to be type-safe and handle-safe, enforcing a special implementation // (to an otherwise generic WinAPI) for each handle type. This is to make sure // only a correct set of permissions can be set for corresponding object types and // mainly that handles do not leak. // Hence the AccessRule and the NativeObjectSecurity classes are abstract. // This is the simplest possible implementation that yet allows us to make use // of the existing .NET implementation, sparing necessity to // P/Invoke the underlying WinAPI. private class GenericAccessRule : AccessRule { public GenericAccessRule( IdentityReference identity, int accessMask, AccessControlType type) : base(identity, accessMask, false, InheritanceFlags.None, PropagationFlags.None, type) { } } private class GenericSecurity : NativeObjectSecurity { public GenericSecurity( bool isContainer, ResourceType resType, SafeHandle objectHandle, AccessControlSections sectionsRequested) : base(isContainer, resType, objectHandle, sectionsRequested) { } new public void Persist(SafeHandle handle, AccessControlSections includeSections) { base.Persist(handle, includeSections); } new public void AddAccessRule(AccessRule rule) { base.AddAccessRule(rule); } #region NativeObjectSecurity Abstract Method Overrides public override Type AccessRightType { get { throw new NotImplementedException(); } } public override AccessRule AccessRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) { throw new NotImplementedException(); } public override Type AccessRuleType { get { return typeof(AccessRule); } } public override AuditRule AuditRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) { throw new NotImplementedException(); } public override Type AuditRuleType { get { return typeof(AuditRule); } } #endregion } // Handles returned by GetProcessWindowStation and GetThreadDesktop should not be closed private class NoopSafeHandle : SafeHandle { public NoopSafeHandle(IntPtr handle) : base(handle, false) { } public override bool IsInvalid { get { return false; } } protected override bool ReleaseHandle() { return true; } } 

这是症状:
– 权利不足;
– 一个库的失败负载;

使用Filemon来检测一些访问被拒绝或
WinDbg在debugging器中运行应用程序并查看任何问题。

基于@Stephen Martin和Martin Prikryl的回答。

此代码可帮助您使用服务中的不同用户凭据运行进程。
我现在已经优化了源代码。
现在也可以撤销和设置权利。

 namespace QlikConnectorPSExecute { #region Usings using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; #endregion //inspired by: http://stackoverflow.com/questions/677874/starting-a-process-with-credentials-from-a-windows-service public class WindowsGrandAccess : IDisposable { #region DLL-Import // All the code to manipulate a security object is available in .NET framework, // but its API tries to be type-safe and handle-safe, enforcing a special implementation // (to an otherwise generic WinAPI) for each handle type. This is to make sure // only a correct set of permissions can be set for corresponding object types and // mainly that handles do not leak. // Hence the AccessRule and the NativeObjectSecurity classes are abstract. // This is the simplest possible implementation that yet allows us to make use // of the existing .NET implementation, sparing necessity to // P/Invoke the underlying WinAPI. [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetProcessWindowStation(); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetCurrentThreadId(); #endregion #region Variables && Properties public static int WindowStationAllAccess { get; private set; } = 0x000f037f; public static int DesktopRightsAllAccess { get; private set; } = 0x000f01ff; private GenericSecurity WindowStationSecurity {get; set;} private GenericSecurity DesktopSecurity { get; set; } private int? OldWindowStationMask { get; set; } private int? OldDesktopMask { get; set; } private NTAccount AccountInfo { get; set; } private SafeHandle WsSafeHandle { get; set; } private SafeHandle DSafeHandle { get; set; } #endregion #region Constructor & Dispose public WindowsGrandAccess(NTAccount accountInfo, int windowStationMask, int desktopMask) { if (accountInfo != null) Init(accountInfo, windowStationMask, desktopMask); } public void Dispose() { try { if (AccountInfo == null) return; RestAccessMask(OldWindowStationMask, WindowStationAllAccess, WindowStationSecurity, WsSafeHandle); RestAccessMask(OldDesktopMask, DesktopRightsAllAccess, DesktopSecurity, DSafeHandle); } catch (Exception ex) { throw new Exception($"The object \"{nameof(WindowsGrandAccess)}\" could not be dispose.", ex); } } #endregion #region Methods private void Init(NTAccount accountInfo, int windowStationMask, int desktopMask) { AccountInfo = accountInfo; WsSafeHandle = new NoopSafeHandle(GetProcessWindowStation()); WindowStationSecurity = new GenericSecurity(false, ResourceType.WindowObject, WsSafeHandle, AccessControlSections.Access); DSafeHandle = new NoopSafeHandle(GetThreadDesktop(GetCurrentThreadId())); DesktopSecurity = new GenericSecurity(false, ResourceType.WindowObject, DSafeHandle, AccessControlSections.Access); OldWindowStationMask = ReadAccessMask(WindowStationSecurity, WsSafeHandle, windowStationMask); OldDesktopMask = ReadAccessMask(DesktopSecurity, DSafeHandle, desktopMask); } private AuthorizationRuleCollection GetAccessRules(GenericSecurity security) { return security.GetAccessRules(true, false, typeof(NTAccount)); } private int? ReadAccessMask(GenericSecurity security, SafeHandle safeHandle, int accessMask) { var ruels = GetAccessRules(security); var username = AccountInfo.Value; if (!username.Contains("\\")) username = $"{Environment.MachineName}\\{username}"; var userResult = ruels.Cast<GrantAccessRule>().SingleOrDefault(r => r.IdentityReference.Value.ToLower() == username.ToLower() && accessMask == r.PublicAccessMask); if (userResult == null) { AddGrandAccess(security, accessMask, safeHandle); userResult = ruels.Cast<GrantAccessRule>().SingleOrDefault(r => r.IdentityReference.Value.ToLower() == username.ToLower()); if (userResult != null) return userResult.PublicAccessMask; } else return userResult.PublicAccessMask; return null; } private void AddGrandAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle) { var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow); security.AddAccessRule(rule); security.Persist(safeHandle, AccessControlSections.Access); } private void RemoveGrantAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle) { var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow); security.RemoveAccessRule(rule); security.Persist(safeHandle, AccessControlSections.Access); } private void SetGrandAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle) { var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow); security.SetAccessRule(rule); security.Persist(safeHandle, AccessControlSections.Access); } private void RestAccessMask(int? oldAccessMask, int fullAccessMask, GenericSecurity security, SafeHandle safeHandle) { if (oldAccessMask == null) RemoveGrantAccess(security, fullAccessMask, safeHandle); else if (oldAccessMask != fullAccessMask) { SetGrandAccess(security, oldAccessMask.Value, safeHandle); } } #endregion #region private classes private class GenericSecurity : NativeObjectSecurity { public GenericSecurity( bool isContainer, ResourceType resType, SafeHandle objectHandle, AccessControlSections sectionsRequested) : base(isContainer, resType, objectHandle, sectionsRequested) { } new public void Persist(SafeHandle handle, AccessControlSections includeSections) { base.Persist(handle, includeSections); } new public void AddAccessRule(AccessRule rule) { base.AddAccessRule(rule); } new public bool RemoveAccessRule(AccessRule rule) { return base.RemoveAccessRule(rule); } new public void SetAccessRule(AccessRule rule) { base.SetAccessRule(rule); } new public AuthorizationRuleCollection GetAccessRules(bool includeExplicit, bool includeInherited, Type targetType) { return base.GetAccessRules(includeExplicit, includeInherited, targetType); } public override Type AccessRightType { get { throw new NotImplementedException(); } } public override AccessRule AccessRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) { return new GrantAccessRule(identityReference, accessMask, isInherited, inheritanceFlags, propagationFlags, type); } public override Type AccessRuleType { get { return typeof(AccessRule); } } public override AuditRule AuditRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) { throw new NotImplementedException(); } public override Type AuditRuleType { get { return typeof(AuditRule); } } } private class GrantAccessRule : AccessRule { public GrantAccessRule(IdentityReference identity, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) : base(identity, accessMask, isInherited, inheritanceFlags, propagationFlags, type) { } public GrantAccessRule(IdentityReference identity, int accessMask, AccessControlType type) : base(identity, accessMask, false, InheritanceFlags.None, PropagationFlags.None, type) { } public int PublicAccessMask { get { return base.AccessMask; } } } // Handles returned by GetProcessWindowStation and GetThreadDesktop should not be closed private class NoopSafeHandle : SafeHandle { public NoopSafeHandle(IntPtr handle) : base(handle, false) {} public override bool IsInvalid { get { return false; } } protected override bool ReleaseHandle() { return true; } } #endregion } } 

使用示例

 using (var windowsAccess = new WindowsGrandAccess(accountInfo, WindowsGrandAccess.WindowStationAllAccess, WindowsGrandAccess.DesktopRightsAllAccess)) { ... } 

谢谢。

你如何设置域,用户和密码? 您是否正确设置了域以及密码(它必须使用SecureString)。

另外,你是否设置了WorkingDirectory属性? 使用用户名和密码时,该文档指出您必须设置WorkingDirectory属性。

可能由服务启动的任何进程也必须具有“作为服务login”的规定。

如果您用于启动第二个进程的用户标识没有该方框的pipe理权限,则可能是这种情况。

一个简单的testing就是改变本地安全策略,使用户标识“作为服务login”并重新尝试。

编辑:额外的信息后..

在Google上放牧,似乎0xc0000142涉及到无法初始化所需的DLL。 有没有什么服务已经打开,产生的过程需要? 在任何情况下,它看起来都与被启动的过程有关,而不是你如何做。

今天我遇到了这个问题,我花了相当长的一段时间试图弄清楚。 我最终做的是将服务创build为交互式(使用允许服务与services.msc中的桌面交互checkbox)。 只要我这样做,0xc0000142错误消失了。