管理服务:将用户登录到桌面,生成可与桌面交互的进程

时间:2015-05-08 20:29:44

标签: c# windows service impersonation

我很难找到一个非常具体的用例。有问题的应用程序有两个组件:Windows服务,需要在桌面外的特权上下文中运行(即,在用户登录时是否接受连接)和客户端Winforms应用程序。该服务接受websocket连接,如果连接请求成功,则应该以交互方式(进入桌面)记录用户,并以具有桌面访问权限的用户生成进程。我使用了以下链接,虽然他们能够冒充用户,但他们实际上并没有将用户登录到桌面,即如果我使用VNC观看系统,或者我在本地系统上测试它,用户没有登录。但是,该过程会以用户身份生成,但显然不会使用桌面访问。

是否有人会将用户登录到桌面?

我尝试过的链接和代码:

Using Process.Start() to start a process as a different user from within a Windows Service

How to use LogonUser properly to impersonate domain user from workgroup client公开

Launch a process under another user's credentials

https://social.msdn.microsoft.com/Forums/vstudio/en-US/9fb068b0-507b-4b6b-879a-b0fbe492ba92/c-start-program-with-different-user-credentials

目前的代码是:

using Cassia;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceProcess;

namespace program
{
    public partial class service
    {
        #region Interop

        [StructLayout(LayoutKind.Sequential)]
        public struct LUID
        {
            public UInt32 LowPart;
            public Int32 HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct LUID_AND_ATTRIBUTES
        {
            public LUID Luid;
            public UInt32 Attributes;
        }

        public struct TOKEN_PRIVILEGES
        {
            public UInt32 PrivilegeCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public LUID_AND_ATTRIBUTES[] Privileges;
        }

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin,
            TokenElevationType,
            TokenLinkedToken,
            TokenElevation,
            TokenHasRestrictions,
            TokenAccessInformation,
            TokenVirtualizationAllowed,
            TokenVirtualizationEnabled,
            TokenIntegrityLevel,
            TokenUIAccess,
            TokenMandatoryPolicy,
            TokenLogonSid,
            MaxTokenInfoClass
        }

        [Flags]
        enum CreationFlags : uint
        {
            CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
            CREATE_DEFAULT_ERROR_MODE = 0x04000000,
            CREATE_NEW_CONSOLE = 0x00000010,
            CREATE_NEW_PROCESS_GROUP = 0x00000200,
            CREATE_NO_WINDOW = 0x08000000,
            CREATE_PROTECTED_PROCESS = 0x00040000,
            CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
            CREATE_SEPARATE_WOW_VDM = 0x00001000,
            CREATE_SUSPENDED = 0x00000004,
            CREATE_UNICODE_ENVIRONMENT = 0x00000400,
            DEBUG_ONLY_THIS_PROCESS = 0x00000002,
            DEBUG_PROCESS = 0x00000001,
            DETACHED_PROCESS = 0x00000008,
            EXTENDED_STARTUPINFO_PRESENT = 0x00080000
        }

        public enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation
        }

        public enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }

        [Flags]
        enum LogonFlags
        {
            LOGON_NETCREDENTIALS_ONLY = 2,
            LOGON_WITH_PROFILE = 1
        }

        enum LOGON_TYPE
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK,
            LOGON32_LOGON_BATCH,
            LOGON32_LOGON_SERVICE,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT,
            LOGON32_LOGON_NEW_CREDENTIALS
        }

        enum LOGON_PROVIDER
        {
            LOGON32_PROVIDER_DEFAULT,
            LOGON32_PROVIDER_WINNT35,
            LOGON32_PROVIDER_WINNT40,
            LOGON32_PROVIDER_WINNT50
        }

        struct SECURITY_ATTRIBUTES
        {
            public uint Length;
            public IntPtr SecurityDescriptor;
            public bool InheritHandle;
        }

        [Flags]
        enum SECURITY_INFORMATION : uint
        {
            OWNER_SECURITY_INFORMATION = 0x00000001,
            GROUP_SECURITY_INFORMATION = 0x00000002,
            DACL_SECURITY_INFORMATION = 0x00000004,
            SACL_SECURITY_INFORMATION = 0x00000008,
            UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
            UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
            PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
            PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
        }

        [StructLayoutAttribute(LayoutKind.Sequential)]
        struct SECURITY_DESCRIPTOR
        {
            public byte revision;
            public byte size;
            public short control; // public SECURITY_DESCRIPTOR_CONTROL control;
            public IntPtr owner;
            public IntPtr group;
            public IntPtr sacl;
            public IntPtr dacl;
        }

        struct STARTUPINFO
        {
            public uint cb;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Reserved;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Desktop;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Title;
            public uint X;
            public uint Y;
            public uint XSize;
            public uint YSize;
            public uint XCountChars;
            public uint YCountChars;
            public uint FillAttribute;
            public uint Flags;
            public ushort ShowWindow;
            public ushort Reserverd2;
            public byte bReserverd2;
            public IntPtr StdInput;
            public IntPtr StdOutput;
            public IntPtr StdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESS_INFORMATION
        {
            public IntPtr Process;
            public IntPtr Thread;
            public uint ProcessId;
            public uint ThreadId;
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool InitializeSecurityDescriptor(IntPtr pSecurityDescriptor, uint dwRevision);
        const uint SECURITY_DESCRIPTOR_REVISION = 1;

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool SetSecurityDescriptorDacl(ref SECURITY_DESCRIPTOR sd, bool daclPresent, IntPtr dacl, bool daclDefaulted);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        extern static bool DuplicateTokenEx(
            IntPtr hExistingToken,
            uint dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpTokenAttributes,
            SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
            TOKEN_TYPE TokenType,
            out IntPtr phNewToken);

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken
            );

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool GetTokenInformation(
            IntPtr TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength
            );

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool CreateProcessAsUser(
            IntPtr Token,
            [MarshalAs(UnmanagedType.LPTStr)] string ApplicationName,
            [MarshalAs(UnmanagedType.LPTStr)] string CommandLine,
            ref SECURITY_ATTRIBUTES ProcessAttributes,
            ref SECURITY_ATTRIBUTES ThreadAttributes,
            bool InheritHandles,
            uint CreationFlags,
            IntPtr Environment,
            [MarshalAs(UnmanagedType.LPTStr)] string CurrentDirectory,
            ref STARTUPINFO StartupInfo,
            out PROCESS_INFORMATION ProcessInformation);

        [DllImport("Kernel32.dll")]
        extern static int CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TokPriv1Luid
        {
            public int Count;
            public long Luid;
            public int Attr;
        }

        internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
        internal const int TOKEN_QUERY = 0x00000008;
        internal const int TOKEN_DUPLICATE = 0x0002;
        internal const int TOKEN_ASSIGN_PRIMARY = 0x0001;

        #endregion

        public static bool LoginUser(string domain, string username, string password, string program, string workingDir)
        {
            IntPtr token = IntPtr.Zero;
            IntPtr primaryToken = IntPtr.Zero;

            try
            {
                bool result = LogonUser(username, domain, password, (int)LOGON_TYPE.LOGON32_LOGON_NETWORK, (int)LOGON_PROVIDER.LOGON32_PROVIDER_DEFAULT, out token);
                if (!result)
                {
                    int winError = Marshal.GetLastWin32Error();
                    Console.WriteLine("LoginUser unable to login user " + username + ", error: " + winError);
                    return false;
                }

                SECURITY_ATTRIBUTES processAttributes = new SECURITY_ATTRIBUTES();
                SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR();
                IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sd));
                Marshal.StructureToPtr(sd, ptr, false);
                InitializeSecurityDescriptor(ptr, SECURITY_DESCRIPTOR_REVISION);
                sd = (SECURITY_DESCRIPTOR)Marshal.PtrToStructure(ptr, typeof(SECURITY_DESCRIPTOR));

                result = SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false);
                if (!result)
                {
                    int winError = Marshal.GetLastWin32Error();
                }

                primaryToken = new IntPtr();
                result = DuplicateTokenEx(token, 0, ref processAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
                if (!result)
                {
                    int winError = Marshal.GetLastWin32Error();
                }

                processAttributes.SecurityDescriptor = ptr;
                processAttributes.Length = (uint)Marshal.SizeOf(sd);
                processAttributes.InheritHandle = true;

                SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
                threadAttributes.SecurityDescriptor = IntPtr.Zero;
                threadAttributes.Length = 0;
                threadAttributes.InheritHandle = false;

                bool inheritHandles = true;
                IntPtr environment = IntPtr.Zero;

                STARTUPINFO startupInfo = new STARTUPINFO();
                startupInfo.Desktop = "";

                PROCESS_INFORMATION processInformation;

                result = CreateProcessAsUser(
                    primaryToken,
                    program,
                    program, 
                    ref processAttributes, 
                    ref threadAttributes, 
                    inheritHandles, 
                    16, 
                    environment, 
                    workingDir,
                    ref startupInfo, 
                    out processInformation);

                if (!result)
                {
                    int winError = Marshal.GetLastWin32Error();
                    Console.WriteLine("LoginUser unable to create process as user " + username + ", error: " + winError);
                    return false;
                }

                return true;
            } 
            catch (Exception e) 
            {
                Console.WriteLine("LoginUser exception encountered: " + e.Message());
                return false;
            } 
            finally
            {
                if (token != IntPtr.Zero)
                {
                    int x = CloseHandle(token);
                    if (x == 0)
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    x = CloseHandle(primaryToken);
                    if (x == 0)
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
        }

        public static SecureString securePassword(string password)
        {
            if (string_null(password)) return null;
            SecureString secure = new SecureString();
            foreach (char c in password)
            {
                secure.AppendChar(c);
            }
            return secure;
        }
    }
}

