Запуск процесса на экране приветствия Windows 7

Итак, вот совок:

Некоторое время назад я написал крошечное приложение на C#, которое отображает имя хоста, IP-адрес, отображаемую дату, статус разморозки (мы используем DeepFreeze), текущий домен и текущую дату/время для отображения на экране приветствия наших лабораторных компьютеров с Windows 7. . Это должно было заменить наш предыдущий информационный блок, который был установлен статически при запуске и фактически встраивал текст в фон, на что-то немного более динамичное и функциональное. Приложение использует таймер для обновления IP-адреса, состояния глубокой заморозки и часов каждую секунду, а также проверяет, вошел ли пользователь в систему, и убивает себя, когда обнаруживает такое состояние.

Если мы просто запустим его через наш сценарий запуска (установленный с помощью групповой политики), он будет держать сценарий открытым, и машина никогда не дойдет до приглашения для входа в систему. Если мы используем что-то вроде команд start или cmd, чтобы запустить его в отдельной оболочке/процессе, он будет работать до тех пор, пока сценарий запуска не завершится, и в этот момент Windows, похоже, очистит все дочерние процессы сценария. В настоящее время мы можем обойти это, используя psexec -s -d -i -x для его запуска, что позволяет ему сохраняться после завершения сценария запуска, но может быть невероятно медленным, добавляя от 5 секунд до более минуты к нашему времени запуска.

Мы экспериментировали с использованием другого приложения C# для запуска процесса через класс Process, используя вызовы WMI (Win32_Process и Win32_ProcessStartup) с различными флагами запуска и т. д., но все они заканчиваются одним и тем же результатом завершения сценария и получения процесса информационного блока. убит. Я возился с переписыванием приложения как службы, но службы никогда не были предназначены для взаимодействия с рабочим столом, не говоря уже об окне входа в систему, и казалось, что заставить вещи работать в правильном контексте никогда не получалось.

Итак, на вопрос: есть ли у кого-нибудь хороший способ сделать это? Запустить задачу, чтобы она не зависела от сценария запуска и запускалась поверх экрана приветствия?


person peelman    schedule 18.06.2010    source источник


Ответы (4)


Это можно сделать с помощью множества вызовов Win32 API. Мне удалось загрузить программу с графическим интерфейсом на рабочий стол Winlogon (прежде чем кто-либо спросит, это не интерактивный графический интерфейс). В основном вам нужно запустить процесс загрузчика как SYSTEM, который затем создаст новый процесс. Поскольку вы, скорее всего, хотите, чтобы этот процесс запускался при запуске, вы можете либо использовать планировщик задач для запуска загрузчика как SYSTEM, либо вы можете использовать службу, чтобы сделать то же самое. В настоящее время я использую службу, но я попытался использовать планировщик задач, и он работал нормально.

Краткое содержание:

  1. Захватите процесс Winlogon.exe (как процесс)
  2. Возьмите токен winlogon, используя OpenProcessToken, используя .handle процесса
  3. Создайте новый токен и скопируйте на него токен winlogon
  4. Повысить привилегии токена
  5. Создайте процесс с помощью CreateProcessAsUser, задав для lpDesktop значение «Winsta0\Winlogon» и используя созданный токен.

Пример кода:

        // grab the winlogon process
        Process winLogon = null;
        foreach (Process p in Process.GetProcesses()) {
            if (p.ProcessName.Contains("winlogon")) {
                winLogon = p;
                break;
            }
        }
        // grab the winlogon's token
        IntPtr userToken = IntPtr.Zero;
        if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken)) {
            log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
        }

        // create a new token
        IntPtr newToken = IntPtr.Zero;
        SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
        tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
        SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
        threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
        // duplicate the winlogon token to the new token
        if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
            TOKEN_TYPE.TokenImpersonation, out newToken)) {
            log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
        }
        TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
        tokPrivs.PrivilegeCount = 1;
        LUID seDebugNameValue = new LUID();
        if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue)) {
            log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
        }
        tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
        tokPrivs.Privileges[0].Luid = seDebugNameValue;
        tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        // escalate the new token's privileges
        if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero)) {
            log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
        }
        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
        STARTUPINFO si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        si.lpDesktop = "Winsta0\\Winlogon";
        // start the process using the new token
        if (!CreateProcessAsUser(newToken, process, process, ref tokenAttributes, ref threadAttributes,
            true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,
            logInfoDir, ref si, out pi)) {
            log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
        }

        Process _p = Process.GetProcessById(pi.dwProcessId);
        if (_p != null) {
            log("Process " + _p.Id + " Name " + _p.ProcessName);
        } else {
            log("Process not found");
        }
person Jon    schedule 29.06.2010
comment
@Fluxer Процесс входа в систему Windows XP сильно отличается от процесса входа в Vista и выше. XP использовала GINA, но, начиная с Vista и выше, Microsoft переписала раздел входа в систему, чтобы использовать CredentialProviders. Надеюсь это поможет. - person Brett Rigby; 16.10.2012
comment
Я получаю ошибку 1314 при попытке запустить CreateProcessAsUser. Какие-либо предложения? string lpAppName = @D:\SMU\Ubiquitous Computing 7390\Final Project\Locker\Locker\bin\Debug\Locker.exe; if (!CreateProcessAsUser(newToken, lpAppName, null, ref tokenAttributes, ref threadAttributes, true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero, null, ref si, out pi)) - person Jake Drew; 02.12.2014

Это один из тех вопросов типа "Вам действительно нужна веская причина для этого". Microsoft очень старается блокировать приложения, работающие на экране запуска — каждый бит кода в Windows, который взаимодействует с экраном входа в систему, очень тщательно проверяется, потому что последствия для безопасности ошибки в коде, работающем на экране входа, ужасны — если вы облажаетесь даже немного, вы позволите вредоносным программам попасть на компьютер.

Почему вы хотите запускать свою программу на экране входа в систему? Может быть, есть задокументированный способ сделать это не так рискованно.

