如何在具有管理员权限的进程中启动一个不需要管理员权限的新进程?

47

我正在创建一个应用程序的自动更新器。该应用程序由用户启动,无需管理员特权即可运行。自动更新器使用管理员特权启动,并在下载新文件之前杀死应用程序。

问题在于,当自动更新器完成后,我想启动更新的应用程序。如果我使用常规的System.Diagnostics.Process.Start(file),应用程序也会以管理员特权启动,并且必须作为当前用户运行才能正常工作。

那么,如何使自动更新器作为当前用户而不是管理员启动应用程序呢?

我尝试使用以下方法:

var pSI = new ProcessStartInfo() { 
    UseShellExecute = false, 
    UserName = Environment.UserName, 
    FileName = file 
};
System.Diagnostics.Process.Start(pSI);

但是这会抛出错误"无效的用户名或密码"。 我已经确认用户名是正确的,并且我理解密码可能无效,因为我没有包含它。 但是要求用户输入他/她的密码不是一个选项,因为启动应用程序自动化的整个原因是为了使用户更容易使用。

有什么建议吗?


这个回答描述了一种方法,其中你需要复制当前进程令牌,使用 SetTokenInformation 删除高完整性SID,然后使用修改后的令牌使用 CreateProcessAsUser 启动新进程。 - Simon MᶜKenzie
我知道这已经过时了,但我在这里找到了一个非常好的方法,比这篇文章上的答案对我更有效。CreateProcessWithTokenW调用失败了。这个链接可以解决问题:https://code.msdn.microsoft.com/windowsapps/CSCreateLowIntegrityProcess-d7cb5e4d/sourcecode?fileId=52580&pathId=1953311234 - tdemay
C++问题对应于这个问题:https://dev59.com/lpjga4cB1Zd3GeqPHDrR - NH.
11个回答

18

您想要实现的目标不是很容易且不受支持。但是,使用少量的黑客技巧是可能的。Aaron Margosis写了一篇文章描述了其中一种技术。

为了引用相关部分,您需要执行以下步骤:

  1. 在当前token中启用SeIncreaseQuotaPrivilege
  2. 获取表示桌面shell的HWND(GetShellWindow)
  3. 获取与该窗口关联的进程的进程ID(PID)(GetWindowThreadProcessId)
  4. 打开该进程(OpenProcess)
  5. 从该进程获取访问令牌(OpenProcessToken)
  6. 使用该令牌创建一个主令牌(DuplicateTokenEx)
  7. 使用该主令牌启动新进程(CreateProcessWithTokenW)

该文章包含一些演示C++源代码的下载链接,从中翻译为C#应该相对简单。


你给我的链接很好用!我不懂c++,但我会尝试将那段代码转换成c#。一旦stack overflow允许我,我会接受你的答案。谢谢! - Tono Nam
@Tono,同样的链接在比这个回答早一年发布的答案中已经提到了。 - TLama
C#的移植版本在哪里可以找到? - Alessio
CreateProcessWithTokenW 对我来说失败了。我在这里找到了另一种方法,它很好地工作:https://code.msdn.microsoft.com/windowsapps/CSCreateLowIntegrityProcess-d7cb5e4d/sourcecode?fileId=52580&pathId=1953311234 - tdemay
1
如果CreateProcessWithTokenW失败,那可能是因为“Secondary Logon Service”被禁用了。不幸的是,许多所谓的“优化”网站建议禁用此“不必要”的服务。IShellDispatch2技术在这方面似乎更可靠,因为它不需要这个服务运行。缺点是它对创建进程的控制较少。 - zett42

14

