以下是我通过克隆资源管理器的令牌来获得最佳结果的方法:
var shellWnd = WinAPI.GetShellWindow();
if (shellWnd == IntPtr.Zero)
throw new Exception("Could not find shell window");
uint shellProcessId;
WinAPI.GetWindowThreadProcessId(shellWnd, out shellProcessId);
var hShellProcess = WinAPI.OpenProcess(0x00000400 , false, shellProcessId);
var hShellToken = IntPtr.Zero;
if (!WinAPI.OpenProcessToken(hShellProcess, 2 , out hShellToken))
throw new Win32Exception();
uint tokenAccess = 8 | 1 | 2 | 0x80 | 0x100 ;
var hToken = IntPtr.Zero;
WinAPI.DuplicateTokenEx(hShellToken, tokenAccess, IntPtr.Zero, 2 , 1 , out hToken);
var pi = new WinAPI.PROCESS_INFORMATION();
var si = new WinAPI.STARTUPINFO();
si.cb = Marshal.SizeOf(si);
if (!WinAPI.CreateProcessWithTokenW(hToken, 0, null, cmdArgs, 0, IntPtr.Zero, null, ref si, out pi))
throw new Win32Exception();
替代方案
起初我选择了drf的出色答案,但进行了一些扩展。如果上面(克隆Explorer的令牌)不符合您的喜好,请继续阅读但是注意到最后有一个陷阱。
按照drf描述的方法使用时,该过程是在没有管理员访问权限的情况下启动的,但它仍具有很高的完整性级别。典型的未提升的进程具有中等完整性级别。
请尝试这样做:使用Process Hacker查看以这种方式启动的进程的属性;您将看到PH认为该进程已被提升,即使它没有管理员访问权限。添加完整性列,您将看到其为“高”。
修复方法相当简单:在使用SaferComputeTokenFromLevel
后,我们需要将令牌完整性级别更改为“中”。执行此操作的代码可能如下所示(从MSDN示例转换而来):
if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid))
throw new Win32Exception();
var TIL = new TOKEN_MANDATORY_LABEL();
TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */;
TIL.Label.Sid = pMediumIntegritySid;
pTIL = Marshal.AllocHGlobal(Marshal.SizeOf<TOKEN_MANDATORY_LABEL>());
Marshal.StructureToPtr(TIL, pTIL, false);
if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL,
(uint) Marshal.SizeOf<TOKEN_MANDATORY_LABEL>()
+ WinAPI.GetLengthSid(pMediumIntegritySid)))
throw new Win32Exception();
哎呀,这仍然不能完全解决问题。实际上,该进程将不具有管理员访问权限;它将没有高完整性,但是它仍将拥有一个标记为“提升”的令牌。
无论这对您是否构成问题我不知道,但这可能是我最终克隆资源管理器令牌的原因,就像在此答案开头所描述的那样。
以下是我完整的源代码(改自 drf 的答案),展示了所有P/Invoke的辉煌:
var hSaferLevel = IntPtr.Zero;
var hToken = IntPtr.Zero;
var pMediumIntegritySid = IntPtr.Zero;
var pTIL = IntPtr.Zero;
var pi = new WinAPI.PROCESS_INFORMATION();
try
finally
这里是你需要额外添加的P/Invoke定义,除了drf的答案中列出的之外:
And here are the P/Invoke definitions you'll need in addition to those listed in drf's answer:
[DllImport("advapi32.dll", SetLastError = true)]
public static extern Boolean SetTokenInformation(IntPtr TokenHandle, int TokenInformationClass,
IntPtr TokenInformation, UInt32 TokenInformationLength);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll")]
public static extern uint GetLengthSid(IntPtr pSid);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ConvertStringSidToSid(
string StringSid,
out IntPtr ptrSid);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalFree(IntPtr hMem);