#winapi #process #explorer #shellexecuteex
#winapi #процесс #проводник #shellexecuteex
Вопрос:
Я использую IShellDispatch2.ShellExecute
для запуска процесса под управлением обычного пользователя из моего процесса с повышенными правами, как описано в статье Рэймонда Чена. В отличие от ShellExecuteEx, этот метод не возвращает никакой информации о процессе.
Мне нужно знать, когда завершится запущенный процесс, и мне может понадобиться его код выхода. Есть ли способ получить дескриптор этого процесса (любым другим способом, кроме создания моментальных снимков)?
Комментарии:
1. Почему вы не можете использовать ShellExecuteEx?
2. @battlmonstr — для запуска процесса под другим, невыровненным токеном
3. Если вы знаете данные пользователя, возможно, вызов команды «runas» возможен для запуска как пониженный. Также это кажется актуальным: social.msdn.microsoft.com/Forums/windowsdesktop/en-US /…
4. если у вас есть права на отладку, вы можете взять токен системного процесса, олицетворить его с ним, затем использовать свой токен сеанса с
WTSQueryUserToken
или найти его по logonsid из перечисления процессов и использоватьCreateProcessAsUserW
, наконец
Ответ №1:
Вы не можете, потому что оболочка не предоставляет метод ShellExecuteEx , и даже если бы это было так, возвращенный дескриптор процесса не был бы действительным в вашем процессе.
Наименее хакерское решение, которое я могу придумать, — это создать небольшое вспомогательное приложение, которое действует как посредник между командной оболочкой и реальным приложением, которое вы хотите запустить. Это приложение-посредник может вызвать ShellExecuteEx
и отправить сообщение обратно в ваше реальное приложение после завершения дочернего процесса.
Комментарии:
1. можно, конечно, создать все без вспомогательного процесса, но код будет немного большим
2. @RbMm Это действительно зависит, поиск в списке процессов по определенному имени процесса / пути, запущенный после определенной временной метки, все еще может давать ложноположительные результаты. Процесс посредника (может быть даже реализован в вашем main .EXE) гарантированно не даст ложных срабатываний.
3. нет, у меня есть решение, которое не зависит от имени процесса / пути / времени и не содержит ложных срабатываний
4. нам нужно использовать
CreateProcessAsUserW
. но здесь проблема 2 — получить токен пользователя и привилегии (для вызоваCreateProcessAsUserW
и возможного получения токена пользователя, зависит от способа). мы можем получить требуемые привилегии, если у нас изначально есть привилегия отладки: перечислите процессы, откройте ит-токены, посмотрите, есть ли токены, содержащие требуемые привилегии, дублируйте и олицетворяйте. для получения пользовательского токена мы можем использовать илиWTSQueryUserToken
(из того же сеанса терминала) или снова выполнить поиск у процессов с тем же идентификатором входа sid в токене
Ответ №2:
вы можете использовать CreateProcessAsUserW
для запуска процесса с токеном пользователя без повышенных прав. но здесь существует несколько проблем — вам нужны SE_INCREASE_QUOTA_NAME
и SE_ASSIGNPRIMARYTOKEN_NAME
привилегии для вызова этого api, если hToken является ограниченной версией основного токена вызывающего.
во-вторых, как получить токен пользователя? вы можете использовать WTSQueryUserToken
для этого, но для вызова этого api вам нужны SE_TCB_NAME
привилегии
итак, вам нужно иметь / получить 3 привилегии SE_ASSIGNPRIMARYTOKEN_NAME
, SE_TCB_NAME
и SE_INCREASE_QUOTA_NAME
. в общих LocalSystem
процессах это есть. мы можем запустить часть этого процесса, если у нас есть SE_DEBUG_PRIVILEGE
.
итак, в общем, нам нужно сделать следующее:
- получить self
SessionId
(требуется вызовWTSQueryUserToken
) - включить
SE_DEBUG_PRIVILEGE
в токене процесса или потока (токен с повышенными правами обычно имеет эту привилегию) - найдите процессы с токеном, которые имеют привилегию, требуемую для as
- олицетворять себя с помощью этого токена
- вызов
WTSQueryUserToken
- вызов
CreateProcessAsUserW
—— код: —————
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, BOOLamp; 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 amp; 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,
amp;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, amp;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, amp;hToken))
{
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, amp;rcb))
{
dwError = takePrivileges(hToken, ptp, rcb, bContinue);
break;
}
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
} while (bContinue amp;amp; Process32NextW(hSnapshot, amp;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(), amp;SessionId));
if (NOERROR == dwError amp;amp;
(NOERROR == (dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, amp;hToken)))))
{
dwError = BOOL_TO_ERROR(DuplicateTokenEx(hToken,
TOKEN_IMPERSONATE|TOKEN_ADJUST_PRIVILEGES, 0,
::SecurityImpersonation, ::TokenImpersonation, amp;hDupToken));
CloseHandle(hToken);
if (dwError == NOERROR)
{
// get SE_DEBUG_PRIVILEGE
static ::TOKEN_PRIVILEGES tp = { 1, { { {SE_DEBUG_PRIVILEGE }, SE_PRIVILEGE_ENABLED } } };
AdjustTokenPrivileges(hDupToken, FALSE, amp;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, amp;hToken))
{
dwError = BOOL_TO_ERROR(CreateEnvironmentBlock(amp;lpEnvironment, hToken, FALSE));
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(CreateProcessAsUserW(
hToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT,
lpEnvironment, lpCurrentDirectory, amp;si, amp;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);
}
}