看此贴有感而发。
曾经在 Legacy BIOS 时代,你可以很容易地从 Windows Boot Manager 切换到其他引导器(比如 ntldr,比如 GRUB ),因为有现实的兼容需求存在。只需要添加一个实模式启动项即可。
然而在 UEFI 时代,Windows Boot Manager 并没有提供这个功能。你可能尝试过将 grub.efi
直接添加为 Windows Boot Manager 的启动项目,但是发现这样并没有什么卵用 —— 尽管 winload.efi
, bootmgr.efi
之类的程序都是以 .efi 为扩展名。关了 Secure Boot 也是如此。
因为实际上 Windows 那些玩意并不是标准的 EFI 程序!观察 MSVC Linker 的选项,你就会注意到一些神奇的东西:
今天的主角就是 BOOT_APPLICATION
。上述提及的 winload.efi
之类的程序就是这个类型的。它和 EFI_APPLICATION
有着很大的区别。
这是一个常见的 EFI 程序的入口点:
EFI_STATUS
EFIAPI
InitializeUserInterface(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
而这是一个 Boot Application 的入口点:
// Boot Manager Application Entrypoint
// Bootstraps environment and transfer control to EFI Application entry point.
NTSTATUS BlApplicationEntry(
_In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootAppParameters,
_In_ PBL_LIBRARY_PARAMETERS LibraryParameters
)
入口点都不一样,怎么谈恋爱,当然加载不起来(。
Windows Boot Manager 是一个 UEFI Application。从固件进入后,它会创建一套自己的环境(与固件相独立),配置自己的 MMU 映射和自己的异常向量,为加载 Windows Kernel 作准备。所以 前人的尝试 只能运行 EFI 的一部分功能,而且好像只在 VMware 的固件上工作。对此感兴趣的可以看一下 bootmgfw.efi 的 BlInitializeLibrary
函数的内容。
创建完自己的环境后,它会把相关信息放到一个结构体里供其他 Boot Application 作参考,这玩意大概长这样:
typedef struct _BOOT_APPLICATION_PARAMETER_BLOCK
{
/* This header tells the library what image we're dealing with */
unsigned long Signature[2];
unsigned long Version;
unsigned long Size;
unsigned long ImageType;
unsigned long MemoryTranslationType;
/* Where is the image located */
unsigned __int64 ImageBase;
unsigned long ImageSize;
/* Offset to BL_MEMORY_DATA */
unsigned long MemoryDataOffset;
/* Offset to BL_APPLICATION_ENTRY */
unsigned long AppEntryOffset;
/* Offset to BL_DEVICE_DESCRPIPTOR */
unsigned long BootDeviceOffset;
/* Offset to BL_FIRMWARE_PARAMETERS */
unsigned long FirmwareParametersOffset;
/* Offset to BL_RETURN_ARGUMENTS */
unsigned long ReturnArgumentsOffset;
} BOOT_APPLICATION_PARAMETER_BLOCK, *PBOOT_APPLICATION_PARAMETER_BLOCK;
里面有原始固件环境信息结构体的指针(随体系结构而变)。最近相关内容好像进公开的 Windows SDK/DDK 里了,感兴趣的可以找一下。也可以看一下这个很刺激的演讲,Alex Ionescu 写的。
以下内容为在 ARMv7 上的情况。其他平台请自己分析,不过大同小异。直接贴代码。
// 先去拿一个固件描述符。之后你要把这玩意传进下面的函数里
FirmwareDescriptor = (PBL_FIRMWARE_DESCRIPTOR) (ParamPointer + BootAppParameters->FirmwareParametersOffset);
...
// Switch to "real" mode and never look back.
// And we do not need boot application context in this application.
void SwitchToRealModeContext(PBL_FIRMWARE_DESCRIPTOR FirmwareDescriptor)
{
// 切换到 Firmware Mode (代码里称之为 Real Mode, anyway ARM 上没有 Real Mode)
// 从固件环境描述符里获得目前的中断状态
unsigned int InterruptState = FirmwareDescriptor->InterruptState;
// 关掉中断
DisableInterrupt();
// 切换 MMU 状态,以下请参考 ARMv7 手册
unsigned long Value = FirmwareDescriptor->MmState.HardwarePageDirectory |
FirmwareDescriptor->MmState.TTB_Config;
ArmMoveToProcessor(Value, CP15_TTBR0);
ArmInstructionSynchronizationBarrier();
ArmMoveToProcessor(0, CP15_TLBIALL);
ArmInvalidateBTAC();
ArmDataSynchronizationBarrier();
ArmInstructionSynchronizationBarrier();
// 切换好了,下面切换异常状态。参考 ARMv7 手册
ArmMoveToProcessor(FirmwareDescriptor->ExceptionState.IdSvcRW, CP15_TPIDRPRW);
ArmDataSynchronizationBarrier();
ArmMoveToProcessor(FirmwareDescriptor->ExceptionState.Control, CP15_SCTLR);
ArmInvalidateBTAC();
ArmDataSynchronizationBarrier();
ArmInstructionSynchronizationBarrier();
ArmMoveToProcessor(FirmwareDescriptor->ExceptionState.Vbar, CP15_VBAR);
ArmInstructionSynchronizationBarrier();
// 如果固件描述符之前报告开了中断,那么重新开中断
if (InterruptState) ArmEnableInterrupt();
}
在完成这些步骤后你就可以把相关信息传送到标准的 EFI 程序入口点了:
SomeRandomEfiApplicationEntry(
FirmwareDescriptor->ImageHandle,
FirmwareDescriptor->SystemTable
);
然后就可以做任何事情了。抛砖引玉,你可以自己分析一下在 x86 和 amd64 上这个事情上怎么实现的。
前者是 EFI Application,后者是 Boot Application。但是前者除了会设置环境外,和后者没有什么本质上的区别。
Windows Phone 上的其他 ELF chainload (包括跑一些喜闻乐见的东西)就是用这个东西做的。因为那个漏洞的本质是 Windows Boot Manager 感觉没有打开 Secure Boot,但是固件依然会验证从固件本身加载的程序,所以需要一个 chainloader 来取得控制权。代码可以来这儿看。在 Windows Phone 上需要为这个启动项打开 NoIntegrityChecks 开关。
程序退出时不需要切换回 Boot Manager 状态,bootmgfw 会负责善后。退出后重新回到启动选项界面。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.