对于某些版本的Windows,Windows服务无法在用户登录时使用WTSQueryUserToken启动交互式进程

时间:2015-02-23 12:57:20

标签: c# windows windows-services interopservices

link之后,我在我的C#解决方案中实现了 WTSQueryUserToken ,并从我的Windows服务的CreateProcessAsUserWrapper.LaunchChildProcess("app_path")调用了OnStart方法以“LocalSystem”运行。它能够启动可与桌面交互的进程,但适用于Windows Professional而不适用于Windows Ultimate。我在Windows Professional 64位上尝试过,它能够以交互方式成功启动用户登录过程,但在Windows Ultimate 64位版本上, CreateProcessAsUser方法返回false 和服务可以看到在SCM运行,而过程不是。

我的流程是一个win表单应用,需要与当前登录的用户帐户一起运行以进行桌面交互,并使用InteropServices。这可能是一个框架问题吗?如何解决问题?请帮忙。

编辑: 这两台机器都运行Windows 7 64位。我添加了以下代码以获取CreateProcessAsUser之后的错误代码,并且返回“请求的操作需要提升”作为错误。

string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;

仅在Windows 7旗舰版中发生,而在Windows 7 Professional中不发生。我需要为每个登录用户运行我的进程,这就是为什么我认为我不能使用CreateProcessWithLogonW方法。我现在如何修复此提升权限问题?

编辑: 有2个app.manifest个文件,一个在服务项目中,另一个在win form(process)项目中。我删除了这两个文件,使用msi构建了WiX并再次进行了测试。现在它已在每台计算机上成功运行,每个登录的用户帐户都会启动该过程。但是,现在出现了一个问题。实际上我需要在windows / system文件夹下写一些日志,这是我现在不能做的。我从这个过程中抛出了“UnauthorizedAccessException”。此外,除管理员之外的任何用户都不允许停止或卸载该过程,但在这种情况下,用户可以启动任务管理器并从那里结束该过程,因为它与相应的用户帐户一起运行。是否可以使用用户帐户运行该进程但具有提升的权限,以便我可以在windows / system文件夹下编写我的日志并阻止用户在没有管理员权限的情况下停止该进程?至少解决第一个问题,即在系统文件夹下写日志文件对我来说非常重要。

编辑: 在添加SetTokenInformationDuplicateTokenEx后,我的服务无法启动应用程序,日志SetTokenInformation()返回FALSE。错误:客户端不保留所需的权限”。我提供完整的代码,请查看我是否已按照所有步骤正确更新。

using System;
using System.IO;
using System.ComponentModel;
using System.Security.Principal;
using System.Text;
using System.Collections;
using System.Web;
using System.Net;
using System.Diagnostics;
using System.Runtime.InteropServices; // DllImport

namespace CSCreateProcessAsUserFromService
{
    class CreateProcessAsUserWrapper1
    {
        static String tmp_log = Path.Combine(CreateDirectoryAtSystemFolder(), "Temp-Report-Service.txt");
        static string errorMessage;

        public static void LaunchChildProcess(string ChildProcName)
        {
            IntPtr ppSessionInfo = IntPtr.Zero;
            UInt32 SessionCount = 0;

            File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess() OK 1" + "\n");

            if (WTSEnumerateSessions(
                (IntPtr)WTS_CURRENT_SERVER_HANDLE,  // Current RD Session Host Server handle would be zero.
                0,                                  // This reserved parameter must be zero.
                1,                                  // The version of the enumeration request must be 1.
                ref ppSessionInfo,                  // This would point to an array of session info.
                ref SessionCount                    // This would indicate the length of the above array.
                ))
            {
                File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions OK 2" + "\n");

                for (int nCount = 0; nCount < SessionCount; nCount++)
                {
                    // Extract each session info and check if it is the 
                    // "Active Session" of the current logged-on user.
                    //ppSessionInfo = new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)));
                    WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(
                        ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)),
                        typeof(WTS_SESSION_INFO)
                        );
                    /*WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(
                        new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO))),
                        typeof(WTS_SESSION_INFO)
                        );*/

                    if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State)
                    {
                        WindowsIdentity m_ImpersonatedUser;

                        IntPtr hToken = IntPtr.Zero;
                        IntPtr hTokenDuplicate = IntPtr.Zero;
                        const int SecurityImpersonation = 2;
                        const int TokenType = 1;

                        File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) 3" + "\n");

