C# 在管理员权限的进程里降权启动一个权限为普通权限的进程
在管理员权限的进程里降权启动一个权限为普通权限的进程,在根据网上的说法尝试了很多方式,要么不成功降权,要么需要账户名+密码。
最终在 https://blog.csdn.net/WPwalter/article/details/88384279#_explorerexe__50 博客中找到一个比较简单的方式。
var subProcessFileName = "C:\Users\walterlv\Desktop\walterlv.exe"; Process.Start("explorer.exe", subProcessFileName);
因为绝大多数用户启动系统的时候,explorer.exe 进程都是处于运行状态,而如果启动一个新的 explorer.exe,都会自动激活当前正在运行的进程而不会启动新的。
于是我们可以委托默认以普通权限运行的 explorer.exe 来代理启动我们需要启动的子进程,这时启动的子进程便是与 explorer.exe 相同权限的,也就是降权运行了。
通过以上代码,walterlv.exe
就会以与 explorer.exe 相同权限运行,也就是降权运行了。
下面的代码,如果发现自己是以管理员权限运行的,那么就降权重新运行自己,然后自己退出。
var identity = WindowsIdentity.GetCurrent(); var principal = new WindowsPrincipal(identity); if (principal.IsInRole(WindowsBuiltInRole.Administrator)) { // 检测到当前进程是以管理员权限运行的,于是降权启动自己之后,把自己关掉。 Process.Start("explorer.exe", Assembly.GetEntryAssembly().Location); Shutdown(); return; }
注意:
1. 使用以上方式启动的进程没办法传递初始入参。
组内大佬 @vinpay 提醒,可以先用explorer.exe启动一个中继进程B,有管理员权限的进程A将入参保存到本地,在中继进程启动进程C,并且在本地读取入参,把入参传递给进程C。由于进程B是普通权限,所有启动的进程C也是普通管理员权限。
2. 如果文件地址在C:\Program Files (x86)中,右键exe——复制文件地址,然后粘贴VS替换一个字符串,会出现路径中的Files与(x86)中间的空格被删掉的情况。
最近一位同事发现可以使用复制令牌的方式降权启动一个权限为普通权限的进程,并且可以直接传参。
var procs = Process.GetProcessesByName("explorer"); if (procs.Length > 0 && procs[0] is { } process) { var cmds = $"D:\\test.exe 123"; var processElevation = ProcessTokenHelper.CreateProcessAsSameToken(process.Handle, cmds); }
using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace StartProcessByCopyToken { public static class ProcessTokenHelper { /// <summary> /// 进程是否拥有提升的令牌 /// </summary> /// <param name="processHandle"></param> /// <param name="isElevated"></param> /// <returns></returns> public static bool GetProcessElevation(IntPtr processHandle, out bool isElevated) { isElevated = false; if (!OpenProcessToken(processHandle, TOKEN_QUERY, out var hToken)) return false; try { if (GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevationType, out var elevationType, sizeof(TOKEN_ELEVATION_TYPE), out var dwSize)) { isElevated = elevationType == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull; } return true; } catch (Exception) { return false; } finally { CloseHandle(hToken); } } /// <summary> /// 复制指定进程的令牌,并以复制的令牌启动程序 /// </summary> /// <param name="processHandle"></param> /// <param name="commandLine"></param> /// <returns></returns> public static Process CreateProcessAsSameToken(IntPtr processHandle, string commandLine) { var hToken = IntPtr.Zero; var duplicateToken = IntPtr.Zero; if (string.IsNullOrWhiteSpace(commandLine)) return null; try { if (!OpenProcessToken(processHandle, TOKEN_DUPLICATE, out hToken)) return null; var securityAttrs = new SECURITY_ATTRIBUTES(); securityAttrs.Length = Marshal.SizeOf(securityAttrs); var startupInfo = new STARTUPINFO(); DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, ref securityAttrs, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref duplicateToken); startupInfo.cb = Marshal.SizeOf(securityAttrs); CreateProcessWithToken(duplicateToken, 0, null, commandLine, 0, IntPtr.Zero, null, ref startupInfo, out var procInfo ); return Process.GetProcessById((int)procInfo.dwProcessId); } catch { return null; } finally { if (hToken != IntPtr.Zero) CloseHandle(hToken); if (duplicateToken != IntPtr.Zero) CloseHandle(duplicateToken); } } [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool OpenProcessToken(IntPtr processHandle, int desiredAccess, out IntPtr tokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInformationClass, out TOKEN_ELEVATION_TYPE tokenInformation, int tokenInformationLength, out uint returnLength); [DllImport("advapi32.dll", EntryPoint = "CreateProcessWithToken", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern bool CreateProcessWithToken(IntPtr hToken, int dwLogonFlags, String lpApplicationName, String lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] private static extern bool DuplicateTokenEx(IntPtr existingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int tokenType, int impersonationLevel, ref IntPtr duplicateTokenHandle); private const int TOKEN_DUPLICATE = 0x0002; private const int TOKEN_QUERY = 0x0008; private const uint MAXIMUM_ALLOWED = 0x2000000; private enum TOKEN_INFORMATION_CLASS { TokenElevationType = 18, TokenLinkedToken = 19 } private enum TOKEN_ELEVATION_TYPE { TokenElevationTypeDefault = 1, TokenElevationTypeFull = 2, TokenElevationTypeLimited = 3 } private enum SECURITY_IMPERSONATION_LEVEL : int { SecurityAnonymous = 0, SecurityIdentification = 1, SecurityImpersonation = 2, SecurityDelegation = 3, } private enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation = 2 } [StructLayout(LayoutKind.Sequential)] private struct SECURITY_ATTRIBUTES { public int Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } [StructLayout(LayoutKind.Sequential)] private struct STARTUPINFO { public int cb; public String lpReserved; public String lpDesktop; public String lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } } }