首页 文章

在Windows服务中模拟用户

提问于
浏览
18

我试图在Windows服务中模拟域用户,并将该服务作为本地系统帐户登录 .

到目前为止,我只能通过记录服务并使用用户凭据设置进程来实现此功能,如下所示 .

ProcessStartInfo startInfo = new ProcessStartInfo();
        startInfo.FileName = CommandDetails.Command;
        startInfo.WorkingDirectory = Settings.RoboCopyWorkingDirectory;
        startInfo.Arguments = commandLine;

        startInfo.UseShellExecute = false;
        startInfo.CreateNoWindow = true;
        startInfo.RedirectStandardError = true;
        startInfo.RedirectStandardOutput = true;

        // Credentials
        startInfo.Domain = ImperDomain;
        startInfo.UserName = ImperUsername;
        startInfo.Password = ImperPasswordSecure;

        process = Process.Start(startInfo);

我的目标是不在域用户中使用服务日志,而是将域帐户密码重置为本地系统 .

当我使用本地系统时,我得到 Access is denied

任何想法如何实现这一目标?

StackTace

Access is denied

   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at Ace.WindowsService.ProcessCmd.ProcessCommand.StartProcess(ProcessStartInfo startInfo) in

我已经尝试将代码包装在下面列出的Impersonate代码中但没有成功 .

Impersonate Code

public class Impersonation2 : IDisposable
{
    private WindowsImpersonationContext _impersonatedUserContext;

    // Declare signatures for Win32 LogonUser and CloseHandle APIs
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool LogonUser(
      string principal,
      string authority,
      string password,
      LogonSessionType logonType,
      LogonProvider logonProvider,
      out IntPtr token);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool RevertToSelf();

    // ReSharper disable UnusedMember.Local
    enum LogonSessionType : uint
    {
        Interactive = 2,
        Network,
        Batch,
        Service,
        NetworkCleartext = 8,
        NewCredentials
    }
    // ReSharper disable InconsistentNaming
    enum LogonProvider : uint
    {
        Default = 0, // default for platform (use this!)
        WinNT35,     // sends smoke signals to authority
        WinNT40,     // uses NTLM
        WinNT50      // negotiates Kerb or NTLM
    }
    // ReSharper restore InconsistentNaming
    // ReSharper restore UnusedMember.Local

    /// <summary>
    /// Class to allow running a segment of code under a given user login context
    /// </summary>
    /// <param name="user">domain\user</param>
    /// <param name="password">user's domain password</param>
    public Impersonation2(string domain, string username, string password)
    {
        var token = ValidateParametersAndGetFirstLoginToken(username, domain, password);

        var duplicateToken = IntPtr.Zero;
        try
        {
            if (DuplicateToken(token, 2, ref duplicateToken) == 0)
            {
                throw new Exception("DuplicateToken call to reset permissions for this token failed");
            }

            var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
            _impersonatedUserContext = identityForLoggedOnUser.Impersonate();
            if (_impersonatedUserContext == null)
            {
                throw new Exception("WindowsIdentity.Impersonate() failed");
            }
        }
        finally
        {
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (duplicateToken != IntPtr.Zero)
                CloseHandle(duplicateToken);
        }
    }

    private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
    {


        if (!RevertToSelf())
        {
            throw new Exception("RevertToSelf call to remove any prior impersonations failed");
            ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");

        }

        IntPtr token;

        var result = LogonUser(domain, username,
                               password,
                               LogonSessionType.Interactive,
                               LogonProvider.Default,
                               out token);
        if (!result)
        {
            var errorCode = Marshal.GetLastWin32Error();
            ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user.  LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
            throw new Exception("Logon for user " + username + " failed.");
        }
        return token;
    }

