Win32安全管理功能提供了创建具有普通用户权限的受限令牌的能力;有了令牌,您可以调用CreateProcessAsUser
使用该令牌运行该进程。下面是作为普通用户运行 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 http://msdn.microsoft.com/en-us/library/windows/desktop/ms722428%28v=vs.85%29.aspx指示身份级别(有限、正常或升高)。设置
levelId
to SAFER_LEVELID_NORMALUSER
(0x20000) 提供普通用户级别。
-
SaferComputeTokenFromLevel http://msdn.microsoft.com/en-us/library/windows/desktop/ms722424%28v=vs.85%29.aspx为所提供的级别创建一个令牌。通过
NULL
InAccessToken 参数使用当前线程的标识。
-
CreateProcessAsUser http://msdn.microsoft.com/en-us/library/windows/desktop/ms682429%28v=vs.85%29.aspx使用提供的令牌创建进程。由于会话已经是交互式的,因此大多数参数可以保留默认值。 (第三个参数,
lpCommandLine
可以作为字符串提供来指定命令行。)
-
CloseHandle(内核32) http://msdn.microsoft.com/en-us/library/windows/desktop/ms724211%28v=vs.85%29.aspx and SaferCloseLevel http://msdn.microsoft.com/en-us/library/windows/desktop/ms722423%28v=vs.85%29.aspx释放分配的内存。
最后,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);