                        if (RevertToSelf())
                        {
                            File.AppendAllText(tmp_log, "\n" + "RevertToSelf() TRUE 4 " + "\n");

                            if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken))
                            {
                                File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) TRUE 5 " + "\n");

                                LUID luid = new LUID();
                                // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
                                // I would prefer to not have to use a security attribute variable and to just 
                                // simply pass null and inherit (by default) the security attributes
                                // of the existing token. However, in C# structures are value types and therefore
                                // cannot be assigned the null value.
                                SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                                sa.Length = Marshal.SizeOf(sa);
                                //if (DuplicateToken(hToken, SecurityImpersonation, ref hTokenDuplicate) != 0)
                                if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED/*GENERIC_ACCESS*/, ref sa,
                                    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                                    (int)TOKEN_TYPE.TokenPrimary, out/*ref*/ hTokenDuplicate))
                                {
                                    File.AppendAllText(tmp_log, "\n" + " DuplicateTokenEx() TRUE 6 " + "\n");

                                    WindowsImpersonationContext m_ImpersonationContext;
                                    m_ImpersonatedUser = new WindowsIdentity(hTokenDuplicate);
                                    using (m_ImpersonationContext = m_ImpersonatedUser.Impersonate())
                                    {
                                        if (m_ImpersonationContext != null)
                                        {
                                            File.AppendAllText(tmp_log, Environment.NewLine + "User Name: " +
                                                      WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).Name +
                                                      Environment.NewLine + "SID: " +
                                                      WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).User.Value);

                                            TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
                                            {
                                                PrivilegeCount = 1,
                                                Privileges = new Int32[3]
                                            };

                                            tp.Privileges[1] = luid.HighPart;
                                            tp.Privileges[0] = luid.LowPart;
                                            tp.Privileges[2] = SE_PRIVILEGE_ENABLED;

                                            //Adjust Token privilege
                                            if (SetTokenInformation(hTokenDuplicate,
                                                TOKEN_INFORMATION_CLASS.TokenSessionId,
                                                ref tSessionInfo.SessionID,
                                                (UInt32)Marshal.SizeOf(tSessionInfo.SessionID)))
                                            {
                                                File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() TRUE 7 " + "\n");

                                                if (AdjustTokenPrivileges(hTokenDuplicate,
                                                   false, ref tp, Marshal.SizeOf(tp),
                                                   IntPtr.Zero, IntPtr.Zero))
                                                {
                                                    File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() TRUE 8 " + "\n");

                                                    //ImpersonateLoggedOnUser(hToken);
                                                    //WindowsIdentity.Impersonate(hToken);

                                                    //errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                                    //File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n");

                                                    // Launch the child process interactively 
                                                    // with the token of the logged-on user.
                                                    PROCESS_INFORMATION tProcessInfo;
                                                    STARTUPINFO tStartUpInfo = new STARTUPINFO();
                                                    tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
                                                    tStartUpInfo.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

                                                    // flags that specify the priority and creation method of the process
                                                    int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
                                                    IntPtr pEnv = IntPtr.Zero;
                                                    if (CreateEnvironmentBlock(ref pEnv, hTokenDuplicate, true))
                                                    {
                                                        dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
                                                        File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() TRUE 9 " + "\n");
                                                    }
                                                    else
                                                    {
                                                        pEnv = IntPtr.Zero;
                                                        File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() ELSE 9 " + "\n");
                                                    }
                                                    // create a new process in the current user's logon session
                                                    bool ChildProcStarted = CreateProcessAsUser(
                                                        hTokenDuplicate,        // client's access token
                                                        ChildProcName,          // file to execute
                                                        null,                   // command line
                                                        ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                                        ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                                        false,                  // handles are not inheritable
                                                        dwCreationFlags,        // creation flags
                                                        IntPtr.Zero,            // pointer to new environment block 
                                                        null,                   // name of current directory 
                                                        ref tStartUpInfo,                 // pointer to STARTUPINFO structure
                                                        out tProcessInfo            // receives information about new process
                                                        );

                                                    /*bool ChildProcStarted = CreateProcessAsUser(
                                                       //hToken,             // Token of the logged-on user.
                                                       hTokenDuplicate,    // Token of the logged-on user.
                                                       ChildProcName,      // Name of the process to be started.
                                                       null,               // Any command line arguments to be passed.
                                                       IntPtr.Zero,        // Default Process' attributes.
                                                       IntPtr.Zero,        // Default Thread's attributes.
                                                       false,              // Does NOT inherit parent's handles.
                                                       0,                  // No any specific creation flag.
                                                       null,               // Default environment path.
                                                       null,               // Default current directory.
                                                       ref tStartUpInfo,   // Process Startup Info. 
                                                       out tProcessInfo    // Process information to be returned.
                                                       );*/

                                                    errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                                    File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-CreateProcessAsUser-" + errorMessage + "\n");

                                                    if (ChildProcStarted)
                                                    {
                                                        // The child process creation is successful!

                                                        // If the child process is created, it can be controlled via the out 
                                                        // param "tProcessInfo". For now, as we don't want to do any thing 
                                                        // with the child process, closing the child process' handles 
                                                        // to prevent the handle leak.
                                                        CloseHandle(tProcessInfo.hThread);
                                                        CloseHandle(tProcessInfo.hProcess);

                                                        File.AppendAllText(tmp_log, "\n" + "ChildProcStarted() 10 true!" + "\n");
                                                    }
                                                    else
                                                    {
                                                        // CreateProcessAsUser failed!
                                                        errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                                        File.AppendAllText(tmp_log, "\n" + " ChildProcStarted() 10 failed! ERROR: " + errorMessage + "\n");
                                                    }

                                                    // Whether child process was created or not, close the token handle 
                                                    // and break the loop as processing for current active user has been done.
                                                    CloseHandle(hToken);
                                                    CloseHandle(hTokenDuplicate);
                                                    // Undo impersonation
                                                    //m_ImpersonationContext.Undo();
                                                    break;
                                                }
                                                else
                                                {
                                                    errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                                    File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() FALSE 8 ERROR: " + errorMessage + "\n");
                                                }
                                            }
                                            else
                                            {
                                                errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                                //File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n");
                                                File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() FALSE 7 ERROR: " + errorMessage + "\n");
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                    File.AppendAllText(tmp_log, "\n" + "DuplicateTokenEx() FALSE 6 ERROR: " + errorMessage + "\n");
                                }
                            }
                            else
                            {
                                // WTSQueryUserToken failed!
                                errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                                File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) FALSE 5 ERROR: " + errorMessage + "\n");
                            }
                        }
                        else
                        {
                            errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                            File.AppendAllText(tmp_log, "\n" + "RevertToSelf() FALSE 4 ERROR: " + errorMessage + "\n");

                        }
                    }
                    else
                    {
                        // This Session is not active!
                        errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                        File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) FALSE 3 This Session is not active! ERROR: " + errorMessage + "\n");
                    }
                }

                // Free the memory allocated for the session info array.
                WTSFreeMemory(ppSessionInfo);
            }
            else
            {
                // WTSEnumerateSessions failed!
                errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions FAILED 2 ERROR: " + errorMessage + "\n");
            }
        }


        #region P/Invoke WTS APIs
        /// <summary>
        /// Struct, Enum and P/Invoke Declarations of WTS APIs.
        /// </summary>
        /// 

        private const int WTS_CURRENT_SERVER_HANDLE = 0;
        private enum WTS_CONNECTSTATE_CLASS
        {
            WTSActive,
            WTSConnected,
            WTSConnectQuery,
            WTSShadow,
            WTSDisconnected,
            WTSIdle,
            WTSListen,
            WTSReset,
            WTSDown,
            WTSInit
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct WTS_SESSION_INFO
        {
            public UInt32 SessionID;
            public string pWinStationName;
            public WTS_CONNECTSTATE_CLASS State;
        }

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool WTSEnumerateSessions(
            IntPtr hServer,
            [MarshalAs(UnmanagedType.U4)] UInt32 Reserved,
            [MarshalAs(UnmanagedType.U4)] UInt32 Version,
            ref IntPtr ppSessionInfo,
            [MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount
            );

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern void WTSFreeMemory(IntPtr pMemory);

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
        #endregion


        #region P/Invoke CreateProcessAsUser
        /// <summary>
        /// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser.
        /// </summary>
        /// 

        #region WMI Constants

        private const String cstrScope = "root\\CIMV2";
        private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";

        #endregion

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        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, CharSet = CharSet.Auto)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        #region Enumerations

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin,
            MaxTokenInfoClass  // MaxTokenInfoClass should always be the last enum
        }


        enum TOKEN_TYPE : int
        {
            TokenPrimary = 1,
            TokenImpersonation = 2
        }

        enum SECURITY_IMPERSONATION_LEVEL : int
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3,
        }

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

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

        [StructLayout(LayoutKind.Sequential)]
        struct TOKEN_PRIVILEGES
        {
            public Int32 PrivilegeCount;
            //LUID_AND_ATRIBUTES
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public Int32[] Privileges;
        }

        #endregion

        #region Constants

        public const uint GENERIC_ACCESS = 0x1000000;
        public const uint MAXIMUM_ALLOWED = 0x2000000;
        public const int CREATE_NEW_CONSOLE = 0x00000010;

        public const int IDLE_PRIORITY_CLASS = 0x40;
        public const int NORMAL_PRIORITY_CLASS = 0x20;
        public const int HIGH_PRIORITY_CLASS = 0x80;
        public const int REALTIME_PRIORITY_CLASS = 0x100;

        const Int32 READ_CONTROL = 0x00020000;

        const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;

        const Int32 STANDARD_RIGHTS_READ = READ_CONTROL;
        const Int32 STANDARD_RIGHTS_WRITE = READ_CONTROL;
        const Int32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL;

        const Int32 STANDARD_RIGHTS_ALL = 0x001F0000;

        const Int32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;

        const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001;
        const Int32 TOKEN_DUPLICATE = 0x0002;
        const Int32 TOKEN_IMPERSONATE = 0x0004;
        const Int32 TOKEN_QUERY = 0x0008;
        const Int32 TOKEN_QUERY_SOURCE = 0x0010;
        const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
        const Int32 TOKEN_ADJUST_GROUPS = 0x0040;
        const Int32 TOKEN_ADJUST_DEFAULT = 0x0080;
        const Int32 TOKEN_ADJUST_SESSIONID = 0x0100;

        const Int32 TOKEN_ALL_ACCESS_P = (
            STANDARD_RIGHTS_REQUIRED |
            TOKEN_ASSIGN_PRIMARY |
            TOKEN_DUPLICATE |
            TOKEN_IMPERSONATE |
            TOKEN_QUERY |
            TOKEN_QUERY_SOURCE |
            TOKEN_ADJUST_PRIVILEGES |
            TOKEN_ADJUST_GROUPS |
            TOKEN_ADJUST_DEFAULT);

        const Int32 TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;

        const Int32 TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;


        const Int32 TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
                                      TOKEN_ADJUST_PRIVILEGES |
                                      TOKEN_ADJUST_GROUPS |
                                      TOKEN_ADJUST_DEFAULT;

        const Int32 TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;

        //const UInt32 MAXIMUM_ALLOWED = 0x2000000;

        const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
        const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;

        /*const Int32 IDLE_PRIORITY_CLASS = 0x40;
        const Int32 NORMAL_PRIORITY_CLASS = 0x20;
        const Int32 HIGH_PRIORITY_CLASS = 0x80;
        const Int32 REALTIME_PRIORITY_CLASS = 0x100;*/

        //const Int32 CREATE_NEW_CONSOLE = 0x00000010;

        const string SE_DEBUG_NAME = "SeDebugPrivilege";
        const string SE_RESTORE_NAME = "SeRestorePrivilege";
        const string SE_BACKUP_NAME = "SeBackupPrivilege";

        const Int32 SE_PRIVILEGE_ENABLED = 0x0002;

        const Int32 ERROR_NOT_ALL_ASSIGNED = 1300;

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESSENTRY32
        {
            UInt32 dwSize;
            UInt32 cntUsage;
            UInt32 th32ProcessID;
            IntPtr th32DefaultHeapID;
            UInt32 th32ModuleID;
            UInt32 cntThreads;
            UInt32 th32ParentProcessID;
            Int32 pcPriClassBase;
            UInt32 dwFlags;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            string szExeFile;
        }

        const UInt32 TH32CS_SNAPPROCESS = 0x00000002;

        const Int32 INVALID_HANDLE_VALUE = -1;

        #endregion

        /* [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            string lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation
            );*/

         [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
         public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
             ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
             String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CloseHandle(IntPtr hHandle);
        #endregion

        [DllImport("kernel32.dll")]
        public static extern UInt32 WTSGetActiveConsoleSessionId();

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean AdjustTokenPrivileges(IntPtr TokenHandle, Boolean DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern Boolean CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);

        [DllImport("advapi32.DLL")]
        public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user

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

        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
        public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
            int ImpersonationLevel, out/*ref*/ IntPtr DuplicateTokenHandle);

        ///
        /// A process should call the RevertToSelf function after finishing 
        /// any impersonation begun by using the DdeImpersonateClient, 
        /// ImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient, 
        /// ImpersonateSelf, ImpersonateAnonymousToken or SetThreadToken function.
        /// If RevertToSelf fails, your application continues to run in the context of the client,
        /// which is not appropriate. You should shut down the process if RevertToSelf fails.
        /// RevertToSelf Function: http://msdn.microsoft.com/en-us/library/aa379317(VS.85).aspx
        ///
        /// A boolean value indicates the function succeeded or not.
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();



        [DllImport("shell32.dll")]
        public static extern bool SHGetSpecialFolderPath(IntPtr hwndOwner, [Out]StringBuilder lpszPath, int nFolder, bool fCreate);

        static string System32_SysWOW64_Folder()
        {
            StringBuilder path = new StringBuilder(260);
            SHGetSpecialFolderPath(IntPtr.Zero, path, 0x0029, false);
            return path.ToString();
        }

        //=================== Create directory =====================
        private static string CreateDirectoryAtSystemFolder()
        {
            string tmpDir = null;
            string sysFolderPath = System32_SysWOW64_Folder();
            //string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); //System32_SysWOW64_Folder();
            //string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); //ProgramFiles_Folder();
            tmpDir = sysFolderPath + "\\logdata"; //ConfigurationSettings.AppSettings["ImageSavedPath"].ToString();
            if (!System.IO.Directory.Exists(@tmpDir))
            {
                System.IO.Directory.CreateDirectory(@tmpDir);
            }

            return tmpDir;
        }
    }
}