以下是针对Aaron Margosis的文章的C#代码:

        private static void RunAsDesktopUser(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
            throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));

        // To start process as shell user you will need to carry out these steps:
        // 1. Enable the SeIncreaseQuotaPrivilege in your current token
        // 2. Get an HWND representing the desktop shell (GetShellWindow)
        // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)
        // 4. Open that process(OpenProcess)
        // 5. Get the access token from that process (OpenProcessToken)
        // 6. Make a primary token with that token(DuplicateTokenEx)
        // 7. Start the new process with that primary token(CreateProcessWithTokenW)

        var hProcessToken = IntPtr.Zero;
        // Enable SeIncreaseQuotaPrivilege in this process.  (This won't work if current process is not elevated.)
        try
        {
            var process = GetCurrentProcess();
            if (!OpenProcessToken(process, 0x0020, ref hProcessToken))
                return;

            var tkp = new TOKEN_PRIVILEGES
            {
                PrivilegeCount = 1,
                Privileges = new LUID_AND_ATTRIBUTES[1]
            };

            if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
                return;

            tkp.Privileges[0].Attributes = 0x00000002;

            if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
                return;
        }
        finally
        {
            CloseHandle(hProcessToken);
        }

        // Get an HWND representing the desktop shell.
        // CAVEATS:  This will fail if the shell is not running (crashed or terminated), or the default shell has been
        // replaced with a custom shell.  This also won't return what you probably want if Explorer has been terminated and
        // restarted elevated.
        var hwnd = GetShellWindow();
        if (hwnd == IntPtr.Zero)
            return;

        var hShellProcess = IntPtr.Zero;
        var hShellProcessToken = IntPtr.Zero;
        var hPrimaryToken = IntPtr.Zero;
        try
        {
            // Get the PID of the desktop shell process.
            uint dwPID;
            if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)
                return;

            // Open the desktop shell process in order to query it (get the token)
            hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);
            if (hShellProcess == IntPtr.Zero)
                return;

            // Get the process token of the desktop shell.
            if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
                return;

            var dwTokenRights = 395U;

            // Duplicate the shell's process token to get a primary token.
            // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
            if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))
                return;

            // Start the target process with the new token.
            var si = new STARTUPINFO();
            var pi = new PROCESS_INFORMATION();
            if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
                return;
        }
        finally
        {
            CloseHandle(hShellProcessToken);
            CloseHandle(hPrimaryToken);
            CloseHandle(hShellProcess);
        }

    }

    #region Interop

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

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    private struct LUID_AND_ATTRIBUTES
    {
        public LUID Luid;
        public UInt32 Attributes;
    }

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

    [Flags]
    private enum ProcessAccessFlags : uint
    {
        All = 0x001F0FFF,
        Terminate = 0x00000001,
        CreateThread = 0x00000002,
        VirtualMemoryOperation = 0x00000008,
        VirtualMemoryRead = 0x00000010,
        VirtualMemoryWrite = 0x00000020,
        DuplicateHandle = 0x00000040,
        CreateProcess = 0x000000080,
        SetQuota = 0x00000100,
        SetInformation = 0x00000200,
        QueryInformation = 0x00000400,
        QueryLimitedInformation = 0x00001000,
        Synchronize = 0x00100000
    }

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

    private enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private 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;
    }

    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

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

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

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);


    [DllImport("user32.dll")]
    private static extern IntPtr GetShellWindow();

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);

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

    [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

    #endregion

1
这是一个很好的开始,谢谢!扩展它以返回Process对象并传递参数相当容易:CreateProcessWithTokenW(hPrimaryToken, 0, fileName, $"\"{fileName}\" {arguments}",...return Process.GetProcessById(pi.dwProcessId);请注意,您可以等待进程退出,但无法检查其错误代码。 - Andrew Hanlon
如何将此解决方案应用于运行我的自己的Windows应用程序? - shmnff
你好,我正在尝试使用这种情况以提升用户权限运行进程。例如:我正在从管理员帐户(或SYSTEM帐户)运行代码,并且需要以桌面用户的身份(该用户不是管理员组的一部分)运行新进程,但我需要以管理员权限运行新进程。 - Darksody

11

这是一个我几年前看到过并现在重新访问的非常老的问题。由于它是第一个谷歌搜索结果...我会在这里发布我的答案。

我发现的所有解决方案都非常复杂和荒谬。多年来,我偶然发现了一种解决方案,我没有在任何地方看到过文档,并且一直没有真正分享,直到现在。

代码非常简单......本质上,我们正在编写一个批处理文件,其中包含您想要运行的进程的可执行文件的名称/路径,以及您想要的任何参数。然后,我们使用批处理文件的路径生成一个explorer.exe进程...

File.WriteAllText(@"C:\test.bat", @"C:\test.exe -randomArgs");

var proc = new Process
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "explorer.exe",
        Arguments = @"C:\test.bat",
        UseShellExecute = true,
        Verb = "runas",
        WindowStyle = ProcessWindowStyle.Hidden
    }
};
proc.Start();

