Дождитесь процесса, запущенного IShellDispatch2.ShellExecute

#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 , наконец

5. Как мне запустить программу от имени пользователя рабочего стола из приложения с повышенными правами?

Ответ №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);
    }
}