5.3 Windows系统的引导过程
图5-2展示了适用于Windows Vista和更高版本的Windows启动过程和所涉及组件的示意图。图中的每个方框代表在引导过程中按从上到下的顺序执行和控制的模块。就像你所看到的那样,它与传统启动过程的迭代非常相似。但是,随着现代Windows操作系统组件的复杂性增加,引导过程中涉及的模块也随之增加。
图5-2 Windows引导过程的高级视图
在接下来的几节中,我们将参考此图详细介绍引导过程。如图5-2所示,当计算机首次启动时,BIOS启动代码接收到控制命令,就像软件看到的那样,这是启动过程的开始,其他逻辑涉及硬件/固件级别(例如,在芯片组初始化期间),但在引导过程中对软件不可见。
5.3.1 BIOS和预引导环境
BIOS执行基本的系统初始化和开机自检工作,可以确保关键的系统硬件能够正常工作。BIOS还提供了一个专门的环境,其中就包括与系统设备通信所需的基本服务。这种简化的I/O接口首先在预引导环境中可用,随后被操作系统的抽象用法所取代。在分析Bootkit恶意软件时,最有趣的是磁盘服务,它暴露了许多用于执行磁盘I/O操作的入口点。可以通过称为中断13h处理程序(或简称为INT 13h)的特殊处理程序访问磁盘服务。引导程序通常会通过篡改INT 13h来针对磁盘服务。这样做是为了通过修改操作系统启动期间从硬盘驱动器读取的操作系统和引导组件来禁用或规避操作系统保护。
接下来,BIOS将会查找可引导的磁盘驱动器,该磁盘驱动器承载要加载的操作系统实例。这可能是硬盘驱动器、USB驱动器或CD驱动器。一旦确定了可引导设备,BIOS引导代码将加载MBR,如图5-2所示。
5.3.2 主引导记录
主引导记录(MBR)是一个数据结构,包含关于硬盘驱动器分区和引导代码的信息。它的主要任务是确定可引导硬盘驱动器的活动分区,该分区包含要加载的操作系统实例。一旦确定了活动分区,MBR就读取并执行其引导代码。代码清单5-1展示了MBR的结构。
代码清单5-1 MBR结构体
就像你所看到的那样,MBR引导代码❶的大小被限制为446字节(十六进制表示为0x1BE,这是一个引导代码逆向工程师熟悉的值),所以它只能实现基本功能。接下来,MBR解析分区表(如代码清单5-1中❷所示),以查找活动分区,读取第一个扇区中的卷引导记录(VBR),并将控制权转移给它。
1. 分区表
MBR中的分区表是一个包含四个元素的数组,每个元素都由代码清单5-2所示的MBR_PARTITION_TABLE_ENTRY
结构描述。
代码清单5-2 分区表条目的结构体
MBR_PARTITION_TABLE_ENTRY
结构体的第一个字节❶是status
字段,表示分区是否处于活动状态。在任何时候,都只能有一个分区被标记为活动状态,活动状态用值128(十六进制为0x80)表示。
type
字段❷列出了分区类型。最常见的类型有:
- EXTENDED MBR分区类型
- FAT12文件系统
- FAT16文件系统
- FAT32文件系统
- IFS(用于安装过程的可安装文件系统)
- LDM(适用于Microsoft Windows NT的逻辑磁盘管理器)
- NTFS(主要的Windows文件系统)
类型的值为0表示未使用。字段lbaStart
和size
❸定义了分区在磁盘上的位置,以扇区表示。lbaStart
字段表示分区从硬盘驱动器开始处的偏移量,而size
字段表示分区的大小。
2. 微软Windows驱动器布局
图5-3显示了微软Windows系统中典型的两个分区的可引导硬盘驱动器的布局。
图5-3 典型的启动硬盘驱动器布局
Bootmgr分区包含bootmgr模块和其他一些操作系统引导组件,而操作系统分区包含一个托管操作系统和用户数据的卷。bootmgr模块的主要目的是确定要加载哪个特定的操作系统实例。如果计算机上安装了多个操作系统,bootmgr将显示一个对话框,提示用户选择一个。bootmgr模块还提供参数(是否应该处于安全模式下,使用最后一个已知的良好配置来确定如何加载操作系统,禁用驱动程序签名强制执行,等等)。
5.3.3 卷引导记录和初始程序加载器
硬盘驱动器可能包含承载不同操作系统的多个实例的几个分区,但是通常只有一个分区应该被标记为活动的。MBR不包含解析活动分区上使用的特定文件系统的代码,因此它读取并执行分区的第一个扇区VBR,如图5-2的第三层所示。
VBR包含分区布局信息,它指定正在使用的文件系统的类型及其参数,以及从活动分区读取初始程序装入器(IPL)模块的代码。IPL模块实现了文件系统解析功能,以便能够从分区的文件系统中读取文件。
代码清单5-3显示了VBR的布局,它由BIOS_PARAMETER_BLOCK_NTFS
和BOOTS-TRAP_CODE
结构组成。BIOS_PARAMETER_BLOCK(BPB)
结构的布局对应卷的文件系统。BIOS_PARAMETER_BLOCK_NTFS
和VOLUME_BOOT_RECORD
结构对应于NTFS卷。
代码清单5-3 VBR布局
注意,VBR结构体的开始部分是一个jmp
指令❷,将系统的传输控制转换到VBR代码。VBR代码将从分区中读取和执行IPL,指定的位置是HiddenSectors
字段❶。IPL报告硬盘驱动器开始的偏移量(在扇区)。VBR的布局如图5-4所示。
图5-4 VBR结构体
如你所见,VBR基本上由以下组件组成:
- 负责加载IPL的VBR代码。
- BIOS参数块(存储卷参数的数据结构)。
- 出现错误时显示给用户的文本字符串。
- 0xAA55,VBR的2字节签名。
IPL通常占用15个连续的512字节的扇区,并且恰好位于VBR之后。它实现了足够解析分区文件系统并继续加载bootmgr模块的代码。IPL和VBR一起使用是因为VBR只能占用一个扇区,并且由于卷的文件系统可用空间太少,因此无法实现足够的功能来解析卷的文件系统。
5.3.4 bootmgr模块和引导配置数据
IPL从文件系统中读取并加载操作系统引导管理器的bootmgr模块,如图5-2的第四层所示。IPL运行后,bootmgr接管引导过程。
bootmgr模块读取了引导配置数据(BCD),其中包含几个重要的系统参数,包括那些影响安全策略的参数,比如第6章中介绍的内核模式代码签名策略。Bootkit恶意软件经常试图绕过bootmgr的代码完整性验证实现。
bootmgr模块的起源
Windows Vista中引入了bootmgr模块,以替代在以前Windows NT版本中发现的ntldr
引导加载程序。微软的想法是在引导链中创建一个额外的抽象层,以便将预引导环境与操作系统内核层隔离开来。引导模块与操作系统内核的隔离为Windows带来了引导管理和安全性方面的改进,使得在内核模式模块上实施安全策略(例如内核模式代码签名策略)更加容易。遗留的ntldr
被分成两个模块:bootmgr和win-load.exe(或者winresu .exe,如果操作系统从休眠状态加载)。每个模块实现不同的功能。
bootmgr模块将管理启动过程,直到用户选择启动选项为止(对于Windows 10,如图5-5所示)。一旦用户做出选择,程序winload.exe(或winresume.exe)将加载内核,启动驱动程序以及一些系统注册表数据。
图5-5 Windows 10中的bootmgr引导菜单
1. 真实模式与保护模式
首次打开计算机电源时,CPU在真实模式下运行,这是一种传统的执行模式,该模式使用16位内存模型,其中RAM中的每个字节都由包含两个字(2个字节)的指针寻址:segment_start : segment_offset。此模式对应于段存储模型,其中地址空间分为多个段。每个目标字节的地址由段的地址和段内目标字节的偏移量来描述。在这里,segment_start指定目标段,而segment_offset是目标段中引用字节的偏移量。
真实模式寻址方案只允许使用少量可用的系统内存。具体来说,内存中的实际(物理)地址被计算为最大地址,表示为ffff:ffff,它只有1 114 095字节(65 535×16 + 65 535),这意味着真实模式下的地址空间被限制在1MB左右—这显然不足以满足现代操作系统和应用程序的需要。为了规避这一限制并访问所有可用内存,bootmgr和winload.exe在bootmgr接管后将处理器切换到保护模式(在64位系统中称为长模式)。
bootmgr模块由16位真实模式代码和一个压缩的PE镜像组成,该映像在未压缩时以保护模式执行。16位代码从bootmgr映像中提取和解压缩PE,将处理器切换到保护模式,并将控制权传递给未压缩的模块。
注意 Bootkit恶意软件必须正确处理处理器执行模式的切换,以保持对引导代码执行的控制。在切换之后,整个内存布局被改变,并且以前位于一个连续的内存地址集的代码的一部分可以移动到不同的内存段。Bootkit恶意软件必须实现相当复杂的功能来解决这个问题并保持对引导过程的控制。
2. BCD引导变量
bootmgr初始化保护模式后,未压缩的映像将接受控制并从BCD加载引导配置信息。当BCD存储在硬盘驱动器上时,其布局与注册表配置单元相同。(要浏览其内容,使用regedit
并导航到键HKEY_LOCAL_MACHINE\BCD000000。)
注意 为了从硬盘驱动器读取数据,bootmgr运行在保护模式下,而为了使用INT 13h磁盘服务,需要在真实模式下运行。为此,bootmgr将处理器执行的运行环境保存在临时变量中,临时切换到真实模式,执行INT 13h处理程序,然后返回到保护模式,还原保存的运行环境。
BCD存储区包含bootmgr加载操作系统所需的所有信息,包含要加载的操作系统实例分区的路径、可用的引导应用程序、代码完整性选项以及指示操作系统在预安装模式、安全模式加载的参数等。
表5-1展示了Bootkit恶意软件作者最感兴趣的BCD中的参数。
表5-1 BCD引导变量
变量BcdLibraryBoolean_DisableIntegrityCheck
用于禁用完整性检查并允许加载未签名的内核模式驱动程序。在Windows 7及更高版本中,此选项将被忽略,如果启用了Secure Boot,则无法设置此选项。
变量BcdOSLoaderBoolean_WinPEMode
指示系统应在Windows预安装环境模式下启动,该模式本质上是具有有限服务的最小Win32操作系统,主要用于为Windows安装准备计算机。此模式还禁止内核完整性检查,包括在64位系统上强制执行的内核模式代码签名策略。
变量BcdLibraryBoolean_AllowPrereleaseSignatures
使用测试代码签名证书来加载内核模式驱动程序以进行测试。这些证书可以通过Windows驱动程序工具包中包含的工具生成。(Necurs Rootkit使用此过程将恶意的内核模式驱动程序安装到系统上,并使用自定义证书签名。)
获取引导选项后,bootmgr执行自我完整性验证。如果检查失败,bootmgr将停止引导系统并显示一条错误消息。但是,如果BCD中BcdLibraryBoolean_DisableInte-grityCheck
或BcdOSLoaderBoolean_WinPEModeis
设置为TRUE
,则bootmgr不会执行自我完整性检查。因此,如果任意一个变量为TRUE
,那么bootmgr不会注意到它是否被恶意代码篡改了。
加载了所有必要的BCD参数并通过了自我完整性验证之后,bootmgr将要选择引导应用程序进行加载。如果是重新从硬盘加载操作系统,bootmgr会选择winload.exe;如果是从休眠状态中恢复时,bootmgr则会选择winresume.exe。这些PE模块负责加载和初始化操作系统内核模块。然后bootmgr以同样的方式检查引导应用程序的完整性,如果BcdLibraryBoolean_DisableIntegrityCheck
或BcdOSLoaderBoolean_WinPEMode
为TRUE
,则再次跳过验证。
在引导过程的最后一步,一旦用户选择了要加载的特定操作系统实例,bootmgr就会加载winload.exe。正确初始化所有模块后,winload.exe(见图5-2中的第5层)将控制权传递给操作系统内核,该内核继续引导过程(第6层)。与bootmgr一样,winload.exe会检查它负责的所有模块的完整性。为了将恶意模块注入操作系统内核模式地址空间中,许多引导程序都试图绕过这些检查。
当winload.exe接收到对操作系统引导的控制时,它将启用在保护模式下的分页,然后加载操作系统内核镜像及其依赖项,包括以下模块:
- bootvid.dll:支持计算机图形图像的模块。
- ci.dll:代码完整性模块。
- clfs.dll:通用日志文件系统驱动程序。
- hal.dll:硬件抽象层库。
- kdcom.dll:内核调试器协议通信库。
- pshed.dll:特定于平台的硬件错误驱动程序。
除了这些模块之外,winload.exe还将加载包括存储设备驱动程序、早期启动反恶意软件(ELAM)模块(在第6章中进行了说明)和系统注册表配置单元的启动驱动程序。
注意 为了从硬盘驱动器读取所有组件,winload.exe使用bootmgr提供的接口。此接口依赖BIOS INT 13h磁盘服务。因此,如果INT 13h处理程序被Bootkit恶意程序挂载了,则该恶意软件可以欺骗winload.exe读取的所有数据。
加载可执行文件时,winload.exe根据系统的代码完整性策略验证其完整性。加载所有模块后,winload.exe会将控制权转移到操作系统内核镜像以对其进行初始化,如以下各章节所述。