c# - 如何启动未提升的进程

标签 c# .net vb.net elevation process-elevation

我的应用在 requestedExecutionLevel 设置为 highestAvailable 时运行。

如何运行未提升的进程?

我尝试了以下方法,但没有用:

Process.Start(new ProcessStartInfo {FileName = "foo.exe", Verb = "open"})

我尝试了以下信任级别来使用 Win32 API 启动我的进程,但它们都无法正常工作:

0
1260: This program is blocked by group policy. For more information, contact your system administrator.

0x1000
The application was unable to start correctly (0xc0000142). Click OK to close the application. 

0x10000
Process starts then hangs

0x20000
All options are not available

0x40000
Runs as admin

如果我从提升的应用程序运行 tskill foo,它会以正确的权限重新启动 foo。

我需要的是一种无需指定信任级别的解决方案。该进程应自动以正确的信任级别启动,就像 tskill 工具以正确的信任级别重新启动 foo.exe 一样。用户选择并运行 foo.exe,所以它可以是任何东西。

如果我能以某种方式获得进程的信任级别,我可以很容易地做到这一点,因为 foo.exe 在我的应用程序可以捕获其信任级别时运行。

最佳答案

Win32 安全管理功能提供了创建具有普通用户权限的受限 token 的能力;使用 token ,您可以调用 CreateProcessAsUser 以使用该 token 运行进程。下面是作为普通用户运行 cmd.exe 的概念证明,无论该进程是否在提升的上下文中运行。

// Initialize variables.  
IntPtr hSaferLevel, hToken;
STARTUPINFO si = default(STARTUPINFO);
SECURITY_ATTRIBUTES processAttributes = default(SECURITY_ATTRIBUTES);
SECURITY_ATTRIBUTES threadAttributes = default(SECURITY_ATTRIBUTES);
PROCESS_INFORMATION pi;
si.cb = Marshal.SizeOf(si);

// The process to start (for demonstration, cmd.exe)
string ProcessName = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.System),
    "cmd.exe");

// Create the restricted token info
if (!SaferCreateLevel(
     SaferScopes.User,
     SaferLevels.NormalUser, // Program will execute as a normal user
     1, // required
     out hSaferLevel,
     IntPtr.Zero))
         throw new Win32Exception(Marshal.GetLastWin32Error());

// From the level create a token
if (!SaferComputeTokenFromLevel(
     hSaferLevel,
     IntPtr.Zero,
     out hToken,
     SaferComputeTokenFlags.None,
     IntPtr.Zero))
         throw new Win32Exception(Marshal.GetLastWin32Error());

// Run the process with the restricted token
if (!CreateProcessAsUser(
     hToken,
     ProcessName,
     null, ref processAttributes, ref threadAttributes,
     true, 0, IntPtr.Zero, null,
     ref si, out pi))
         throw new Win32Exception(Marshal.GetLastWin32Error());

 // Cleanup
 if (!CloseHandle(pi.hProcess))
     throw new Win32Exception(Marshal.GetLastWin32Error());
 if (!CloseHandle(pi.hThread))
     throw new Win32Exception(Marshal.GetLastWin32Error());
 if (!SaferCloseLevel(hSaferLevel))
     throw new Win32Exception(Marshal.GetLastWin32Error());

此方法使用以下 Win32 函数:

  • SaferIdentifyLevel以指示身份级别(受限、正常或提升)。将 levelId 设置为 SAFER_LEVELID_NORMALUSER (0x20000) 提供普通用户级别。
  • SaferComputeTokenFromLevel为提供的级别创建一个 token 。将 NULL 传递给 InAccessToken 参数使用当前线程的标识。
  • CreateProcessAsUser使用提供的 token 创建进程。由于 session 已经是交互式的,因此大多数参数可以保留为默认值。 (第三个参数 lpCommandLine 可以作为字符串提供以指定命令行。)
  • CloseHandle (Kernel32)SaferCloseLevel释放分配的内存。

最后,P/Invoke 代码如下(主要从 pinvoke.net 复制):

[Flags]
public enum SaferLevels : uint
{
    Disallowed = 0,
    Untrusted = 0x1000,
    Constrained = 0x10000,
    NormalUser = 0x20000,
    FullyTrusted = 0x40000
}

[Flags]
public enum SaferComputeTokenFlags : uint
{
    None = 0x0,
    NullIfEqual = 0x1,
    CompareOnly = 0x2,
    MakeIntert = 0x4,
    WantFlags = 0x8
}

[Flags]
public enum SaferScopes : uint
{
    Machine = 1,
    User = 2
}

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

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


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

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

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCreateLevel(
    SaferScopes dwScopeId,
    SaferLevels dwLevelId,
    int OpenFlags,
    out IntPtr pLevelHandle,
    IntPtr lpReserved);

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCloseLevel(
    IntPtr pLevelHandle);

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferComputeTokenFromLevel(
  IntPtr levelHandle,
  IntPtr inAccessToken,
  out IntPtr outAccessToken,
  SaferComputeTokenFlags dwFlags,
  IntPtr lpReserved
);

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

关于c# - 如何启动未提升的进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17765568/

相关文章:

c# - 无法使用syncvars统一设置文本

c# - Automapper 自定义解析器源成员到目标对象列表映射问题

c# - 如何从外部 javascript 文件获取值到代码隐藏文件?

vb.net - 如何给函数和函数参数添加描述?

.net - 静态属性和锁使用

c# - 如何在 C# 中将委托(delegate)转换为对象?

c# - 如何在没有复数父元素的情况下从 xml 反序列化列表?

c# - 如何强制仅匿名访问 Controller 操作?

c# - Azure Durable Functions 中的最大事件响应数据大小限制

c# - 关于 C# Syntax with : and proceeding arguments in class declaration. Online converters failed to convert