我的目标是能够简单地称它为:

if (!LoginUser("machinename", "username", "password", "c:\\path\\to\\program.exe", "c:\\path\\to"))
{
  // error
}
else
{
  // success, user is logged into desktop and app is launch
  // as user with desktop access
}

3 个答案:

答案 0 :(得分:3)

之前我遇到了相同的情况,事情变得复杂,所以我只使用 PSEXEC Process.Start

只需使用PSEXEC,就像他们的样本上所示,你所要做的就是这样的事情

通过代码或手动(即program.exe)将可执行文件复制或安装到远程系统并以交互方式执行,在DannyGlover帐户下运行:

psexec \\workstation64 -c program.exe -u YourUser -p YourPa55w0rd

现在您知道了参数,然后可以使用 Process.Start 来运行它。 所以它看起来像这样

using System.Diagnostics;
...
Process process = new Process();
process.StartInfo.FileName = "program.exe";
process.StartInfo.Arguments = "\\workstation64 -c program.exe -u YourUser -p YourPa55w0rd";
process.Start();
process.WaitForExit();

顺便提一下,您可以在这里了解更多并下载PSEXEC http://ss64.com/nt/psexec.html

答案 1 :(得分:0)

我在Elance.com上发现了您的问题并找到了此链接。

我和我的一个项目有同样的问题。这需要Windows服务启动POS终端通信器exe,并且应该具有管理员访问权限的UI。我尝试了同样模仿winlogon.exe以避免UAC,但这没有帮助。

我的问题是我使用了计划任务并使用c#创建了我的任务。 你可以在这里找到一个非常好的图书馆http://taskscheduler.codeplex.com。 现在,在必要的呼叫中,您可以动态运行任务,并且您可以完全控制该任务。

您可以动态创建自己的任务/编辑/删除,并且可以通过不避免使用UAC来运行您的应用。

答案 2 :(得分:0)

您正在寻求创建新的互动会话。不幸的是,这是不可能的。据我了解,您希望启动一个过程,然后使用VNC远程控制机器。也许你只需要使用远程桌面?