    public void Dispose()
    {
        // Stop impersonation and revert to the process identity
        if (_impersonatedUserContext != null)
        {
            _impersonatedUserContext.Undo();
            _impersonatedUserContext = null;
        }
    }

Update

如果我只是在运行,如果我只是执行它,这工作正常 . 但是当它作为服务运行时,它将无法工作

Update 2

当我将模拟登录更改为LogonSessionType.NewCredentials并从进程中删除信用时,我没有从Process.Start获取访问权限 . 但是我现在在运行robocopy命令时看到错误 . 当我在进程上拥有凭据时,它不会从robocopy命令生成日志文件

Error

2016/07/16 09:19:12 ERROR 5 (0x00000005) 
Accessing Source Directory \\[server]\[path]\
Access is denied.

Change

var result = LogonUser(domain, username,
   password,
   LogonSessionType.NewCredentials,
   LogonProvider.Default,
   out token);

更新3

复制和移动功能正常工作 . 但创建子流程却不是 . 正如Hary Johnston建议的那样,我一直在玩CreateProcessAsUser .

1 回答

  • 9

    我能够让它发挥作用 .

    对于正常模拟,我使用以下代码

    public class Impersonation : IDisposable
    {
        private WindowsImpersonationContext _impersonatedUserContext;
    
        #region FUNCTIONS (P/INVOKE)
    
        // Declare signatures for Win32 LogonUser and CloseHandle APIs
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool LogonUser(
          string principal,
          string authority,
          string password,
          LogonSessionType logonType,
          LogonProvider logonProvider,
          out IntPtr token);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr handle);
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool RevertToSelf();
    
        #endregion
    
        #region ENUMS
    
        enum LogonSessionType : uint
        {
            Interactive = 2,
            Network,
            Batch,
            Service,
            NetworkCleartext = 8,
            NewCredentials
        }
    
        enum LogonProvider : uint
        {
            Default = 0, // default for platform (use this!)
            WinNT35,     // sends smoke signals to authority
            WinNT40,     // uses NTLM
            WinNT50      // negotiates Kerb or NTLM
        }
    
        #endregion
    
    
        /// <summary>
        /// Class to allow running a segment of code under a given user login context
        /// </summary>
        /// <param name="user">domain\user</param>
        /// <param name="password">user's domain password</param>
        public Impersonation(string domain, string username, string password)
        {
            var token = ValidateParametersAndGetFirstLoginToken(username, domain, password);
    
            var duplicateToken = IntPtr.Zero;
            try
            {
                if (DuplicateToken(token, 2, ref duplicateToken) == 0)
                {
    
    
                    throw new InvalidOperationException("DuplicateToken call to reset permissions for this token failed");
                }
    
                var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
                _impersonatedUserContext = identityForLoggedOnUser.Impersonate();
                if (_impersonatedUserContext == null)
                {
                    throw new InvalidOperationException("WindowsIdentity.Impersonate() failed");
                }
            }
            finally
            {
                if (token != IntPtr.Zero)
                    CloseHandle(token);
                if (duplicateToken != IntPtr.Zero)
                    CloseHandle(duplicateToken);
            }
        }
    
        private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
        {
    
    
            if (!RevertToSelf())
            {
                ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
    
                throw new InvalidOperationException("RevertToSelf call to remove any prior impersonations failed");
    
            }
    
            IntPtr token;
    
            var result = LogonUser(domain, username,
                                   password,
                                   LogonSessionType.NewCredentials,
                                   LogonProvider.Default,
                                   out token);
            if (!result)
            {
                var errorCode = Marshal.GetLastWin32Error();
                ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user.  LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
                throw new InvalidOperationException("Logon for user " + username + " failed.");
            }
            return token;
        }
    
