Win32 - запуск наивысшего доступного дочернего процесса как обычного пользовательского процесса - PullRequest
0 голосов
/ 11 мая 2018

Предположим, ваша учетная запись Windows находится в группе администраторов, UAC включен, и вы запускаете программу A с обычными привилегиями пользователя.А никогда не просит возвышения и никогда не получает его.Теперь предположим, что A хочет запустить программу B, у которой в манифесте есть самая высокая доступность.

  • Если A вызывает CreateProcess (B), это приведет к ошибке 740 («требуется повышение прав»)

  • Если A вызывает ShellExecuteEx (B), Windows отобразит окно UAC с просьбой запустить B с повышенными правами.Пользователь может сказать «Да», в этом случае Б будет работать с повышенными правами, или «Нет», и в этом случае запуск не удастся.

Мой вопрос: есть ли способ достичьТретий вариант, где мы просто запускаем B без повышения прав?

Кажется, что это должно быть в принципе возможно, так как "HigherAvailable" означает, что B предпочитает работать с повышением, новполне способен работать в обычном пользовательском режиме.Но я не могу придумать, как это сделать.Я перепробовал все виды вещей с токенами и CreateProcessAsUser (), но, похоже, все сводится к следующему: «наивысший доступный», по-видимому, неизменно ссылается на скрытые привилегии, присущие учетной записи пользователя, а не на действительные привилегии, выраженные в каких-либо явно выраженных привилегиях.созданный токен.

Я надеюсь, что на самом деле есть какой-то способ использовать CreateProcessAsUser () для этого, и я просто упускаю хитрость для правильного построения токена.

Обновление - решено: нижеприведенное решение __COMPAT_LAYER = RunAsInvoker прекрасно работает.Одно предостережение, хотя.Это принудительно заставляет подпроцесс запускаться «как вызывающий»: он применяется, даже если вызываемый exe-файл указывает «requireAdministrator» в своем манифесте.Я думаю, что исходная ошибка «Требуется повышение прав», как правило, предпочтительнее, когда exe указывает «requireAdministrator».Единственная причина, по которой я хотел, чтобы поведение RunAsInvoker для программ, помеченных как «наивысший доступный», заключалось в том, что такие программы прямо говорят: «Я могу нормально работать в любом режиме», поэтому давайте продолжим работу в обычном пользовательском режиме, когда неудобно использовать режим администратора.Но «requireAdministrator» - это другое дело: такие программы говорят: «Я не могу нормально работать без повышенных привилегий».Кажется, для таких программ лучше сразу потерпеть неудачу, чем заставить их работать без повышенных прав, что может привести к возникновению ошибок привилегий / доступа, которые они неправильно запрограммировали для обработки.Поэтому я думаю, что полное решение общего назначения потребовало бы проверки манифеста приложения и применения принуждения RunAsInvoker только в том случае, если в манифесте указано «наивысший доступный».Еще более совершенным решением было бы использование одного из методов, обсуждаемых в другом месте, чтобы дать вызывающей стороне возможность вызывать UAC при представлении с программой «requireAdministrator» и предлагать пользователю возможность запустить его с повышенными правами.Я могу представить себе покрытие CreateProcessEx () с парой новых флагов для «относиться к привилегиям процесса как к наивысшим доступным привилегиям» и «вызывать UAC, если требуется повышение прав».(Другой подход, описанный ниже, перехватывает NTDLL! RtlQueryElevationFlags (), чтобы сообщить CreateProcess (), что UAC недоступен, имеет точно такое же предостережение относительно программ requireAdministrator.)

(вероятно, это говорит о том, что оболочка Windows неЯ даже не предлагаю способ сделать это ... запуск B напрямую из оболочки даст вам блок UAC, который позволит вам запускать с правами администратора или вообще не запускать. Если был какой-либо способ сделать это, блок UACможет предложить третью кнопку для запуска без привилегий. Но опять же, это может быть просто решением UX, что третий вариант слишком запутан для гражданского населения.)

