[Windows内核源码分析0] 引导过程(Phase0部分分析)
第一个能显示出源码并下到断点的函数是ExpInitializeExecutive上面的描述说该例程会被调用2次, 首先在Phase0阶段初始化执行体层和所有它的子组件, 接着在Phase1阶段再次执行初始化工作。
来看看当前的调用堆栈:
可以看到调用顺序是KiSystemStartup调用了KiInitializeKernel接着在调用了ExpInitializeExecutive
接下来在这三个函数上下断点, 然后再次尝试, 发现在KiInitializeKernel上断了下来, 先尝试调试KiInitializeKernel
KiSystemStartup是有源码的, 但是我在这个函数上下断点没断下来, 所以我在单步补过后直接进入了KiInitializeKernel内。
这个函数的的描述说明了该函数的作用:
该函数的主要目的是初始化内核数据结构, 初始化空闲线程和处理器对象, 初始化PCB结构即进程控制块, 并调用执行体层的初始化例程, 并且也用于新处理器上线时的初始化。
进入KiInitializeKernel后首先:
接着填充R0和R3都共享的SharedUserData区域关于引导选项的信息, 这些信息都是从boot.ini中设置的
下面同样都是初始化PRCB中的数据结构初始化一些分发器内核对象
如果主处理器初始化成功了,则继续初始化处理器控制块PRCB中的内容,并将处理器的一些信息保存下来
调用了KiInitSystem, 初始化各类分发器对象和内核对象
初始化了DPC对象, 一些自旋锁以及各类链表
初始化了定时器链表对象等
SSDT和Shadow SSDT的初始化
接下去调用了KeInitializeProcess初始化内核进程对象
说白了就是把一些信息填充到这个结构体里:
初始化内核线程对象
KeInitializeThread内部首先调用了KeInitalThread初始化了线程对象后调用KeStartThread启动该线程
看一下KeStartThread内部做了什么, 首先继续填充线程对象包括线程优先级, 处理器亲和性等信息
从处理器亲和性数值判断出该线程最适合使用哪一个处理器(WRK必须运行在多核处理器上, 所以要选择一个处理器)
将该对象插入到进程的线程链表中,等待处理器调度,至此该线程便要开始运行了。
初始化处理器控制块
接下去调用了ExpInitializeExecutive函数
进入后调用的第一个判断是ExpIsLoaderValid
来看看这个函数的原型:
其内部使用了一个名为LOADER_PARAMETER_BLOCK的结构体,看一下这个结构体的数据结构:
typedef struct _LOADER_PARAMETER_BLOCK {
LIST_ENTRY LoadOrderListHead; // 加载模块链表
LIST_ENTRY MemoryDescriptorListHead; // 内存描述符链表
LIST_ENTRY BootDriverListHead; // 引导驱动链表
ULONG_PTR KernelStack;
ULONG_PTR Prcb;
ULONG_PTR Process;
ULONG_PTR Thread;
ULONG RegistryLength;
PVOID RegistryBase;
PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;
PCHAR ArcBootDeviceName;
PCHAR ArcHalDeviceName;
PCHAR NtBootPathName;
PCHAR NtHalPathName;
PCHAR LoadOptions;
PNLS_DATA_BLOCK NlsData;
PARC_DISK_INFORMATION ArcDiskInformation;
PVOID OemFontFile;
struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;
PLOADER_PARAMETER_EXTENSION Extension;
union {
I386_LOADER_BLOCK I386;
// ALPHA_LOADER_BLOCK Alpha;
// IA64_LOADER_BLOCK Ia64;
} u;
} LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;
来看看监视里该结构的内容, 其中红框框出的部分实际上是在C:\boot.ini中的配置信息, 这个信息会在windows刚开启位于实模式时显示,并让用户选择引导选项。由于当前还没有任何一个进程被创建, 所以Prcb和Process都是0.
设置InitializationPhase为0代表我们在Phase0
调用HalInitSystem来初始化HAL硬件抽象层
这部分没有源码, 但从反汇编里可以看到其是从halacpim.dll中导出的。
从其内部的函数名可以看出与硬件关系非常大,HAL硬件抽象层原本就是微软用于屏蔽不同物理设备上不同接口的不一致从而提供一组同一的接口。其中包含了关于PIC可编程控制器, CMOS等硬件相关,这里不做深究。
初始化完HAL后将中断打开
拼搭出路径C:\WINDOWS:
将其转成ANSI:
调用ExInitSystem初始化执行体各个组件和数据结构:
进入ExInitSystem后根据之前设置的InitializationPhase来判定是对Phase0的初始化还是Phase1的,这里很显然是Phase0
进入ExpInitSystemPhase0后对各类执行体资源进行初始化
初始化分页内存池的链表
以及其他各类自旋锁和链表
返回后调用MmInitSystem初始化内存管理器和内存池, 在阶段0其只要工作是初始化内存管理页函数, 分页内存池即堆空间和PFN数据库:
获取页表地址:
设置堆区信息:
初始化各类执行体对象
确定用户态与内核态之间的界限
MmInitSystem函数代码量非常大, 分别分了2个阶段来进行初始化,这里不做详细讨论。
接下去遍历当前的PRCB中的模块链表并加载对应的符号
看了下之后加载了APCI.sys, pci.sys, isapnp.sys, compbatt.sys, intelide.sys, MountMgr.sys, ftdisk.sys等等
接着执行了ObInitSystem初始化对象管理器, SeInitSystem初始化安全子系统, PsInitSystem初始化进程线程管理器, PpInitSystem初始化即插即用管理器, DbgkInitialize初始化调试子系统等。
由于篇幅都比较长这4个部分我都会专门用博客来记录详细过程。
完成这四部分的初始化后, 对SharedUserData设置一些关于操作系统的信息后便返回了
ExpInitializeExecutive返回后, 对PRCB和线程对象进行最后的一些设置后返回
返回后发现程序竟然回到了KiSystemStartup内
把当前线程设为idle线程
蜕变成Idle线程后就是执行KiIdleLoop, Idle线程不停的自旋。
(完)