有一个启动程序 - 一个C ++程序,您需要以管理员身份运行。启动程序也代表管理员启动另一个进程,因此,使用普通用户权限运行的第三方程序(AutoHotkey)无法访问它。第二个进程不需要管理员权限,因此我希望以普通用户的权限实现启动。怎么做?
目前我正在使用boost::process::system
运行流程。
答案 0 :(得分:3)
Raymond Chen有一篇关于这个主题的博客文章:
How can I launch an unelevated process from my elevated process and vice versa?
从一个不高尚的流程转变为一个升级的流程很容易。您可以通过将
runas
谓词传递给ShellExecute或ShellExecuteEx来运行具有提升的进程。走另一条路是比较棘手的。首先,很难让你的令牌正确地去除高程性质。而另一方面,即使你能做到这一点,也不是正确的事情,因为没有提升的用户可能与提升的用户不同。
...
此处的解决方案是返回资源管理器并请求资源管理器为您启动该程序。由于资源管理器作为原始的非高级用户运行,因此程序...将作为[用户]运行。
他的文章提供了以下代码来启动一个非高效的流程,并详细解释了代码实际在做什么(下面提到的FindDesktopFolderView
函数在他的Manipulating the positions of desktop icons博客文章中定义):< / p>
#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>
// FindDesktopFolderView incorporated by reference
void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
CComPtr<IShellView> spsv;
FindDesktopFolderView(IID_PPV_ARGS(&spsv));
CComPtr<IDispatch> spdispView;
spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
spdispView->QueryInterface(riid, ppv);
}
void ShellExecuteFromExplorer(
PCWSTR pszFile,
PCWSTR pszParameters = nullptr,
PCWSTR pszDirectory = nullptr,
PCWSTR pszOperation = nullptr,
int nShowCmd = SW_SHOWNORMAL)
{
CComPtr<IShellFolderViewDual> spFolderView;
GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
CComPtr<IDispatch> spdispShell;
spFolderView->get_Application(&spdispShell);
CComQIPtr<IShellDispatch2>(spdispShell)
->ShellExecute(CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(pszDirectory ? pszDirectory : L""),
CComVariant(pszOperation ? pszOperation : L""),
CComVariant(nShowCmd));
}
int __cdecl wmain(int argc, wchar_t **argv)
{
if (argc < 2) return 0;
CCoInitialize init;
ShellExecuteFromExplorer(
argv[1],
argc >= 3 ? argv[2] : L"",
argc >= 4 ? argv[3] : L"",
argc >= 5 ? argv[4] : L"",
argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);
return 0;
}
答案 1 :(得分:1)
如果我们以管理员身份运行(更具体的是,在令牌中启用了 S-1-5-32-544&#39;管理员&#39; 组),我们可以打开本地系统进程令牌(它授予“管理员”所需的所有权限。 所以我们可以做下一步:
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_QUERY_LIMITED_INFORMATION
TOKEN_QUERY|TOKEN_DUPLICATE
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
和SE_INCREASE_QUOTA_PRIVILEGE
-
它需要致电CreateProcessAsUser
和SE_TCB_PRIVILEGE
需要WTSQueryUserToken
TokenImpersonation
类型。现在我们可以
WTSQueryUserToken
- 因为没有提升
用户令牌CreateProcessAsUser
使用此令牌代码:
static volatile UCHAR guz;
ULONG RunNonElevated(_In_ ULONG SessionId,
_In_ HANDLE hToken,
_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation)
{
ULONG err;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);
union {
PVOID buf;
::PTOKEN_PRIVILEGES ptp;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, &rcb))
{
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)
{
err = NOERROR;
if (DuplicateTokenEx(hToken,
TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
0, ::SecurityImpersonation, ::TokenImpersonation,
&hToken))
{
if (fAdjust)
{
AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
err = GetLastError();
}
if (err == NOERROR)
{
if (SetThreadToken(0, hToken))
{
HANDLE hUserToken;
if (WTSQueryUserToken(SessionId, &hUserToken))
{
if (!CreateProcessAsUserW(hUserToken,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation))
{
err = GetLastError();
}
CloseHandle(hUserToken);
}
else
{
err = GetLastError();
}
SetThreadToken(0, 0);
}
else
{
err = GetLastError();
}
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
}
} while (Privileges++, --PrivilegeCount);
}
return ERROR_NOT_FOUND;
}
} while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG RunNonElevated(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
ULONG SessionId;
if (!ProcessIdToSessionId(GetCurrentProcessId(), &SessionId))
{
return GetLastError();
}
BOOLEAN b;
RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, b);
ULONG err = NOERROR;
// much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
err = RunNonElevated(
SessionId,
hToken,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
并测试:
void test()
{
STARTUPINFO si = { sizeof(si)};
PROCESS_INFORMATION pi;
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (!RunNonElevated(ApplicationName, L"cmd /k whoami.exe /priv /groups",0,0,0,0,0,0,&si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
}
有了这个,我们不仅可以运行不升级的进程,而且,如果需要,可以使用重复的句柄
启动它