通过克隆 Explorer 的令牌,我得到了最好的结果,如下所示:
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 /* QueryInformation */, false, shellProcessId);
var hShellToken = IntPtr.Zero;
if (!WinAPI.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out hShellToken))
throw new Win32Exception();
uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
var hToken = IntPtr.Zero;
WinAPI.DuplicateTokenEx(hShellToken, tokenAccess, IntPtr.Zero, 2 /* SecurityImpersonation */, 1 /* TokenPrimary */, 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 认为该进程已被提升,即使它没有管理访问权限。添加一个 Integrity 列,您会看到它是“高”。
修复相当简单:使用 后SaferComputeTokenFromLevel
,我们需要将令牌完整性级别更改为中等。执行此操作的代码可能如下所示(从MSDN 示例转换而来):
// Get the Medium Integrity SID
if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid))
throw new Win32Exception();
// Construct a structure describing the token integrity level
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);
// Modify the token
if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL,
(uint) Marshal.SizeOf<TOKEN_MANDATORY_LABEL>()
+ WinAPI.GetLengthSid(pMediumIntegritySid)))
throw new Win32Exception();
唉,这仍然不能完全解决问题。该进程将没有管理权限;它不会有很高的完整性,但它仍然会有一个标记为“提升”的令牌。
我不知道这对您来说是否是个问题,但这可能是我最终克隆 Explorer 令牌的原因,如本答案开头所述。
这是我的完整源代码(修改后的 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
{
var si = new WinAPI.STARTUPINFO();
si.cb = Marshal.SizeOf(si);
var processAttributes = new WinAPI.SECURITY_ATTRIBUTES();
var threadAttributes = new WinAPI.SECURITY_ATTRIBUTES();
var args = CommandRunner.ArgsToCommandLine(Args);
if (!WinAPI.SaferCreateLevel(WinAPI.SaferScopes.User, WinAPI.SaferLevels.NormalUser, 1, out hSaferLevel, IntPtr.Zero))
throw new Win32Exception();
if (!WinAPI.SaferComputeTokenFromLevel(hSaferLevel, IntPtr.Zero, out hToken, WinAPI.SaferComputeTokenFlags.None, IntPtr.Zero))
throw new Win32Exception();
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();
if (!WinAPI.CreateProcessAsUser(hToken, null, commandLine, ref processAttributes, ref threadAttributes, true, 0, IntPtr.Zero, null, ref si, out pi))
throw new Win32Exception();
}
finally
{
if (hToken != IntPtr.Zero && !WinAPI.CloseHandle(hToken))
throw new Win32Exception();
if (pMediumIntegritySid != IntPtr.Zero && WinAPI.LocalFree(pMediumIntegritySid) != IntPtr.Zero)
throw new Win32Exception();
if (pTIL != IntPtr.Zero)
Marshal.FreeHGlobal(pTIL);
if (pi.hProcess != IntPtr.Zero && !WinAPI.CloseHandle(pi.hProcess))
throw new Win32Exception();
if (pi.hThread != IntPtr.Zero && !WinAPI.CloseHandle(pi.hThread))
throw new Win32Exception();
}
除了 drf 的答案中列出的定义之外,这里还有您需要的 P/Invoke 定义:
[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);