person ReinstateMonica Larry Osterman    schedule 19.06.2010
comment
Я понимаю паранойю Microsoft, но это одна из тех вещей, которые заставляют меня ненавидеть Windows еще больше. Нам нужна информация на экране по разным причинам, большинство из которых являются нашими собственными. Рассматриваемая программа не принимает пользовательский ввод, а просто отображает информацию. Вероятность того, что 14-тысячное приложение C# представляет угрозу безопасности, для нас не проблема, особенно когда у нас есть 50 000 потенциальных пользователей на машину. - person peelman; 19.06.2010
comment
Может ли информация, которую вы хотите отобразить, поместиться в окне сообщения? В Windows есть механизм отображения текстовой строки в окне сообщения во время входа в систему, который может делать то, что вы хотите. - person ReinstateMonica Larry Osterman; 19.06.2010
comment
Возвращаясь к моему исходному сообщению, это машинные данные для персонала службы поддержки и эксплуатации, поэтому им не нужно входить в систему, чтобы просмотреть их. Важно, чтобы это была наглядная, всегда доступная информация. OSX предоставляет аналогичную информацию в окне входа в систему, что избавляет нас от необходимости делать там что-то столь же сложное. - person peelman; 19.06.2010
comment
Какая информация им нужна, которую нельзя получить удаленно через WMI? Windows содержит ошеломляющий объем управленческой информации, которую можно прочитать с помощью API-интерфейсов WMI, предназначенных для персонала службы поддержки и эксплуатации. Есть ли нужная им информация, доступная только на физической консоли? - person ReinstateMonica Larry Osterman; 20.06.2010
comment
Знаешь, если ты не поможешь мне решить проблему, о которой я спрашивал, я точно так же не буду спорить о создании новых проблем. - person peelman; 20.06.2010
comment
Со своего места я пытаюсь помочь вам решить проблему. Не существует надежного способа сделать то, что вы хотите, без создания потенциальных угроз безопасности системы, поэтому я пытаюсь придумать альтернативный механизм, который позволит вам получить необходимую информацию, не взламывая функции безопасности системы. Примерно раз в 3 месяца кто-то из MSFT задает аналогичный вопрос о нашем внутреннем псевдониме Windows Tricks, и ответ всегда такой же, как тот, который я даю — найдите другой способ делать то, что вы хотите, потому что это безопаснее для все наши клиенты. - person ReinstateMonica Larry Osterman; 21.06.2010
comment
И прежде чем вы ответите, если вы продолжаете получать такого рода запросы, разве это не показывает вам, что вам нужно сделать это безопасным?, нет никакого способа исправить эту проблему - код, работающий на рабочем столе входа в систему, уязвим для взлома (en.wikipedia.org/wiki/Shatter_attack), если вы тщательно не убедитесь, что логика вашего пользовательского интерфейса безопасна. из них (что означает длительные, сложные проверки кода каждого бита кода пользовательского интерфейса, включая кодовую базу WPF, которую вы используете), нет никакого способа гарантировать, что ваше приложение будет полностью безопасным. Всегда лучше найти другой путь. - person ReinstateMonica Larry Osterman; 21.06.2010
comment
Ну, опять же, я ценю беспокойство, но мы уже делаем это через psexec, так что не будет ли довольно очевидно, что мы оба знаем и можем меньше заботиться о проблемах безопасности? А какая часть из 50 000 пользователей была непонятна? Это не рабочие станции АНБ, это компьютерные лаборатории в очень большом кампусе колледжа. Ваш трюк с окном сообщений, о котором я знаю, требует, чтобы каждый учащийся входил в систему, чтобы подтвердить ящик, что неприемлемо. Наши операторы и вспомогательный персонал входят в комнаты с 20, 30 или даже 100 машинами, и для нас важно иметь эту информацию на экране входа в систему. - person peelman; 21.06.2010
comment
Я очень хорошо осведомлен о возможностях wmi и его удаленной мощности, но это не приносит нам пользы, когда мы пытаемся проверить или исправить проблемы с физической станцией. Если бы MS была способна на некоторую предусмотрительность, они могли бы встроить НЕКОТОРЫЕ возможности расширения в authui.dll, либо позволив ей читать XML-файл конфигурации, аналогичный внутренним для нее, либо, что еще хуже, с помощью ключей реестра. Ни одно из решений не было бы идеальным для нашего состояния глубокой заморозки, IP-адреса или часов, поскольку всю эту информацию необходимо регулярно обновлять. - person peelman; 21.06.2010
comment
Вот риск, которому вы подвергаетесь: вы запускаете свой пользовательский интерфейс на рабочем столе, и какой-то умный студент выясняет, как использовать ваш пользовательский интерфейс. Теперь они завладели вашим рабочим столом. Они повторяют для каждой машины в лаборатории, теперь они владеют вашей лабораторией. Теперь какой-то администратор домена входит в машину в лаборатории. Теперь ваш умный ученик владеет вашим доменом. И как только умный студент получит ваш домен, игра будет окончена. Для этого есть решения: например, я знаю университеты (и компании), которые каждую ночь восстанавливают образ каждого лабораторного компьютера. Но эти средства, как правило, довольно драконовские. - person ReinstateMonica Larry Osterman; 21.06.2010
comment
Просто наличие компьютеров с Windows в кампусе — это риск, как вы его определяете. В системе есть гораздо большие дыры, которые они могут использовать, особенно если они, по-видимому, могут перейти от процесса, работающего как локальный пользователь, к чему-то с привилегиями домена. Я хочу сказать, что это наши машины, это наша задача. Мы каждый день идем на компромиссы в вопросах безопасности, чтобы наши машины работали как для нас, так и для наших конечных пользователей. - person peelman; 21.06.2010
comment
И просто чтобы нарисовать вам еще более мрачную картину, пользователи садятся за наши машины, взламывают корпус, сбрасывают BIOS, который очищает пароль, затем они могут загружаться с чего угодно, модифицировать наши диски, как они считают нужным, и позволяют машины загружаются, минуя всю нашу безопасность. Они устанавливают кейлоггер и начинают собирать пароли. Наши кошмарные сценарии не вращаются вокруг скучающего студента CS, пытающегося использовать какое-то глупое маленькое прозрачное окно на экране входа в систему, состоящее только из ярлыков. - person peelman; 21.06.2010
comment
Когда пользователи имеют физический доступ к машинам, ваша безопасность уже скомпрометирована. Это приложение не представляет для нас значительной сетевой угрозы, и это все, что нас действительно волнует. Если то, что вы говорите, на самом деле правда, то тот факт, что нет способа запустить такой процесс безопасным образом, действительно говорит о улучшениях в безопасности Windows. Вместо того, чтобы сделать его лучше, давайте просто ограничим то, что люди могут делать. Вот идея сделать машины еще более безопасными: мы просто отключим их от сети и уберем клавиатуры и мыши. - person peelman; 21.06.2010

