由于无法访问Windows Vista的源代码,我们通过debugger(调试器)、disassembler(反编译)、hex editor(16进制编辑器,例如UE)学习了Windows Vista Community Technical Preview (CTP) Build 5365版本。如果读者不熟悉X86体系架构和汇编语言的知识,推荐大家一本不错的关于X86汇编语言的在线图书[8]。另外,在看该文档前你需要熟悉一些Windows系统架构的知识,推荐大家阅读[9],该书讲解了Windows系统架构的知识,同时这本书的一些知识同样可以应用到Vista系统上。在该文档公布时Vista系统还在测试阶段,但Vista在后来版本的安全性方面又做了一些改动。我们希望Vista在最终的发布版本上有更多的改变,同时我们也计划继续对未来的Vista版本的内核改动进行研究。
如果启动列表的某个条目被选择,跟随BmpTransferExecution之后,将使用BmpLaunchBootEntry对该条目进行加载。BmpTransferExecution将重新找回启动选项(通过BlGetBootOptionString)并把他们交给BlImgLoadBootApplication。如果FVE(Full Volume Encryption)被激活,BlFveSecureBootUnlockBootDevice和 BlFveSecureBootCheckpointBootApp将被调用。由于Windows分区被加密,必须在把控制权交给Vista OS Loader前对分区进行解密。
最后,Boot Manager调用BlImgStartBootApplication把控制权交给Vista OS Loader。
Windows Vista操作系统加载
bootmgr调用了位于%SystemRoot%\System32\WINLOAD.EXE下的Vista OS Loader。WINLOAD.EXE替换了NTLDR(Windows NT OS Loader),该小节的最后部分,会引用WINLOAD.EXE在开始入口点(OslMain)的指令。
一个典型的Vista OS载入的BCD入口配置文件如下:
Windows Boot Loader Identifier: {current} Type: 10200003 Device: partition=C: SYMANTEC ADVANCED THREAT RESEARCH 3 Path: \Windows\system32\WINLOAD.EXE Description: Microsoft Windows Locale: en-US Inherit options: {bootloadersettings} Boot debugger: No Pre-boot EMS Enabled: No Advanced options: No Options editor: No Windows device: partition=C: Windows root: \Windows Resume application: {3ced334e-a0a5-11da-8c2b-cbb6baaeea6d} No Execute policy: OptIn Detect HAL: No No integrity checks: No Disable boot display: No Boot processor only: No Firmware PCI settings: No Log initialization: No OS boot information: No Kernel debugger: No HAL breakpoint: No EMS enabled in OS: No
+0x000 LoadOrderListHead : struct _LIST_ENTRY +0x010 MemoryDescriptorListHead : struct _LIST_ENTRY +0x020 BootDriverListHead : struct _LIST_ENTRY +0x030 KernelStack : Uint8B +0x038 Prcb : Uint8B +0x040 Process : Uint8B +0x048 Thread : Uint8B +0x050 RegistryLength : Uint4B +0x058 RegistryBase : Ptr64 to Void +0x060 ConfigurationRoot : Ptr64 to struct _CONFIGURATION_COMPONENT_DATA +0x068 ArcBootDeviceName : Ptr64 to Char +0x070 ArcHalDeviceName : Ptr64 to Char +0x078 NtBootPathName : Ptr64 to Char +0x080 NtHalPathName : Ptr64 to Char +0x088 LoadOptions : Ptr64 to Char +0x090 NlsData : Ptr64 to struct _NLS_DATA_BLOCK +0x098 ArcDiskInformation : Ptr64 to struct _ARC_DISK_INFORMATION +0x0a0 OemFontFile : Ptr64 to Void +0x0a8 SetupLoaderBlock : Ptr64 to struct _SETUP_LOADER_BLOCK +0x0b0 Extension : Ptr64 to struct _LOADER_PARAMETER_EXTENSION +0x000 Size : Uint4B +0x004 Profile : struct _PROFILE_PARAMETER_BLOCK +0x014 MajorVersion : Uint4B +0x018 MinorVersion : Uint4B +0x020 EmInfFileImage : Ptr64 to Void +0x028 EmInfFileSize : Uint4B +0x030 TriageDumpBlock : Ptr64 to Void +0x038 LoaderPagesSpanned : Uint4B +0x040 HeadlessLoaderBlock : Ptr64 to struct _HEADLESS_LOADER_BLOCK +0x048 SMBiosEPSHeader : Ptr64 to struct _SMBIOS_TABLE_HEADER +0x050 DrvDBImage : Ptr64 to Void +0x058 DrvDBSize : Uint4B +0x060 NetworkLoaderBlock : Ptr64 to struct _NETWORK_LOADER_BLOCK bytes +0x068 FirmwareDescriptorListHead : struct _LIST_ENTRY +0x078 AcpiTable : Ptr64 to Void +0x080 AcpiTableSize : Uint4B +0x084 BootViaWinload : Bitfield Pos 0, 1 Bit +0x084 BootViaEFI : Bitfield Pos 1, 1 Bit +0x084 Reserved : Bitfield Pos 2, 30 Bits +0x088 LoaderPerformanceData : Ptr64 to struct _LOADER_PERFORMANCE_DATA +0x090 BootApplicationPersistentData : struct _LIST_ENTRY +0x0a0 WmdTestResult : Ptr64 to Void +0x0a8 BootIdentifier : struct _GUID +0x0b8 u : union +0x000 I386 : struct _I386_LOADER_BLOCK +0x000 CommonDataArea : Ptr64 to Void +0x008 MachineType : Uint4B +0x00c VirtualBias : Uint4B
下面会寻找系统磁盘(通过OslEnumerateDisks)和加载系统注册表项HKEY_LOCAL_MACHINE(通过OslpLoadSystemHive)。当系统注册表项加载后,我们遇到了Vista在启动阶段的第一次代码完整性(通过OslInitializeCodeIntegrity)。在该处首先会调用MincrypL_SelfTest,MincrypL_SelfTest验证SHA1散列值,并使PKCS1的签名验证开始工作(using a pre-defined test case=通过预定义的测试样例?)。如果预先定义的测试样例失败了,会返回错误代码到0xC0000428。然后,检查调试器是否开启(通过BlBdDebuggerEnabled)。如果在这里检测到调试器在开启状态,会调用KnownAnswerTest。如果没有检测到调试器处在开启状态,会直接跳过该处。
当所有的完整性检查结束后(unless all integrity checks have been disabled),OslInitializeCodeIntegrity会返回成功状态,然后会继续从OslMain开始执行。接着,OslpLoadAllModules被调用并开始加载系统模块。首先,会调用OslLoadImage来加载NTOSKRNL.EXE和HAL.DLL,在这里仅仅是加载,此时没有解决Imports;第二,如果内核调试被开启,调试驱动会依靠启动调试选项的情况被加载(kdcom.dll for serial port, kd1394.dll for IEEE1394, or kdusb.dll for USB)。第三,NTOSKRNL.EXE的Imports被加载和初始化(使用LoadImports和BindImportRefences函数)。
OslLoadImage calls GetImageValidationFlags to check the filename against a pre-defined list of boot drivers in LoadBootImagesTable. If integrity checks are enabled, then boot drivers must be signed by a trusted root authority and all the image hashes must match the signed catalog file unless a debugger is enabled. If a debugger is enabled, WINLOAD.EXE does not enforce this requirement. Instead it will print an error message to the debugger, but will otherwise ignore the code integrity check failure. However, the following boot drivers (also listed in Appendix A) must pass the code integrity checks even if a debugger is enabled (otherwise WINLOAD.EXE will refuse to boot Windows Vista):
接下来使用OslHiveFindDrivers查找所有的boot drivers,并且根据组(which is ordered according to HKEY_LOCAL_MACHINE\CurrentControlSet\Control\GroupOrderList)和标记(an integer which determines each driver’s order within its respective group)对他们进行分类。这个分类好的boot drivers列表通过OslLoadDrivers进行加载。OslLoadDrivers为列表中的每个驱动调用LoadImageEx。LoadImageEx将会加载每个驱动及相关信息。
在OslpLoadAllModules结束后,OslMain保存启动日志(OslpLogSaveInformation),如果FVE(Full Volume Encryption)选项开启,结束FVE的加载(BlFveSecureBootRestrictToOne and BlTpmShutdown)。最后,调用OslArchTransferToKernel把控制权转交给NTOSKRNL.EXE。