我们生成的资源管理器进程会被操作系统立即终止,但是!正在运行的根资源管理器进程会执行批处理文件!你可以将可执行文件的名称提供给资源管理器(explorer.exe),它会做同样的事情,但这种方法不支持参数。

据我所知,这可能是一个漏洞或未记录的功能。但是我无法想象它如何被恶意使用,因为它允许降低权限...... 这适用于Windows 7/8/8.1/10。


这如何解决以不是管理员而是不同用户身份启动进程的问题? - user3700562
@user3700562因为它使用explorer.exe作为启动批处理文件的网关。 - Gerardo Grignoli
这在我的Windows 10上无法运行。“此应用程序无法在您的计算机上运行。” Windows拒绝启动.CMD.BAT - Gerardo Grignoli
这是一个Safescreen错误。如果Safescreen阻止批处理文件,那就是完全不同的问题。我刚刚测试了一下没有Safescreen,它仍然可以工作。我维护的应用程序之一也使用了这个功能,所以如果它失败了,对我来说将是一个重大的麻烦。 - Eugene
如果在 .bat 文件中添加 del %0 行,则可以在运行后删除自身。 - Serhii Kheilyk

6
假设您是在通知应用程序进行干净关闭而不是终止它,并且如果您仍然能够在发布更新程序之前对应用程序进行更改,那么一个简单的解决方案是在退出之前让应用程序启动一个中间过程。您可以将中间进程的可执行文件创建在临时位置。完成更新后,向中间进程发送信号以重新启动应用程序并退出。这样,一切都会自然发生,您就不必烦恼了。
另一种选择是使用 OpenProcessOpenProcessTokenDuplicateToken 获取应用程序的安全令牌副本,然后再结束它。然后,您可以使用 CreateProcessAsUser 在原始上下文中重新启动应用程序。这两种方法都适用于更新程序在不同帐户和/或不同会话下运行的情况。

4

在CodePlex上有一个名为User Account Control Helpers的项目。

该项目提供了一个库,用于与UAC机制交互。

在库中,您将找到一个名为UserAccountControl的类。该类具有一个静态方法CreateProcessAsStandardUser,用于从具有标准用户权限的提升进程启动进程。

简而言之,该函数打开桌面 shell 进程的进程令牌。然后,它复制该令牌以获取主要令牌。然后使用此令牌在已登录的用户下启动新进程。

有关更多信息,请阅读Aaron Margosis的博客文章。


4

我遇到了类似的问题。在ProcessStartInfo中有一个密码字段,关键是您必须以安全字符串的形式提供密码。所以代码应该像这样:

System.Security.SecureString password = new System.Security.SecureString();
password.AppendChar('c1');
//append the all characters of your password, you could probably use a loop and then,
Process p =new Process();
p.UseShellExecute = false;
p.UserName = Environment.UserName;
p.FileName = file ;
p.Sassword=password;
p.Start();

是的,您需要正确的密码。相比于该论坛上最佳答案,我发现这更容易。 - Aster Veigas
如果你不知道密码,那么这并不容易。除非你拥有这个盒子,否则你怎么能希望知道密码呢? - David Heffernan
那就是我和@DavidHeffernan提到的问题所在。 - Aster Veigas