Я перевел приведенный выше код на C++, если он кому-то еще нужен... Обратите внимание, что есть ссылки на части моего кода, но в любом случае это может помочь:

static bool StartProcess(LPCTSTR lpApplicationPath)
{
    CAutoGeneralHandle hWinlogonProcess = FindWinlogonProcess();
    if (hWinlogonProcess == INVALID_HANDLE_VALUE) 
    {
        DU_OutputDebugStringff(L"ERROR: Can't find the 'winlogon' process");
        return false;
    }

    CAutoGeneralHandle hUserToken;
    if (!OpenProcessToken(hWinlogonProcess, TOKEN_QUERY|TOKEN_IMPERSONATE|TOKEN_DUPLICATE, &hUserToken)) 
    {
        DU_OutputDebugStringff(L"ERROR: OpenProcessToken returned false (error %u)", GetLastError());
        return false;
    }

    // Create a new token
    SECURITY_ATTRIBUTES tokenAttributes = {0};
    tokenAttributes.nLength = sizeof tokenAttributes;

    SECURITY_ATTRIBUTES threadAttributes = {0};
    threadAttributes.nLength = sizeof threadAttributes;

    // Duplicate the winlogon token to the new token
    CAutoGeneralHandle hNewToken;
    if (!DuplicateTokenEx(hUserToken, 0x10000000, &tokenAttributes, 
            SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation,
            TOKEN_TYPE::TokenImpersonation, &hNewToken)) 
    {
        DU_OutputDebugStringff(L"ERROR: DuplicateTokenEx returned false (error %u)", GetLastError());
        return false;
    }

    TOKEN_PRIVILEGES tokPrivs = {0};
    tokPrivs.PrivilegeCount = 1;

    LUID seDebugNameValue = {0};
    if (!LookupPrivilegeValue(nullptr, SE_DEBUG_NAME, &seDebugNameValue)) 
    {
        DU_OutputDebugStringff(L"ERROR: LookupPrivilegeValue returned false (error %u)", GetLastError());
        return false;
    }

    tokPrivs.Privileges[0].Luid = seDebugNameValue;
    tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // Escalate the new token's privileges
    if (!AdjustTokenPrivileges(hNewToken, false, &tokPrivs, 0, nullptr, nullptr))
    {
        DU_OutputDebugStringff(L"ERROR: AdjustTokenPrivileges returned false (error %u)", GetLastError());
        return false;
    }

    PROCESS_INFORMATION pi = {0};
    STARTUPINFO si = {0};
    si.cb = sizeof si;
    si.lpDesktop = L"Winsta0\\Winlogon";

    // Start the process using the new token
    if (!CreateProcessAsUser(hNewToken, lpApplicationPath, nullptr, &tokenAttributes, &threadAttributes,
        true, CREATE_NEW_CONSOLE|INHERIT_CALLER_PRIORITY, nullptr, nullptr, &si, &pi)) 
    {
        DU_OutputDebugStringff(L"ERROR: CreateProcessAsUser returned false (error %u)", GetLastError());
        return false;
    }

    return true;
}
person Simon    schedule 23.07.2014

Я думаю, вы можете это сделать, но это довольно сложно. Интерактивные приложения обычно не могут запускаться на экране приветствия. На высоком уровне вам потребуется:

  • Создайте службу Windows, которая запускается автоматически
  • Используйте службу Windows для создания другого процесса в текущем сеансе и на рабочем столе (используя методы Win32 WTSGetActiveConsoleSessionId и OpenInputDesktop)

Я написал приложение, которое может в некоторой степени взаимодействовать с экраном входа в систему, но оно не отображает никакого пользовательского интерфейса. Это, вероятно, можно сделать, но это может быть даже более сложным.

Примечание. Я обнаружил, что не могу получить результаты OpenInputDesktop из службы Windows. Вместо этого мне пришлось сделать вызов в другом процессе и уведомить службу о перезапуске процесса на правильном рабочем столе.

Надеюсь, это поможет вам хотя бы начать. Удачи!

person dcstraw    schedule 18.06.2010
comment
То, что PSexec может это сделать, говорит мне, что это возможно, я думаю, это просто вопрос имитации того, как он его запускает. Я рассмотрю то, что вы предложили, и посмотрю, куда это меня приведет. - person peelman; 18.06.2010