Запуск процесса на экране приветствия Windows 7 - PullRequest
9 голосов
/ 18 июня 2010

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

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

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

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

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

Ответы [ 4 ]

11 голосов
/ 29 июня 2010

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

Краткое резюме:

  1. Захватить процесс Winlogon.exe (как процесс)
  2. Получите токен winlogon, используя OpenProcessToken, используя .handle of Process
  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");
        }
6 голосов
/ 24 июля 2014

Я перевел приведенный выше код на 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;
}
6 голосов
/ 19 июня 2010

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

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

1 голос
/ 18 июня 2010

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

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

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

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

Я надеюсь, что, по крайней мере, вы можете начать. Удачи!

...