AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 813695
Accepted
aggieNick02
aggieNick02
Asked: 2016-11-08 15:47:08 +0800 CST2016-11-08 15:47:08 +0800 CST 2016-11-08 15:47:08 +0800 CST

如何阻止 Windows 10 安装修改 BIOS 启动设置?

  • 772

我们正在通过 iPXE 将一些系统设置为 PXEboot,并且根据主服务器状态,正常启动或通过 wimboot 和 MDT 重新映像。系统配置为首先从网络引导。iPXE 和 wimboot 都在 UEFI 下运行。

它工作得很好,除了在 Windows 安装结束时,BIOS 已被修改为指向新的 Windows 引导管理器作为主引导设备。因此,如果不进入 bios 和更改设置,它就无法再次成像。

我理解为什么引导顺序会因为 wimboot/MDT 过程涉及多次重新启动而改变。但我真的很想将 PXE 始终作为主引导,或者在完成后将引导顺序重新设置为网络优先。(我的 PXE 服务器将传递网络引导机会,以允许安装工作或在不需要映像时让系统独立运行。)

更新 -我看到两种可能性:

  1. 弄清楚 windows 安装程序如何告诉 UEFI 从目标安装磁盘启动,并在 windows 安装完成后执行相同的操作以设置回 PXE 启动。
  2. 安装 Windows 后使用 Windows 引导管理器和 BCDEdit 将 PXE 引导选项置于从本地磁盘引导之上(在超级用户处发现的问题与此处基本相同。讨论的最终结果并不是我真正想要的( PXE 首先在 UEFI 设置中)但可能会产生相同的行为(PXE 启动总是有机会在 Windows 启动之前采取行动)。
deployment pxe-boot mdt bcdedit wim
  • 2 2 个回答
  • 6841 Views

2 个回答

  • Voted
  1. Best Answer
    aggieNick02
    2016-11-10T14:23:12+08:002016-11-10T14:23:12+08:00

    学到了以下内容:

    1. 在 Linux 上,这将相当简单,通过efibootmgr
    2. EasyUEFI 也可以让我做我想做的事——命令行支持需要相当便宜的许可证;但我感觉不太好依赖像它这样的利基工具,特别是如果有其他选择的话。
    3. UEFI 机器上的 bcdedit 修改 UEFI 设置。我认为它会起作用。
    4. 引导顺序的 UEFI 规范并不太复杂。API 实际上只是 GetVariable/SetVariable 与名为 BootOrder 的变量(按照它们将被尝试的顺序获取/设置引导选项列表)和 Boot####(获取/设置有关每个引导选项的信息)。
    5. 我不知道如何针对 windows 上的 UEFI API 编写 windows 应用程序(任何人?)
    6. Windows 提供了一个 API,其中包含 UEFI 的 GetVariable/SetVariable。

    一旦我了解了引导顺序和 Windows API 的 UEFI 规范,代码(C++,为 64 位构建,因为这就是我们所使用的全部)并不算太糟糕。这需要内置到一个需要管理权限并静态链接windows运行时的exe中,然后我在安装操作系统后在MDT中运行它,然后再重新启动。

    首先,您必须声明调用 API 的特权。使用一个小帮手:

    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);
    }
    

    然后打电话

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

    接下来,获取引导选项列表(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;
    }
    

    然后,您可以遍历每个引导选项,为其形成 Boot#### 变量名称,然后使用它来获取包含有关该选项的信息的结构。您需要查看第一个活动选项的“描述”是否等于“Windows 启动管理器”。描述是结构中偏移量 6 处的以空字符结尾的宽字符串。

    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
        }
    }
    

    现在,如果您发现活动的 WBM 和非 WBM 引导选项,并且第一个 WBM 选项位于 wbmOffset,并且第一个非 WBM 选项位于 nonWBMOffset,且 wbmOffset < nonWBMOffset,则将 BootOrder 变量中的条目交换为以下内容:

        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;
        }
    
    • 3
  2. mmseng
    2019-03-29T09:07:18+08:002019-03-29T09:07:18+08:00

    我想出了这个适用于我的 powershell 脚本。它并不完美,因为它只是“愚蠢地”将第一个非 Windows 启动项移动到顶部。这适用于我的目的,并且可能有一种方法可以让它变得更聪明,而我只是没有找到。

    它看起来很长,但主要是注释和格式以便于理解。它可以重写为 5 或 6 行。

    https://github.com/mmseng/bcdedit-revert-uefi-gpt-boot-order

    # This script looks for the first non-Windows Boot Manager entry in the UEFI/GPT boot order and moves it to the top
    # For preventing newly installed Windows from hijacking the top boot order spot on my UEFI/GPT image testing VMs
    # by mmseng
    # https://github.com/mmseng/bcdedit-revert-uefi-gpt-boot-order
    
    # Notes:
    # - There's very little point in using this on regular production machines being deployed. Its main use is for machines being repeatedly imaged, or might be useful for lab machines.
    # - AFAICT bcdedit provideds no way to pull the friendly names of the devices in the overall UEFI boot order list. Therefore, this script only moves the first entry it identifies in the list which is NOT "{bootmgr}" (a.k.a. "Windows Boot Manager"). It's up to the user to make sure the boot order will exist in a state where the desired result is achieved.
    # - In my case, my test UEFI VMs initially have the boot order of 1) "EFI Network", 2) whatever else. When Windows is installed with GPT partitioning, it changes the boot order to 1) "Windows Boot Manager", 2) "EFI Network", 3) whatever else. In that state, this script can be used to change the boot order to 1) "EFI Network", 2) "Windows Boot Manager", 3) whatever else.
    # - This functionality relies on the completely undocumented feature of bcdedit to modify the "{fwbootmgr}" GPT entry, which contains the overall list of UEFI boot devices.
    # - AFAICT bcdedit is really only designed to edit Windows' own "{bootmgr}" entry which represents one of the "boot devices" in the overall UEFI list.
    # - Here are some sources:
    #   - https://www.cnet.com/forums/discussions/bugged-bcdedit-349276/
    #   - https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/bcd-system-store-settings-for-uefi
    #   - https://www.boyans.net/DownloadVisualBCD.html
    #   - https://serverfault.com/questions/813695/how-do-i-stop-windows-10-install-from-modifying-bios-boot-settings
    #   - https://serverfault.com/questions/714337/changing-uefi-boot-order-from-windows
    
    
    # Read current boot order
    echo "Reading current boot order..."
    $bcdOutput = cmd /c bcdedit /enum "{fwbootmgr}"
    echo $bcdOutput
    
    # Kill as many of the stupid characters as possible
    echo "Removing extraneous characters from boot order output..."
    $bcdOutput = $bcdOutput -replace '\s+',''
    $bcdOutput = $bcdOutput -replace '`t',''
    $bcdOutput = $bcdOutput -replace '`n',''
    $bcdOutput = $bcdOutput -replace '`r',''
    $bcdOutput = $bcdOutput.trim()
    $bcdOutput = $bcdOutput.trimEnd()
    $bcdOutput = $bcdOutput.trimStart()
    $bcdOutput = $bcdOutput -replace ' ',''
    echo $bcdOutput
    
    # Define a reliable regex to capture the UUIDs of non-Windows Boot Manager devices in the boot order list
    # This is difficult because apparently Powershell interprets regex is a fairly non-standard way (.NET regex flavor)
    # https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions
    # Even then, .NET regex testers I used didn't match the behavior of what I got out of various Powershell commands that accept regex strings
    # However this seems to work, even though I can't replicate the results in any regex testers
    $regex = [regex]'^{([\-a-z0-9]+)+}'
    echo "Defined regex as: $regex"
    
    # Save matches
    echo "Save strings matching regex..."
    $foundMatches = $bcdOutput -match $regex
    
    # Grab first match
    # If Windows Boot Manager (a.k.a. "{bootmgr}" was the first in the list, this should be the second
    # Which means it was probably the first before Windows hijacked the first spot
    # Which means it was probably my "EFI Network" boot device
    $secondBootEntry = $foundMatches[0]
    echo "First match: $secondBootEntry"
    
    # Move it to the first spot
    echo "Running this command:"
    echo "cmd /c bcdedit $bcdParams /set `"{fwbootmgr}`" displayorder $secondBootEntry /addfirst"
    cmd /c bcdedit $bcdParams /set "{fwbootmgr}" displayorder $secondBootEntry /addfirst
    
    • 2

相关问题

  • 网络监控产品公司的部署工程师需要具备哪些技能?[关闭]

  • Mac OS X 的无人值守安装

  • Firefox 打包以包含用于网络安装的插件

  • 我应该将 Rails 应用程序部署到哪个目录?

  • 将 iChat 设置部署到多个用户

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve