Como faço para impedir que o Windows 10 Install modifique as configurações de inicialização do BIOS?

3

Estamos configurando alguns sistemas para o PXEboot via iPXE e, dependendo do estado do servidor mestre, inicializamos normalmente ou fazemos a recriação de imagens via wimboot e MDT. Os sistemas são configurados para inicializar a partir da rede primeiro. O iPXE e o wimboot estão sendo executados no UEFI.

Funciona muito bem, exceto que no final da instalação do Windows, o BIOS foi modificado para apontar para o novo Gerenciador de Inicialização do Windows como o dispositivo de inicialização principal. Por isso, não pode ser fotografado novamente sem entrar no BIOS e alterar as configurações.

Eu entendo porque a ordem de inicialização é alterada, pois o processo wimboot / MDT envolve várias reinicializações. Mas eu realmente gostaria de manter o PXE como a inicialização principal ou manipular a ordem de inicialização de volta para ter a primeira rede quando terminar. (Meu servidor PXE transmitirá a oportunidade de inicialização de rede para permitir que a instalação funcione ou deixe o sistema sozinho quando nenhuma imagem for necessária.)

Atualizar - vejo duas possibilidades:

  1. Descubra como o instalador do Windows informa ao UEFI para inicializar a partir do disco de instalação de destino e faça o mesmo quando a instalação do Windows for feita para retornar à inicialização do PXE.
  2. Jogue com o Gerenciador de Inicialização do Windows e o BCDEdit após a instalação do Windows para colocar a opção de inicialização PXE acima da inicialização do disco local (pergunta encontrada em superusuário é basicamente a mesma questão que aqui. O resultado final discutido não é exatamente o que eu realmente quero (PXE primeiro nas configurações UEFI ), mas pode produzir o mesmo comportamento (inicialização PXE sempre tendo a chance de agir antes do Windows iniciar).
por aggieNick02 08.11.2016 / 00:47

1 resposta

3

Aprendi o seguinte:

  1. No Linux, isso seria bastante simples, via efibootmgr
  2. O EasyUEFI me permite fazer o que eu quero também - o suporte de linha de comando requer uma licença relativamente barata; mas não me sinto bem dependendo de uma ferramenta de nicho como essa, especialmente se houver outras opções.
  3. bcdedit em uma máquina UEFI modifica as configurações de UEFI . Eu acho que funcionaria.
  4. A especificação da UEFI para a ordem de inicialização não é muito complicada. A API é realmente apenas GetVariable / SetVariable com variáveis denominadas BootOrder (para obter / definir a lista de opções de inicialização na ordem em que serão tentadas) e Boot #### (para obter / definir informações sobre cada opção de inicialização). / li>
  5. Eu não tenho ideia de como eu escreveria um aplicativo do Windows contra a API da UEFI no Windows (alguém?)
  6. O Windows fornece uma API que, entre outras coisas , envolve o GetVariable / SetVariable do UEFI.

Depois que entendi a especificação UEFI para a ordem de inicialização e a API do Windows, o código (C ++, construído para 64 bits, já que é tudo o que estamos usando) não foi tão ruim. Isso precisa ser construído em um exe que requer privilégios administrativos e vincula estaticamente o tempo de execução do Windows e, em seguida, eu o executo no MDT após o sistema operacional ser instalado antes da reinicialização.

Primeiro, você precisa reivindicar um privilégio para chamar a API. Use um pequeno ajudante:

struct CloseHandleHelper
{
    void operator()(void *p) const
    {
        CloseHandle(p);
    }
};

BOOL SetPrivilege(HANDLE process, LPCWSTR name, BOOL on)
{
    HANDLE token;
    if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &token))
        return FALSE;
    std::unique_ptr<void, CloseHandleHelper> tokenLifetime(token);
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    if (!LookupPrivilegeValueW(NULL, name, &tp.Privileges[0].Luid))
        return FALSE;
    tp.Privileges[0].Attributes = on ? SE_PRIVILEGE_ENABLED : 0;
    return AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL);
}

ligue para

SetPrivilege(GetCurrentProcess(), SE_SYSTEM_ENVIRONMENT_NAME, TRUE));

Em seguida, obtenha a lista de opções de inicialização (uma concatenação de valores uint16_t):

const int BUFFER_SIZE = 4096;
BYTE bootOrderBuffer[BUFFER_SIZE];
DWORD bootOrderLength = 0;
const TCHAR bootOrderName[] = TEXT("BootOrder");
const TCHAR globalGuid[] = TEXT("{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}");
DWORD bootOrderAttributes;
bootOrderLength = GetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, BUFFER_SIZE, &bootOrderAttributes);
if (bootOrderLength == 0)
{
    std::cout << "Failed getting BootOrder with error " << GetLastError() << std::endl;
    return 1;
}

Você pode, então, iterar sobre cada opção de inicialização, formar o nome da variável Boot #### para ela e usá-la para obter uma estrutura com informações sobre a opção. Você vai querer ver se a primeira opção ativa tem "Descrição" igual a "Windows Boot Manager". Descrição é uma cadeia de caracteres larga terminada por caractere nulo no deslocamento 6 na estrutura.

for (DWORD i = 0; i < bootOrderLength; i += 2)
{
    std::wstringstream bootOptionNameBuilder;
    bootOptionNameBuilder << "Boot" << std::uppercase << std::setfill(L'0') << std::setw(4) << std::hex << *reinterpret_cast<uint16_t*>(bootOrderBuffer + i);
    std::wstring bootOptionName(bootOptionNameBuilder.str());
    BYTE bootOptionInfoBuffer[BUFFER_SIZE];
    DWORD bootOptionInfoLength = GetFirmwareEnvironmentVariableEx(bootOptionName.c_str(), globalGuid, bootOptionInfoBuffer, BUFFER_SIZE, nullptr);
    if (bootOptionInfoLength == 0)
    {
        std::cout << "Failed getting option info for option at offset " << i << std::endl;
        return 1;
    }
    uint32_t* bootOptionInfoAttributes = reinterpret_cast<uint32_t*>(bootOptionInfoBuffer);
    //First 4 bytes make a uint32_t comprised of flags. 0x1 means the boot option is active (not disabled)
    if (((*bootOptionInfoAttributes) & 0x1) != 0)
    {
        std::wstring description(reinterpret_cast<wchar_t*>(bootOptionInfoBuffer + sizeof(uint32_t) + sizeof(uint16_t)));
        bool isWBM = boost::algorithm::to_upper_copy<std::wstring>(description) == L"WINDOWS BOOT MANAGER";
        // details - keep track of the value of i for the first WBM and non-WBM options you find, and the fact that you found them
    }
}

Agora, se você encontrou opções de inicialização WBM e não-WBM ativas e a primeira opção WBM está em wbmOffset, e a primeira opção não-WBM está em nonWBMOffset, com wbmOffset < nonWBMOffset, troque as entradas na variável BootOrder com o seguinte:

    uint16_t *wbmBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + wbmOffset);
    uint16_t *nonWBMBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + nonWBMOffset);
    std::swap(*wbmBootOrderEntry, *nonWBMBootOrderEntry);
    if (SetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, bootOrderLength, bootOrderAttributes))
    {
        std::cout << "Swapped WBM boot entry at offset " << wbmOffset << " with non-WBM boot entry at offset " << nonWBMOffset << std::endl;
    }
    else
    {
        std::cout << "Failed to swap WBM boot entry with non-WBM boot entry, error " << GetLastError() << std::endl;
        return 1;
    }
    
por 09.11.2016 / 23:23