(Обратите внимание, что на StackOverflow довольно много постов, и сайты поддержки разработчиков Microsoft, спрашивающие о очень похожем сценарии, который, к сожалению, здесь не применим. В этом сценарии родительская программа работает с повышенными правами,и он хочет запустить дочерний процесс без повышенных прав. Каноническим примером является установщик, который запускается с повышенными правами, как это делают установщики, который хочет запустить только что установленную программу на обычном пользовательском уровне непосредственно перед его выходом.я написал код о том, как это сделать, и я основал свои попытки на некоторых из этих методов, но это действительно другой сценарий, и решения не работают в моей ситуации. Большая разница в том, что дочерние программы онив этом случае попытка запуска не помечена с наивысшей доступностью - дочерняя программа - это просто обычная программа, которая запускается без участия UAC при обычных обстоятельствах. Есть и другое отличие, заключающееся в том, что в этих сценарияхparent уже работает с повышенными правами, тогда как в моем сценарии parent работает как обычный пользовательский уровень;это немного меняет дело, потому что родительский процесс в этом другом сценарии имеет доступ к нескольким привилегированным операциям над токенами, которые я не могу использовать, потому что само A не имеет повышенных прав.Но, насколько я могу судить, эти привилегированные операции с токенами в любом случае не помогут;тот факт, что у ребенка есть флаг наивысшего доступного, является ключевым элементом моего сценария.)

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

Установите для переменной процесса __COMPAT_LAYER значение RunAsInvoker в вашем процессе. Я не думаю, что это официально задокументировано где-либо, но оно работает вплоть до Vista.

Вы также можете сделать его постоянным, установив его в разделе AppCompatFlags\Layers в реестре.

0 голосов
/ 11 мая 2018

возможный вызов решения взлома CreateProcess от пользователя без прав администратора (с ограниченным доступом администратора) для exe с highestAvailable в манифесте (или от любого пользователя без прав для requireAdministrator exe) - это ловушка RtlQueryElevationFlags вызовите и установите возвращенные флаги равными 0. В настоящее время это работает, но, конечно, нет никаких грантополучателей, которые будут работать в следующих версиях окон, если что-то изменилось.однако как есть.

для перехвата одноразового вызова API - мы можем установить аппаратную точку останова на адрес функции и VEX-обработчик .рабочий код демо:

NTSTATUS NTAPI hookRtlQueryElevationFlags (DWORD* pFlags) 
{
    *pFlags = 0;
    return 0;
}

PVOID pvRtlQueryElevationFlags;

LONG NTAPI OnVex(::PEXCEPTION_POINTERS ExceptionInfo)
{
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP &&
        ExceptionInfo->ExceptionRecord->ExceptionAddress == pvRtlQueryElevationFlags)
    {
        ExceptionInfo->ContextRecord->
#if defined(_X86_)
        Eip
#elif defined (_AMD64_)
        Rip 
#else
#error not implemented
#endif
             = (ULONG_PTR)hookRtlQueryElevationFlags;

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

ULONG exec(PCWSTR lpApplicationName)
{
    ULONG dwError = NOERROR;

    if (pvRtlQueryElevationFlags = GetProcAddress(GetModuleHandle(L"ntdll"), "RtlQueryElevationFlags"))
    {
        if (PVOID pv = AddVectoredExceptionHandler(TRUE, OnVex))
        {
            ::CONTEXT ctx = {};
            ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
            ctx.Dr7 = 0x404;
            ctx.Dr1 = (ULONG_PTR)pvRtlQueryElevationFlags;

            if (SetThreadContext(GetCurrentThread(), &ctx))
            {
                STARTUPINFO si = {sizeof(si)};
                PROCESS_INFORMATION pi;
                if (CreateProcessW(lpApplicationName, 0, 0, 0, 0, 0, 0, 0, &si,&pi))
                {
                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                }
                else
                {
                    dwError = GetLastError();
                }

                ctx.Dr7 = 0x400;
                ctx.Dr1 = 0;
                SetThreadContext(GetCurrentThread(), &ctx);
            }
            else
            {
                dwError = GetLastError();
            }
            RemoveVectoredExceptionHandler(pv);
        }
        else
        {
            dwError = GetLastError();
        }
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}
...