        public void Dispose()
        {
            // Stop impersonation and revert to the process identity
            if (_impersonatedUserContext != null)
            {
                _impersonatedUserContext.Undo();
                _impersonatedUserContext = null;
            }
        }
    }
    

    要运行它,我执行以下操作:

    FileInfo fi = new FileInfo(logfile);
                using (var imp = new Impersonation(Settings.ImpersonateUser.AccountDomain, Settings.ImpersonateUser.AccountName, Settings.ImpersonateUser.AccountPassword))
                {
                    if (File.Exists(filename))
                        File.Delete(filename);
                    fi.MoveTo(filename);
                }
    

    为了运行控制台命令,我使用了以下代码 .

    public class CreateProcess
    {
    
        #region Constants
    
        const UInt32 INFINITE = 0xFFFFFFFF;
        const UInt32 WAIT_FAILED = 0xFFFFFFFF;
    
        #endregion
    
    
        #region ENUMS
    
        [Flags]
        public enum LogonType
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK = 3,
            LOGON32_LOGON_BATCH = 4,
            LOGON32_LOGON_SERVICE = 5,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
            LOGON32_LOGON_NEW_CREDENTIALS = 9
        }
    
    
        [Flags]
        public enum LogonProvider
        {
            LOGON32_PROVIDER_DEFAULT = 0,
            LOGON32_PROVIDER_WINNT35,
            LOGON32_PROVIDER_WINNT40,
            LOGON32_PROVIDER_WINNT50
        }
    
        #endregion
    
    
        #region Structs
    
        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
    
    
        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessId;
            public Int32 dwThreadId;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public unsafe byte* lpSecurityDescriptor;
            public int bInheritHandle;
        }
    
        public enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation
        }
    
        public enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }
    
        #endregion
    
    
        #region FUNCTIONS (P/INVOKE)
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool RevertToSelf();
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);
    
    
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean LogonUser
        (
            String UserName,
            String Domain,
            String Password,
            LogonType dwLogonType,
            LogonProvider dwLogonProvider,
            out IntPtr phToken
        );
    
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CreateProcessAsUser
        (
            IntPtr hToken,
            String lpApplicationName,
            String lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            Boolean bInheritHandles,
            Int32 dwCreationFlags,
            IntPtr lpEnvironment,
            String lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation
        );
    
    
    
    
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern UInt32 WaitForSingleObject
        (
            IntPtr hHandle,
            UInt32 dwMilliseconds
        );
    
        [DllImport("kernel32", SetLastError = true)]
        public static extern Boolean CloseHandle(IntPtr handle);
    
        #endregion
    
        #region Functions
    
        public static int LaunchCommand(string command, string domain, string account, string password)
        {
            int ProcessId = -1;
            PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
            STARTUPINFO startInfo = new STARTUPINFO();
            Boolean bResult = false;
    
            UInt32 uiResultWait = WAIT_FAILED;
    
            var token = ValidateParametersAndGetFirstLoginToken(domain, account, password);
    
            var duplicateToken = IntPtr.Zero;
            try
            {
    
                startInfo.cb = Marshal.SizeOf(startInfo);
                //  startInfo.lpDesktop = "winsta0\\default";
    
                bResult = CreateProcessAsUser(
                    token,
                    null,
                    command,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    false,
                    0,
                    IntPtr.Zero,
                    null,
                    ref startInfo,
                    out processInfo
                );
    
                if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }
    
                // Wait for process to end
                uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
    
                ProcessId = processInfo.dwProcessId;
    
                if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
    
            }
            finally
            {
                if (token != IntPtr.Zero)
                    CloseHandle(token);
                if (duplicateToken != IntPtr.Zero)
                    CloseHandle(duplicateToken);
                CloseHandle(processInfo.hProcess);
                CloseHandle(processInfo.hThread);
            }
    
            return ProcessId;
        }
    
    
        private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
        {
    
    
            if (!RevertToSelf())
            {
                ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
                throw new Exception("RevertToSelf call to remove any prior impersonations failed");
            }
    
            IntPtr token;
    
            var result = LogonUser(username,
                                   domain,
                                   password,
                                   LogonType.LOGON32_LOGON_INTERACTIVE,
                                   LogonProvider.LOGON32_PROVIDER_DEFAULT,
                                   out token);
            if (!result)
            {
                var errorCode = Marshal.GetLastWin32Error();
                ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user.  LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
                throw new Exception("Logon for user " + username + " failed.");
            }
            return token;
        }
    
        #endregion
    
    }
    

    并执行以下操作

    string commandLine = "Robocopy " + args;
    
    ProcessId = CreateProcess.LaunchCommand(commandLine, ImperDomain, ImperUsername, ImperPassword);
    

    我还必须对本地策略进行一些更改,因为我希望能够在robocopy中复制权限 .

    感谢所有的评论和帮助 .

相关问题