您可以使用 CreateProcessAsUserW 用于未升级用户令牌的启动过程。但这里存在几个问题 - 你需要 SE_INCREASE_QUOTA_NAME 和 SE_ASSIGNPRIMARYTOKEN_NAME 如果是的话,可以打电话给这个api hToken 是调用者主要令牌的受限版本。
CreateProcessAsUserW
SE_INCREASE_QUOTA_NAME
SE_ASSIGNPRIMARYTOKEN_NAME
第二个如何获得用户令牌?您可以使用 WTSQueryUserToken 为此,但是为了打电话给你需要的api SE_TCB_NAME 特权
WTSQueryUserToken
SE_TCB_NAME
所以你需要拥有/获得3特权 SE_ASSIGNPRIMARYTOKEN_NAME , SE_TCB_NAME ,和 SE_INCREASE_QUOTA_NAME 。一般来说 LocalSystem 过程有它。如果有的话,我们可以打开一些这个过程 SE_DEBUG_PRIVILEGE 。
LocalSystem
SE_DEBUG_PRIVILEGE
所以一般来说我们需要做下一步:
SessionId
-----代码:---------------
inline ULONG BOOL_TO_ERROR(BOOL f) { return f ? NOERROR : GetLastError(); } // use in _alloca(guz) because _alloca(0) work incorrect // return 0 pointer instead allocates a zero-length item volatile UCHAR guz; ULONG takePrivileges(HANDLE hToken, ::PTOKEN_PRIVILEGES ptp, ULONG cb, BOOL& bContinue) { if (ULONG PrivilegeCount = ptp->PrivilegeCount) { int n = 3; BOOL fAdjust = FALSE; ::PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges; do { switch (Privileges->Luid.LowPart) { case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE: case SE_INCREASE_QUOTA_PRIVILEGE: case SE_TCB_PRIVILEGE: if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED)) { Privileges->Attributes |= SE_PRIVILEGE_ENABLED; fAdjust = TRUE; } if (!--n) { bContinue = FALSE; ULONG dwError = BOOL_TO_ERROR(DuplicateTokenEx(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE, 0, ::SecurityImpersonation, ::TokenImpersonation, &hToken)); if (dwError == NOERROR) { if (fAdjust) { AdjustTokenPrivileges(hToken, FALSE, ptp, cb, NULL, NULL); dwError = GetLastError(); } if (dwError == NOERROR) { dwError = BOOL_TO_ERROR(SetThreadToken(0, hToken)); } CloseHandle(hToken); } return dwError; } } } while (Privileges++, --PrivilegeCount); } return ERROR_NOT_FOUND; } ULONG GetPrivileges() { ULONG dwError; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { dwError = ERROR_NOT_FOUND; PROCESSENTRY32W pe = { sizeof(pe) }; if (Process32FirstW(hSnapshot, &pe)) { ULONG cb = 0, rcb = 0x100; PVOID stack = alloca(guz); union { PVOID buf; ::PTOKEN_PRIVILEGES ptp; }; BOOL bContinue = TRUE; do { if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID)) { HANDLE hToken; if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken)) { do { if (cb < rcb) { cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack); } if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, &rcb)) { dwError = takePrivileges(hToken, ptp, rcb, bContinue); break; } } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER); CloseHandle(hToken); } CloseHandle(hProcess); } } while (bContinue && Process32NextW(hSnapshot, &pe)); } CloseHandle(hSnapshot); } else { dwError = GetLastError(); } return dwError; } ULONG RunNotElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine, PCWSTR lpCurrentDirectory) { HANDLE hToken, hDupToken = 0; ULONG SessionId; ULONG dwError = BOOL_TO_ERROR(ProcessIdToSessionId(GetCurrentProcessId(), &SessionId)); if (NOERROR == dwError && (NOERROR == (dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))))) { dwError = BOOL_TO_ERROR(DuplicateTokenEx(hToken, TOKEN_IMPERSONATE|TOKEN_ADJUST_PRIVILEGES, 0, ::SecurityImpersonation, ::TokenImpersonation, &hDupToken)); CloseHandle(hToken); if (dwError == NOERROR) { // get SE_DEBUG_PRIVILEGE static ::TOKEN_PRIVILEGES tp = { 1, { { {SE_DEBUG_PRIVILEGE }, SE_PRIVILEGE_ENABLED } } }; AdjustTokenPrivileges(hDupToken, FALSE, &tp, 0, 0, 0); if ((dwError = GetLastError()) == NOERROR) { dwError = BOOL_TO_ERROR(SetThreadToken(0, hDupToken)); } CloseHandle(hDupToken); if (dwError == NOERROR) { if (NOERROR == (dwError = GetPrivileges())) { STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi; PVOID lpEnvironment; if (WTSQueryUserToken(SessionId, &hToken)) { dwError = BOOL_TO_ERROR(CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE)); if (dwError == NOERROR) { dwError = BOOL_TO_ERROR(CreateProcessAsUserW( hToken, lpApplicationName, lpCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, lpEnvironment, lpCurrentDirectory, &si, &pi)); DestroyEnvironmentBlock(lpEnvironment); } CloseHandle(hToken); if (dwError == NOERROR) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } } SetThreadToken(0, 0); } } } return dwError; } void test_r() { WCHAR cmd[MAX_PATH]; if (GetEnvironmentVariable(L"comspec", cmd, RTL_NUMBER_OF(cmd))) { RunNotElevated1(cmd, L"cmd /k whoami /all",0); } }
您不能,因为shell不公开ShellExecuteEx方法,即使它发生了,返回的进程句柄在您的进程中也无效。
我能想到的最简单的解决方案是创建一个小帮助应用程序,它充当shell和你想要启动的实际应用程序之间的中间人。这个中间人应用程序可以调用 ShellExecuteEx 当子进程退出时,将消息发送回您的实际应用程序。
ShellExecuteEx