对于那么没有相关经验的朋友,在阅读本文时最好对照windows源码来看,否则光看着这么多数据结构就足以头大。对于这篇文章,严格来说,应该是属于学习笔记型,如有分析不当的地方,请各位多指教!
本文的主要目标是根据windows源码分析内核与执行体初始化流程,过程中会涉及大量的内核数据结构与函数,文中并不会介绍每个结构的含义及作用。内核中有很多晦涩难懂且枯燥的代码,我在分析过程尽可能的去分析每一行代码,否则如果只分析思路而不注重细节那么就跟没分析一样。
==========================================================
本文结构:
一、内核初始化
1.1 系统启动过程简介
1.2 内核初始化
二、源码分析
2.1 内核初始化KiInitializeKernel
2.2 初始化内核数据结构KiInitSystem
2.3 [phase0]Ntoskrnl初始化ExpInitializeExecutive
2.4 [phase0]初始化进程管理器PsInitPhase0
2.5 [phase1]Ntoskrnl初始化Phase1Initialiation
==========================================================
一、内核初始化
1.1 系统启动过程简介
对于系统启动过程,已经有太多的资料介绍过了。这里只是稍作温习,为介绍后续内容做准备。这部分的内容参考了http://www.yesky.com/317/1711317.shtml。
系统的启动过程一般分为5个步骤:
(1)预引导过程
[1] 系统加点自检,同时完成硬件设备的枚举和配置。
[2] BIOS确定引导设备位置,加载引导设备的MBR。
[3] 在MBR中扫描分区表,定位活动分区,并加载活动分区上引导扇区到内存
[4] 加载系统根目录的ntldr。
(2)引导过程
[5] 初始化Ntldr,完成处理器模式切换和文件系统驱动的加载,如果使用SCSI设备,
Ntldr将Ntbootdd.sys加载到内存。
[6] Ntldr读取系统根目录的boot.ini
,在屏幕显示系统启动菜单,等待用户选择所需要加载的操作系统。
[7] Ntldr读取并运行程序Ntdetect.com,完成硬件的检测。
[8] Ntldr根据用户的选择调用系统的硬件配置文件。
(3)内核加载,在[8]后清除屏幕,显示进度条。
[9] 加载执行体ntoskrnl.exe
[10] 加载Hal.dll
[11] 加载%systemroot\System32\Config\System下的注册表项HKEY_LOCAL_MACHINE\
SYSTEM。
[12] 选择加载控制集,初始化计算机。
[13] 根据控制集加载低级硬件设备驱动程序。
(4)内核初始化,显示图形界面。
[14] 内核会使用检测到的硬件数据,在注册表中创建HKEY_LOCAL_MACHINE\HARDWA项。
[15] 其次的工作是内核通过复制HKEY_LOCAL_MACHINE\SYSTEM\Select子键Current
项引用的控制集创建Clone控制集。
[16] 内核开始进一步加载和初始化设备驱动程序。
[17] Session Manager(Smss.exe)按顺序启动Windows 2000
更高一层次的子系统和各项服务。
(5)系统登陆过程
[18] 系统首先启动Winlogon.exe。
[19] 启动Local Security Authority(Lsass.exe)
[20] 屏幕显示出登陆对话框。
[21] 系统执行Service Controller(Screg.exe)再次扫描注册表HKEY_LOCAL_MACHINE\
SYSTEM\CurrentControlSet\Control项并自动加载其中系统的或用户的服务。
[22] 此时,用户已成功的登陆到了Windows 2000系统,系统随后把Clone控制集拷贝到
LastKnownGood控制集。
1.2 内核初始化
本文所要介绍的重点是ntoskrnl的初始化流程。这个初始化过程大致分为两个阶段:phase0和phase1。对于具体所处的阶段是由一个全局变量InitializationPhase来标识,当InitializationPhase为0时表示处于phase0,当InitializationPhase为1时表示处于phase1。
Ntoskrnl在入口函数中调用KiSystemStartup,而KiSystemStartup又依次为每个CPU调用HalInitializeProcessor和KiInitializeKernel。如果KiInitializeKernel运行在引导CPU上,则会调用KiInitSystem执行系统范围全局的内核初始化。然后KiInitializeKernel调用ExpInitializeExecutive函数,负责实现phase0阶段的初始化工作。
(注:引导CPU,即0号CPU,每个CPU都以整数标识,0号CPU是第一个被初始化的CPU。当初始化第1个CPU时需要进行额外的操作,因而称之为引导CPU)
在phase0阶段的初始化过程中首先调用HalInitSystem初始化HAL,然后依次初始化内存管理器、对象管理器、安全引用监视器、进程管理器和即插即用管理器。其中在调用PsInitSystem执行进程管理器在phase0阶段初始化时,创建了一个新的系统线程即为Phase1Initialization,用于执行phase1阶段的初始化。由于此时并不允许中断,Phase1Initialization线程并不立即执行。当完成phase0阶段初始化并返回到KiInitializeKernel时,设置IRQL到DISPATCH_LEVEL并使CPU调度Phase1Initialization线程,从而进行phase1阶段的初始化。
关于内核初始化流程可以用下图表示,从上到下表示时间顺序:
图示说明:
[1] 关于KiInitializeKernel的具体实现参见2.1小节。
[2] 关于KiInitSystem的具体实现参见2.2小节
[3] 关于phase0阶段ExpInitializeExecutive的具体实现参见2.3小节。
[4] 关于phase0阶段进程管理器初始化(PsInitSystem)的具体实现参见2.4小节。
[5] 关于Phase1Initialization线程实现phase1阶段初始化的具体过程参见2.5小节。
二、源码分析
2.1 内核初始化KiInitializeKernel
这个函数在系统由bootstrapped启动后且系统未被初始化之前取得控制权。当新的处理器加入时,调用这个例程可以初始化处理器相关的数据结构。主要功能是:
(1)初始化内核数据结构
(2)初始化处理控制块(Processor Control Block)
(3)调用内核执行体初始化例程
(4)最后返回系统启动例程(KiSystemStartup)
下面就是KiInitializeKernel的执行流程:
VOID KiInitializeKernel (
IN PKPROCESS Process,
IN PKTHREAD Thread,
IN PVOID IdleStack, // IDLE线程的内核堆栈基地址
IN PKPRCB Prcb, // 指向处理器控制块结构
IN CCHAR Number, // 指定当前正在初始化的处理器索引号
PLOADER_PARAMETER_BLOCK LoaderBlock
)
{
// 初始化Prcb的部分成员
// ……
// 在系统初始化过程(KiSystemStartup)会枚举所有CPU,
// 然后循环调用KiInitializeKernel依次初始化每个CPU
// 在初始化号CPU时,会初始化一部分内核数据结构
if (Number == 0) {
// 初始化处理器相关的全局信息
// 例如KeI386NpxPresent,KeI386CpuType,KeI386CpuStep,KeI386FxsrPresent等
// ……
// 初始化体系无关的内核数据结构
KiInitSystem();
// 初始化idle线程的进程对象
KeInitializeProcess(Process,…… );
}
else
{
// ……
}
// ……
// 初始化线程对象
KeInitializeThread(Thread, (PVOID)((ULONG)IdleStack),
(PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,
(PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULL, Process);
// ……
// 调用内核执行体的初始化例程
try {
ExpInitializeExecutive(Number, LoaderBlock);
} except (EXCEPTION_EXECUTE_HANDLER) {
KeBugCheck (PHASE0_EXCEPTION);
}
// ……
// 如果是启动CPU(0号),动态分配内核堆栈空间和K的IOPM存储区域
if (Number == 0) {
PVOID DpcStack = MmCreateKernelStack(FALSE);
if (DpcStack == NULL) {
KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);
}
Prcb->DpcStack = DpcStack;
// 分配K的IOPM存储区域,用于BiosCall交换
Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,PAGE_SIZE * 2,' eK');
if (Ki386IopmSaveArea == NULL) {
KeBugCheckEx(NO_PAGES_AVAILABLE, 2, PAGE_SIZE * 2, 0, 0);
}
}
// 设置IRQL为DISPATCH_LEVEL级别,允许线程派发
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
// 设置当前线程优先级为0,这样才能调度Phase1Initialization线程
KeSetPriorityThread(Thread, (KPRIORITY)0);
// 检测是否存在就绪线程,如果不存在则把当前CPU添加到KiIdleSummary
KiAcquireQueuedSpinLock(KiQueuedSpinLockContext(LockQueueDispatcherLock));
if (Prcb->NextThread == (PKTHREAD)NULL) {
SetMember(Number, KiIdleSummary);
}
KiReleaseQueuedSpinLock(KiQueuedSpinLockContext(LockQueueDispatcherLock));
// 提升IRQL到HIGH_LEVEL,屏蔽中断
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
return;
}
VOID KiInitSystem (VOID)
{
ULONG Index;
// 初始化调度队列链表头,每一个优先级都有一个独立的进程链表
for (Index = 0; Index < MAXIMUM_PRIORITY; Index += 1) {
InitializeListHead(&KiDispatcherReadyListHead[Index]);
}
// 初始化BugCheck回调函数链表,及其旋转锁
InitializeListHead(&KeBugCheckCallbackListHead);
KeInitializeSpinLock(&KeBugCheckCallbackLock);
// 初始化定时器过期的DPC对象
KeInitializeDpc(&KiTimerExpireDpc,
(PKDEFERRED_ROUTINE)KiTimerExpiration, NIL);
// 初始化profile链表,及其旋转锁
KeInitializeSpinLock(&KiProfileLock);
InitializeListHead(&KiProfileListHead);
// 初始化当前活动的profile链表
InitializeListHead(&KiProfileSourceListHead);
// 初始化定时器链表
for (Index = 0; Index < TIMER_TABLE_SIZE; Index += 1) {
InitializeListHead(&KiTimerTableListHead[Index]);
}
// 初始化swap通知事件
KeInitializeEvent(&KiSwapEvent,SynchronizationEvent,FALSE);
InitializeListHead(&KiProcessInSwapListHead);
InitializeListHead(&KiProcessOutSwapListHead);
InitializeListHead(&KiStackInSwapListHead);
InitializeListHead(&KiWaitInListHead);
InitializeListHead(&KiWaitOutListHead);
// 初始化SSDT
KeServiceDescriptorTable[0].Base = &KiServiceTable[0];
KeServiceDescriptorTable[0].Count = NULL;
KeServiceDescriptorTable[0].Limit = KiServiceLimit;
#if defined(_IA64_)
KeServiceDescriptorTable[0].TableBaseGpOffset =
(LONG)(*(KiServiceTable-1) - (ULONG_PTR)KiServiceTable);
#endif
KeServiceDescriptorTable[0].Number = &KiArgumentTable[0];
for (Index = 1; Index < NUMBER_SERVICE_TABLES; Index += 1) {
KeServiceDescriptorTable[Index].Limit = 0;
}
// 拷贝SSDT到Shadow服务表
RtlCopyMemory(KeServiceDescriptorTableShadow,
KeServiceDescriptorTable,
sizeof(KeServiceDescriptorTable));
// ……
return;
}
BOOLEAN SeInitSystem( VOID )
{
switch ( InitializationPhase ) {
case 0 : return SepInitializationPhase0();
case 1 : return SepInitializationPhase1();
default: KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
}
return 0;
}
BOOLEAN PsInitSystem ( IN ULONG Phase, IN PLOADER_PARAMETER_BLOCK LoaderBlock )
{
switch ( InitializationPhase ) {
case 0 : return PspInitPhase0(LoaderBlock);
case 1 : return PspInitPhase1(LoaderBlock);
default: KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
}
return 0;
}
BOOLEAN PpInitSystem ( VOID )
{
switch ( InitializationPhase ) {
case 0 : return PiInitPhase0();
case 1 : return PiInitPhase1();
default: KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
}
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)