4
在我的应用程序中,最初我使用了user3122201的解决方案,它非常有效。但是,我希望提升的应用程序可以使用匿名管道与低权限应用程序通信。为了实现这一点,我需要一个允许管道句柄被继承的解决方案。
我提出了以下解决方案,允许句柄继承。
请注意,此解决方案和user3122201的另一个小差异:下面的方法以相同用户的进程运行,但具有受限访问权限,而user3122201的方法则以桌面用户的进程运行。
    public static class ProcessHelper
    {
        /// Runs a process as a non-elevated version of the current user.
        public static Process? RunAsRestrictedUser(string fileName, string? args = null)
        {
            if (string.IsNullOrWhiteSpace(fileName))
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));

            if (!GetRestrictedSessionUserToken(out var hRestrictedToken))
            {
                return null;
            }

            try
            {
                var si = new STARTUPINFO();
                var pi = new PROCESS_INFORMATION();
                var cmd = new StringBuilder();
                cmd.Append('"').Append(fileName).Append('"');
                if (!string.IsNullOrWhiteSpace(args))
                {
                    cmd.Append(' ').Append(args);
                }

                if (!CreateProcessAsUser(
                    hRestrictedToken,
                    null,
                    cmd,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    true, // inherit handle
                    0,
                    IntPtr.Zero,
                    Path.GetDirectoryName(fileName),
                    ref si,
                    out pi))
                {
                    return null;
                }

                return Process.GetProcessById(pi.dwProcessId);
            }
            finally
            {
                CloseHandle(hRestrictedToken);
            }
        }

        // based on https://dev59.com/L3HYa4cB1Zd3GeqPICQD#16110126
        private static bool GetRestrictedSessionUserToken(out IntPtr token)
        {
            token = IntPtr.Zero;
            if (!SaferCreateLevel(SaferScope.User, SaferLevel.NormalUser, SaferOpenFlags.Open, out var hLevel, IntPtr.Zero))
            {
                return false;
            }

            IntPtr hRestrictedToken = IntPtr.Zero;
            TOKEN_MANDATORY_LABEL tml = default;
            tml.Label.Sid = IntPtr.Zero;
            IntPtr tmlPtr = IntPtr.Zero;

            try
            {
                if (!SaferComputeTokenFromLevel(hLevel, IntPtr.Zero, out hRestrictedToken, 0, IntPtr.Zero))
                {
                    return false;
                }

                // Set the token to medium integrity.
                tml.Label.Attributes = SE_GROUP_INTEGRITY;
                tml.Label.Sid = IntPtr.Zero;
                if (!ConvertStringSidToSid("S-1-16-8192", out tml.Label.Sid))
                {
                    return false;
                }

                tmlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tml));
                Marshal.StructureToPtr(tml, tmlPtr, false);
                if (!SetTokenInformation(hRestrictedToken,
                    TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
                    tmlPtr, (uint)Marshal.SizeOf(tml)))
                {
                    return false;
                }

                token = hRestrictedToken;
                hRestrictedToken = IntPtr.Zero; // make sure finally() doesn't close the handle
            }
            finally
            {
                SaferCloseLevel(hLevel);
                SafeCloseHandle(hRestrictedToken);
                if (tml.Label.Sid != IntPtr.Zero)
                {
                    LocalFree(tml.Label.Sid);
                }
                if (tmlPtr != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(tmlPtr);
                }
            }

            return true;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private 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)]
        private struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public uint Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct TOKEN_MANDATORY_LABEL
        {
            public SID_AND_ATTRIBUTES Label;
        }

        public enum SaferLevel : uint
        {
            Disallowed = 0,
            Untrusted = 0x1000,
            Constrained = 0x10000,
            NormalUser = 0x20000,
            FullyTrusted = 0x40000
        }

        public enum SaferScope : uint
        {
            Machine = 1,
            User = 2
        }

        [Flags]
        public enum SaferOpenFlags : uint
        {
            Open = 1
        }

        [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool SaferCreateLevel(SaferScope scope, SaferLevel level, SaferOpenFlags openFlags, out IntPtr pLevelHandle, IntPtr lpReserved);

        [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved);

        [DllImport("advapi32", SetLastError = true)]
        private static extern bool SaferCloseLevel(IntPtr hLevelHandle);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr hObject);

        private static bool SafeCloseHandle(IntPtr hObject)
        {
            return (hObject == IntPtr.Zero) ? true : CloseHandle(hObject);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr LocalFree(IntPtr hMem);

        enum TOKEN_INFORMATION_CLASS
        {
            /// <summary>
            /// The buffer receives a TOKEN_USER structure that contains the user account of the token.
            /// </summary>
            TokenUser = 1,

            /// <summary>
            /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
            /// </summary>
            TokenGroups,

            /// <summary>
            /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
            /// </summary>
            TokenPrivileges,

            /// <summary>
            /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
            /// </summary>
            TokenOwner,

            /// <summary>
            /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
            /// </summary>
            TokenPrimaryGroup,

            /// <summary>
            /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
            /// </summary>
            TokenDefaultDacl,

            /// <summary>
            /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
            /// </summary>
            TokenSource,

            /// <summary>
            /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
            /// </summary>
            TokenType,

            /// <summary>
            /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
            /// </summary>
            TokenImpersonationLevel,

            /// <summary>
            /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
            /// </summary>
            TokenStatistics,

            /// <summary>
            /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
            /// </summary>
            TokenRestrictedSids,

            /// <summary>
            /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token.
            /// </summary>
            TokenSessionId,

            /// <summary>
            /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
            /// </summary>
            TokenGroupsAndPrivileges,

            /// <summary>
            /// Reserved.
            /// </summary>
            TokenSessionReference,

            /// <summary>
            /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
            /// </summary>
            TokenSandBoxInert,

            /// <summary>
            /// Reserved.
            /// </summary>
            TokenAuditPolicy,

            /// <summary>
            /// The buffer receives a TOKEN_ORIGIN value.
            /// </summary>
            TokenOrigin,

            /// <summary>
            /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
            /// </summary>
            TokenElevationType,

            /// <summary>
            /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
            /// </summary>
            TokenLinkedToken,

            /// <summary>
            /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
            /// </summary>
            TokenElevation,

            /// <summary>
            /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
            /// </summary>
            TokenHasRestrictions,

            /// <summary>
            /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
            /// </summary>
            TokenAccessInformation,

            /// <summary>
            /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
            /// </summary>
            TokenVirtualizationAllowed,

            /// <summary>
            /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
            /// </summary>
            TokenVirtualizationEnabled,

            /// <summary>
            /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level.
            /// </summary>
            TokenIntegrityLevel,

            /// <summary>
            /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
            /// </summary>
            TokenUIAccess,

            /// <summary>
            /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
            /// </summary>
            TokenMandatoryPolicy,

            /// <summary>
            /// The buffer receives the token's logon security identifier (SID).
            /// </summary>
            TokenLogonSid,

            /// <summary>
            /// The maximum value for this enumeration
            /// </summary>
            MaxTokenInfoClass
        }

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

        const uint SE_GROUP_INTEGRITY = 0x00000020;

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string? lpApplicationName,
            StringBuilder? lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string? lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);
    }

2

实用的VB.NET代码,可以从高级父进程以默认Shell权限启动一个进程

#Region "References"

Imports System.Runtime.InteropServices

#End Region

Public Class LaunchProcess

#Region "Native Methods"

    <DllImport("User32.dll", SetLastError:=True)> Private Shared Function GetShellWindow() As IntPtr
    End Function

    <DllImport("advapi32.dll", SetLastError:=True)> Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)> Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, ByRef lpdwProcessId As IntPtr) As Integer
    End Function

    <DllImport("kernel32.dll")> Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As IntPtr) As IntPtr
    End Function

    <DllImport("advapi32.dll", SetLastError:=True)> _
    Private Shared Function DuplicateTokenEx( _
    ByVal ExistingTokenHandle As IntPtr, _
    ByVal dwDesiredAccess As UInt32, _
    ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _
    ByVal ImpersonationLevel As Integer, _
    ByVal TokenType As Integer, _
    ByRef DuplicateTokenHandle As System.IntPtr) As Boolean
    End Function

    <DllImport("advapi32.dll", SetLastError:=True)> Private Shared Function LookupPrivilegeValue(lpSystemName As String, lpName As String, ByRef lpLuid As LUID) As Boolean
    End Function

    <DllImport("advapi32.dll", SetLastError:=True)> _
    Private Shared Function AdjustTokenPrivileges( _
    ByVal TokenHandle As IntPtr, _
    ByVal DisableAllPrivileges As Boolean, _
    ByRef NewState As TOKEN_PRIVILEGES, _
    ByVal BufferLengthInBytes As Integer, _
    ByRef PreviousState As TOKEN_PRIVILEGES, _
    ByRef ReturnLengthInBytes As Integer _
  ) As Boolean
    End Function

    <DllImport("advapi32", SetLastError:=True, CharSet:=CharSet.Unicode)> Private Shared Function CreateProcessWithTokenW(hToken As IntPtr, dwLogonFlags As Integer, lpApplicationName As String, lpCommandLine As String, dwCreationFlags As Integer, lpEnvironment As IntPtr, lpCurrentDirectory As IntPtr, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
    End Function

#End Region

#Region "Structures"

    <StructLayout(LayoutKind.Sequential)> Private Structure SECURITY_ATTRIBUTES

        Friend nLength As Integer
        Friend lpSecurityDescriptor As IntPtr
        Friend bInheritHandle As Integer

    End Structure

    Private Structure TOKEN_PRIVILEGES

        Friend PrivilegeCount As Integer
        Friend TheLuid As LUID
        Friend Attributes As Integer

    End Structure

    Private Structure LUID

        Friend LowPart As UInt32
        Friend HighPart As UInt32

    End Structure

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> Private Structure STARTUPINFO

        Friend cb As Integer
        Friend lpReserved As String
        Friend lpDesktop As String
        Friend lpTitle As String
        Friend dwX As Integer
        Friend dwY As Integer
        Friend dwXSize As Integer
        Friend dwYSize As Integer
        Friend dwXCountChars As Integer
        Friend dwYCountChars As Integer
        Friend dwFillAttribute As Integer
        Friend dwFlags As Integer
        Friend wShowWindow As Short
        Friend cbReserved2 As Short
        Friend lpReserved2 As Integer
        Friend hStdInput As Integer
        Friend hStdOutput As Integer
        Friend hStdError As Integer

    End Structure

    Private Structure PROCESS_INFORMATION

        Friend hProcess As IntPtr
        Friend hThread As IntPtr
        Friend dwProcessId As Integer
        Friend dwThreadId As Integer

    End Structure

#End Region

#Region "Enumerations"

    Private 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

    End Enum

#End Region

#Region "Constants"

    Private Const SE_PRIVILEGE_ENABLED = &H2L
    Private Const PROCESS_QUERY_INFORMATION = &H400
    Private Const TOKEN_ASSIGN_PRIMARY = &H1
    Private Const TOKEN_DUPLICATE = &H2
    Private Const TOKEN_IMPERSONATE = &H4
    Private Const TOKEN_QUERY = &H8
    Private Const TOKEN_QUERY_SOURCE = &H10
    Private Const TOKEN_ADJUST_PRIVILEGES = &H20
    Private Const TOKEN_ADJUST_GROUPS = &H40
    Private Const TOKEN_ADJUST_DEFAULT = &H80
    Private Const TOKEN_ADJUST_SESSIONID = &H100
    Private Const SecurityImpersonation = 2
    Private Const TokenPrimary = 1
    Private Const SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"

#End Region

#Region "Methods"

    Friend Shared Sub LaunchFile(ByVal FilePath As String, ByVal IsWaitToFinish As Boolean)

        Try

            'Enable the SeIncreaseQuotaPrivilege in current token
            Dim HPrcsToken As IntPtr = Nothing
            OpenProcessToken(Process.GetCurrentProcess.Handle, TOKEN_ADJUST_PRIVILEGES, HPrcsToken)

            Dim TokenPrvlgs As TOKEN_PRIVILEGES
            TokenPrvlgs.PrivilegeCount = 1
            LookupPrivilegeValue(Nothing, SE_INCREASE_QUOTA_NAME, TokenPrvlgs.TheLuid)
            TokenPrvlgs.Attributes = SE_PRIVILEGE_ENABLED

            AdjustTokenPrivileges(HPrcsToken, False, TokenPrvlgs, 0, Nothing, Nothing)

            'Get window handle representing the desktop shell
            Dim HShellWnd As IntPtr = GetShellWindow()

            'Get the ID of the desktop shell process
            Dim ShellPID As IntPtr
            GetWindowThreadProcessId(HShellWnd, ShellPID)

            'Open the desktop shell process in order to get the process token
            Dim HShellPrcs As IntPtr = OpenProcess(PROCESS_QUERY_INFORMATION, False, ShellPID)
            Dim HShellPrcSToken As IntPtr = Nothing
            Dim HPrimaryToken As IntPtr = Nothing

            'Get the process token of the desktop shell
            OpenProcessToken(HShellPrcs, TOKEN_DUPLICATE, HShellPrcSToken)

            'Duplicate the shell's process token to get a primary token
            Dim TokenRights As UInteger = TOKEN_QUERY Or TOKEN_ASSIGN_PRIMARY Or TOKEN_DUPLICATE Or TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID
            DuplicateTokenEx(HShellPrcSToken, TokenRights, Nothing, SecurityImpersonation, TokenPrimary, HPrimaryToken)

            Dim StartInfo As STARTUPINFO = Nothing
            Dim PrcsInfo As PROCESS_INFORMATION = Nothing

            StartInfo.cb = Marshal.SizeOf(StartInfo)
            Dim IsSuccessed As Boolean = CreateProcessWithTokenW(HPrimaryToken, 1, FilePath, "", 0, Nothing, Nothing, StartInfo, PrcsInfo)

            If IsSuccessed = True Then

                If IsWaitToFinish = True Then

                    Try

                        Dim Prcs As Process = Process.GetProcessById(PrcsInfo.dwProcessId)
                        Prcs.WaitForExit()

                    Catch ex As Exception
                    End Try

                End If

            Else

                'Could not launch process with shell token may be the process needs admin rights to launch, we will try to launch it with default parent process permissions.

                Dim Prcs As New Process
                Prcs.StartInfo.FileName = FilePath
                Prcs.Start()

                If IsWaitToFinish = True Then Prcs.WaitForExit()

            End If

        Catch ex As Exception
        End Try

    End Sub

#End Region

End Class

使用LaunchProcess类

LaunchProcess.LaunchFile("C:\Program Files\Test\text.exe", False)

您可以使用Telerik代码转换器将代码转换为C# http://converter.telerik.com/

0

正如作者所述,这个问题还没有解决。

我现在也遇到了同样的问题,我想描述一下我是如何解决这个问题的。

你知道这并不容易,但最好的解决方案是通过另一个非管理员进程强制计算机启动“*.exe”文件。我所做的是在任务计划程序上创建一个任务没有最高权限参数,这意味着可以设置时间或手动运行此任务。

这似乎很愚蠢,但似乎没有其他办法。

你可以查看这个链接,了解如何在Windows任务计划程序上创建新任务。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接