我有一個提升的過程,在用戶在UAC對話框中回答「是」後啓動。我該如何「解除」一個過程
該過程開始正常,一切按預期工作。
現在我需要在某個時候「解除」該進程,換句話說,進程應該不會像用戶正常啓動一樣提升。
示例場景
- 用戶A登錄
- 用戶A啓動過程P1,這將經由UAC
- 高架過程P1 lauchches過程P2和P2 should'nt得以升高和升高應該在用戶A下再次運行。
有沒有辦法做到這一點?
我有一個提升的過程,在用戶在UAC對話框中回答「是」後啓動。我該如何「解除」一個過程
該過程開始正常,一切按預期工作。
現在我需要在某個時候「解除」該進程,換句話說,進程應該不會像用戶正常啓動一樣提升。
示例場景
有沒有辦法做到這一點?
高架進程已鏈接令牌 - 它指的是非高架用戶會話。
第一種方式:我們可以通過兩種方式使用此鏈接的標記
TokenPrimary
(對於這一點,我們需要有SE_TCB_PRIVILEGE
當我們查詢這個令牌)本SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
和SE_INCREASE_QUOTA_PRIVILEGE
CreateProcessAsUser
之前。因爲提升令牌有 SE_DEBUG_PRIVILEGE
任務可能第二種方式:
查詢從鏈接的標記登錄會話ID(從TOKEN_STATISTICS
AuthenticationId
)
發現過程與流程令牌中的AuthenticationId
相同。
使用此過程父進程的幫助 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
實施方式1:
static volatile UCHAR guz;
ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
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))
{
TOKEN_LINKED_TOKEN tlt;
if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
STARTUPINFO si = { sizeof (si) };
PROCESS_INFORMATION pi;
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
err = GetLastError();
}
CloseHandle(tlt.LinkedToken);
if (err == NOERROR)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
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(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
static TOKEN_PRIVILEGES tp = {
1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
};
AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);
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(hToken, hMyToken, lpApplicationName, lpCommandLine);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_ELEVATION_TYPE tet;
ULONG rcb;
if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
RunNonElevated(hToken, lpApplicationName, lpCommandLine);
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
實施方式2:
ULONG CreateProcessEx(HANDLE hProcess,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
SIZE_T Size = 0;
STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
InitializeProcThreadAttributeList(0, 1, 0, &Size);
ULONG err = GetLastError();
if (err = ERROR_INSUFFICIENT_BUFFER)
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
{
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0,
EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(LUID AuthenticationId,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
ULONG err = ERROR_NOT_FOUND;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
if (Process32First(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
BOOL found = FALSE;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart &&
ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
found = TRUE;
err = CreateProcessEx(hProcess,
lpApplicationName,
lpCommandLine);
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
err = CreateProcessEx(ts.AuthenticationId,
lpApplicationName,
lpCommandLine);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
和測試:
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
CreateProcessEx(ApplicationName, cmdline);
RunNonElevated(ApplicationName, cmdline);
}
的方式#2相同的登錄ID(AuthenticationId
)在我們鏈接的標記理論,我們不能發現的過程。但方式#1總是必須工作。總是存在的系統進程,其中有SeTcbPrivilege
(用於獲取鏈接令牌的主要形式)+ SeAssignPrimaryTokenPrivilege
(對於CreateProcessAsUser
)(SeIncreaseQuotaPrivilege
)在msdn中監聽CreateProcessAsUser
的典型需求,但在我的測試中,即使未啓用此權限,也可以工作。但是所有系統進程(運行爲LocalSystem
)都具有令牌中的這3個特權(從smss.exe
開始),並且一些系統進程始終在系統中運行。
這樣的方式#1必須永遠不會失敗和首選。我們也可以在這裏使用例如來自我們的進程的繼承句柄,用於與子進程交互。這在#2方面是不可能的。它顯示了相當的圖片
在開始的完整性,我們檢查TOKEN_ELEVATION_TYPE
,做的工作,只有當它是TokenElevationTypeFull
。如果TokenElevationTypeLimited
我們沒有提升過程 - 所以沒有任何待辦事項。 情況下TokenElevationTypeDefault
意味着或UAC如果關閉(LUA禁用)或我們運行內置的管理員,和Lua沒有此帳戶(所以所有的進程被「架空」,或更確切地說它的令牌不通過CreateRestrictedToken(..LUA_TOKEN..)
過濾)過濾令牌 - 在這情況下,也沒有任何意義嘗試這個用戶
相當完整的答案。我需要詳細檢查這一點。無論如何Upvoted。謝謝 –
@MichaelWalz存在其他一種方式(較短的代碼) - 通過'CreateRestrictedToken(hToken,LUA_TOKEN,0,0,0,0,0,&hLowToken)'(在自己的進程標記上)'設置'TokenIntegrityLevel'代替' WinMediumLabelSid'和最後''CreateProcessAsUser'帶這個標記(*如果hToken是調用者的主標記的受限版本,則不需要'SE_ASSIGNPRIMARYTOKEN_NAME'特權*)。但進程將在另一個登錄會話中運行。結果所有控制檯應用程序無法運行(分配控制檯),但說記事本和GUI應用程序運行良好 – RbMm
聰明。但是,可能存在邊緣情況,目前沒有您想要的令牌運行的進程。 –
下運行「不升高」的過程不能在過程開始後改變進程的令牌。你可以做的是從你的升級過程中啓動一個新的升級過程。請參閱https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643和http://blogs.microsoft.co.il/sasha/2009/07/09/launch-a-process-作爲標準的用戶從 - 一個升高的過程/ –
[本鏈接(https://blogs.msdn.microsoft.com/aaron_margosis/2009/06/06/faq-how-do-i-start-一個程序作爲桌面用戶從一個高架應用程序/)也是非常有用的。 –
請參閱[此問題](https://stackoverflow.com/q/37948064/7571258)如何從高架創建未升級的過程。答案使用'IShellDispatch2'來完成。該工作原理類似的另一種方法是使用'CreateProcessWithTokenW'爲[在外殼的環境中創建一個新的進程(https://www.codeproject.com/Tips/23090/Creating-a-Process-with-Medium)。這可以更好地控制過程的創建方式。 – zett42