正如我在之前的编辑中提到的,之前添加DuplicateTokenEx&amp; SetTokenInformation,该服务能够运行该应用程序,但无法在System32文件夹下登录。为了达到这个目的,我更新了上面的代码,现在SetTokenInformation()方法显示了权限错误。

编辑: 最后,该服务可以在已登录的用户帐户上启动我的进程,并将所有日志文件写入正确的用户appdata路径。我已删除SetTokenInformation。以下代码使用CreateEnvironmentBlock()方法帮助该过程获得单独的用户环境并检索用户appdata路径。

int dwCreationFlags = DETACHED_PROCESS;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hToken, true))
{
    dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
    pEnv = IntPtr.Zero;
}

bool ChildProcStarted = CreateProcessAsUser(
    hToken,                 // client's access token
    ChildProcName,          // file to execute
    null,                   // command line
    ref saProcess,          // pointer to process SECURITY_ATTRIBUTES
    ref saThread,           // pointer to thread SECURITY_ATTRIBUTES
    false,                  // handles are not inheritable
    dwCreationFlags,        // creation flags
    pEnv,                   // pointer to new environment block 
    null,                   // name of current directory 
    ref tStartUpInfo,       // pointer to STARTUPINFO structure
    out tProcessInfo        // receives information about new process
    );

1 个答案:

答案 0 :(得分:2)

总结:

  • 最初的问题是目标可执行文件被配置为需要UAC提升,但必须在用户的上下文中运行,即使用户不是管理员也是如此。这与它从服务启动的事实没有直接关系,但这是一个复杂的因素,因为它意味着启动应用程序的尝试失败而不是导致提升对话框。决议是将应用程序的清单中的requestedExecutionLevelrequireAdministrator更改为asInvoker,这是默认值。

  • 当应用程序重新配置为不需要UAC提升时,它无法正常运行,因为它试图将日志文件写入全局文件夹。解决方案是将日志文件写入合适的每用户位置。

  • 最后一个问题是应用程序在从服务启动时未正确找到每用户位置。这是因为它被提供了服务环境块的副本而不是适合用户的副本。解决方案是使用CreateEnvironmentBlock()生成合适的环境块并将其作为CreateProcessAsUser的lpEnvironment参数传递。