如何停止Windows 10 Install修改BIOS启动设置?

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

它工作得很好,除了在Windows安装结束时,BIOS已经被修改为指向新的Windows启动pipe理器作为主启动设备。 所以如果不进入bios和更改设置就不能再次成像。

我明白为什么启动顺序会因为wimboot / MDT进程涉及多次重启而发生改变。 但是我真的希望保持PXE作为主引导,或者操作引导顺序,以便在完成时首先拥有networking。 (我的PXE服务器将传递networking启动机会以允许安装工作,或者在不需要成像时将系统保持独立。)

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

  1. 找出Windows安装程序如何告诉UEFI从目标安装磁盘引导,并在Windows安装完成后设置为PXE引导时执行相同的操作。
  2. 使用Windows启动pipe理器和BCDEdit安装Windows之后,将PXE启动选项从本地磁盘启动(在超级用户中发现的问题与此处基本相同,最终结果并不完全是我真正想要的PXE首先在UEFI设置中),但可能会产生相同的行为(PXE引导始终有机会在Windows启动之前采取行动)。

了解以下内容:

  1. 在Linux上,这将是相当简单的,通过efibootmgr
  2. EasyUEFI会让我做我想做的事 – 命令行支持需要相当便宜的许可证; 但我不喜欢这样的利基工具,特别是如果有其他select。
  3. 在UEFI机器上使用bcdedit修改UEFI设置 。 我认为这会奏效。
  4. 启动顺序的UEFI规范不是太复杂。 这个API实际上就是GetVariable / SetVariable,其variables名为BootOrder(按照试用顺序获取/设置启动选项列表)和Boot ####(获取/设置每个启动选项的信息)。
  5. 我不知道如何写一个窗口应用程序对Windows上的UEFI API(任何人?)
  6. Windows提供了一个API ,其中包括UEFI的GetVariable / SetVariable。

一旦理解了引导顺序和Windows API的UEFI规范,代码(C ++,为我们正在使用的64位而构build)并不算太坏。 这需要build立一个需要pipe理权限,并静态链接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; } 

然后,您可以遍历每个启动选项,为其创buildBoot ####variables名称,然后使用它获取有关该选项的信息的结构。 您需要查看第一个活动选项是否具有与“Windows启动pipe理器”相同的“说明”。 描述是结构体中偏移量为6的以空字符结尾的宽string。

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

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

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