以下是按下PC电源开关到进入桌面的整体流程

(图1开机引导过程)
内核的加载过程:
CPU进入内核的入口函数KiSystemStartup后,先调用 KiInitializeBootStructures
初始化,然后调用KdInitSystem初始化内核调试引擎,然后调用KiInitializeKernel开启内核启动的过程,
当0号CPU执行KiInitializeKernel时,会调用KiInitSystem来初始化系统的全局数据结构,然后调用
InitBootProcessor做只需要执行一遍的动作,包括创建和初始化Idle进程和System进程。InitBootProcessor
返回后,KiInitializeKernel的工作基本结束。然后调用KiCompleteKernelInit做收场工作,附加系统线程到
System进程让其准备接班。自己便调用KiIdleLoop进入空闲循环休息了。图2归纳了NT内核的启动过程。
分为左右两个部分。图2的左半部分发生在初始进程中的过程,这个初始过程就是后来的Idle进程,图2的
右半部分发生在System进程中。即所谓的执行体阶段1的初始化。

(图2 操作系统加载过程)
l 开机后,UEFI读取位于ESP分区EFI/Microsoft/Boot/目录下的bootmgfw.efi文件并将计算机控制权交给bootmgfw程序。
l bootmgfw搜索并读取存储于EFI/Microsoft/Boot/目录下的BCD文件。如果有多个操作系统启动选项,则bootmgfw会显示所有启动选项,
l 如果只有一个启动选项,bootmgfw会默认启动。(BCD引导指定文件)
默认启动操作系统后,bootmgrfw搜索并读取Windows分区Windows\System32目录下的winload.efi程序,然后将计算机控制器交给 winload.efi

(图3,中断到bootmgrfw)
输入g进入winload阶段
winload.efi/winload.exe是Windows引导加载程序,负责:
l 加载内核文件(ntoskrnl.exe)和核心依赖模块(如HAL.dll、CI.dll、BootVID.dll等)。
l 加载引导类型(Boot Start)的驱动程序。
l 准备内核执行环境(如GDT/IDT、内存映射)。
l 将控制权移交至内核入口点(KiSystemStartup)

(图4,中断到winload)
输入g进入内核调试阶段
2.3 内核启动阶段
中断到内核调试引擎

(图5 中断到操纵系统的内核调试引擎)
Winload传递参数LoaderBlock给KiSystemStartup,KiSystemStartup把它赋值给KeLoaderBlock。
执行命令:dt poi(nt!KeLoaderBlock) nt!_LOADER_PARAMETER_BLOCK查看

(图6 查看KiSystemStartup的参数)
接下来检测CPU特征和初始化CPU、设置中断描述符表、建立和初始化处理器控制区PCR、建立任务状态段TSS、设置用户调用内核服务的MSR,这些操作在nt!KiInitializeBootStructures中,则nt!KiSystemStartup伪代码为:
void KiSystemStartup(LOADER_PARAMETER_BLOCK* pLoaderParaBlock) {
KeLoaderBlock = pLoaderParaBlock;//KiSystemStartup内核入口函数的参数赋值为全局变量KeLoaderBlock
KiInitializeBootStructures(KeLoaderBlock);//这个函数在初始化内核调试引擎之前,所以0号cpu是无法调试这个函数的,其他的cpu是可以。
KdInitSystem(); //初始化内核调试引擎,这个函数之前的函数都无法下断点,这个函数调用之后的都可以下断点
KiInitializeKernel();//初始化内核
ExpSecurityCookieRandomData = <RDTSC>;
KiIdleLoop();//执行完逻辑后,蜕变为空闲进程
};

(图7 KiSystemStartup函数的逻辑)
使用命令在KiInitializeKernel处下断点,
执行命令:bu nt!KiInitializeKernel
执行命令:g
发现中断到KiInitializeKernel

(图8 KiInitializeKernel函数下断点,并命中)
KiSystemStartup函数在调用KiInitializeKernel之前,先把中断优先级上升到15
执行查看中断优先级命令:!irql

(图9 查看进入KiInitializeKernel函数后的中断优先级)
执行命令:uf /c nt!KiInitializeKernel

(图10 查看KiInitializeKernel函数调用了哪些函数)
精简后,发现KiInitializeKernel主要调用以下函数:
nt!HvlPhase0Initialize () //检查是否运行在虚拟机中,如果运行在虚拟机中,会进一步的检查虚拟机监视器(VMM)是否为微软的Hyper V, 如果是,会检查版本号,以便得到VMM的有待,提高性能
nt!KeCompactServiceTable () //初始化系统服务表,SSDT
nt!KiSetCacheInformation ()//初始化cpu高速缓存信息
nt!KiInitSystem () //调用KiInitializeIdleProcess初始化IDLE进程,初始化内核部件全局数据结构,如蓝屏回调函数链表KeBugCheckCallbackListHead、性能堪察器列表KiProfileListHead和各种同步对象。
nt!InitBootProcessor () //!!!!!!!!!!!!!!!!!!主要负责“执行体0阶段初始化
nt!HalInitSystem ()
nt!KiCompleteKernelInit () //完成内核初始化,并把当前线程挂靠到System进程中

(图11 KiInitializeKernel核心逻辑)
2.3.2 执行体的阶段0初始化
其中上面的nt!InitBootProcessor()主要负责执行体0阶段的初始化
执行命令:bu nt!InitBootProcessor
执行命令:g
执行命令:uf /c nt!InitBootProcessor

(图12 查看InitBootProcessor函数调用了哪些函数)
对函数调用精简后,InitBootProcessor主要调用有:
nt!HalInitSystem () // HAL层内核相关的初始化,阶段0
nt!CmInitSystem0 () // 配置管理初始化,阶段0
nt!ExInitSystem () // Executive层初始化
nt!MmInitSystem () // 内存管理器初始化
nt!EtwInitialize () //ETW初始化
nt!PsInitializeWin32kServiceTable () //Shadow SSDT
nt!HvlPhase1Initialize () //虚拟化阶段1初始化
nt!ObInitSystem () // 对象管理器初始化
nt!SeInitSystem () // 安全管理器初始化
nt!PspInitPhase0 () // !!!!!!!!!!!!!进程管理器阶段0初始化,
nt!DbgkInitialize () // 用户态调试系统初始化
nt!PpInitSystem () // PNP初始化

(图13 nt!InitBootProcessor调用进程管理器阶段0初始化函数PspInitPhase0)
上面流程的核心是调用了进程管理器阶段0的初始化
执行命令:bu nt!PspInitPhase0
来到进程管理器阶段0的初始化函数PspInitPhase0,设置当前进程名字为Idle,且创建了System进程和System进程的Phase1Initialization线程,流程进入了执行体阶段1的初始化
以下是对函数PspInitPhase0的IDA分析

(图14 反汇编执行体阶段0初始化函数PspInitPhase0)
PspInitPhase0执行完后,当前线程(Idle线程)返回到nt!KiSystemStartup, 开始执行nt!KiIdleLoop。
该线程中断请求级别IRQL较高而无法执行。在KiInitializeKernel返回后,KiSystemStartup将当前CPU的IRQL降到DISPATCH_LEVEL,
下次发生时钟中断、内核调度线程时系统线程执行,开始阶段1初始化

(图15 KiSystemStartup执行完毕后变成idle线程)
2.3.3 执行体的阶段1初始化
正是由于KiIdleLoop主动让出了CPU,上面的系统线程Phase1Initialization才得以执行,
我们跟踪到该函数:系统线程中第一个线程的工作函数为Phase1Initialization,我们把执行体阶段1初始化分为三个部分,每个部分由一个子函数完成
首先冗长的部分在其中Phase1InitializationDiscard中执行,
然后又调用漫长的I/O初始化nt!IoInitSystem
然后又调用I/O初始化的后续工作nt!Phase1InitializationIoReady

(图16 Phase1Initialization函数的执行逻辑)
执行命令:bu nt!Phase1Initialization

(图17 命中阶段1初始化函数nt!Phase1Initialization)
2.3.3.1 唤醒其他的CPU工作&以及其他组件的初始化
阶段1的主要工作由Phase1InitializationDiscard完成,这个函数工作量极大,也是内核初始化最核心的部分。
Phase1InitializationDiscard用 HalInitSystem 再一次给HAL初始化机会,用 KeInitializeClock 初始化时钟和系统时间。
之后开始用 KeStartAllProcessors 来唤醒同伴,简称KSAP。KSAP用HAL的 HalEnumerateProcessors 来枚举系统里所有处理器。
对于每个处理器都提供IDT、PCR、ISR栈。ISR栈指中断服务例程栈,专门负责处理NMI、双误、机器检查异常等特殊中断情况,
因为CPU需切换到全新线程上下文。最后KSAP用HalStartNextProcessor唤醒一个新CPU。
执行命令:bu nt!KeStartAllProcessors

(图18 命中阶段1初始化函数nt!KeStartAllProcessors)
执行uf /c nt!Phase1InitializationDiscard,查看调用的函数:
nt!HalInitSystem // HAL层内核相关的初始化,阶段1
nt!PoInitSystem // 电源管理器初始化
nt!KeStartAllProcessors // 让所有CPU开始干活
nt!ObInitSystem // 对象管理器初始化
nt!KeInitSystem // Kernel层相关初始化,对应Executive
nt!KdInitSystem // 内核态调试组件初始化
nt!DbgkInitialize // 用户态调试组件初始化
nt!SeInitSystem // 安全管理器初始化
nt!MmInitSystem // 内存管理器初始化
nt!CcInitializeCacheManager // 缓存管理器初始化
nt!CmInitSystem1 // 配置管理器初始化,阶段1
nt!EmInitSystem // Errata管理器初始化
nt!MfgInitSystem // Manufacturer管理器初始化
nt!PfInitializeSuperfetch // Prefeacther初始化
nt!SmInitSystem // 存储管理器初始化
nt!FsRtlInitSystem // 全局文件系统相关结构初始化
nt!PpInitSystem // PNP初始化
nt!AlpcpInitSystem // ALPC初始化
nt!ExInitSystemPhase2 //Executive第二次初始化
nt!ExInitializeNls // NLS初始化
唤醒后的CPU都会从内核入口开始执行,也都会执行KiSystemStartup
和KiInitializeKernel这样的内核初始化函数,但只有第一个CPU会执行所有的初始化逻辑,
包括全性的初始化,其他的cpu只执行与单个cpu相关的部分,比如只有0号cpu会调用和执行KiInitSystem,另外初始化空闲进程的工作也是
由0号cpu执行,因为只需要一个空闲进程,但因为每个cpu都需要一个空闲的线程,所以每个cpu都需要执行启动空闲线程的代码。例如下面,
3号cpu被唤醒后,从nt!KiSystemStartup开始执行,然后执行nt!KiStartIdleThread创建自己的空闲线程。
初始化空闲进程也只有0号CPU执行,但每个CPU都需要用KiStartIdleThread为自己创建一个空闲线程。
全局变量KeNumberProcessors为系统CPU个数,初始值为0,每初始化结束一个CPU该值自增1。
其他CPU依次返回KiInitializeKernel和KiSystemStartup直接开始KiIdleLoop执行自己的空闲线程。
执行命令:bu nt!KiStartIdleThread
执行命令:db nt!KeNumberProcessors
执行命令:!prc

(图19 命中nt!KiStartIdleThread)
宏观流程:0号cpu先跑,跑到阶段1的初始化,开始调用nt!KeStartAllProcessors唤醒其他的cpu,其他的cpu被唤醒后,
也是从内核的cpu入口nt!KiSystemStartup开始跑,也会经过nt!KiInitializeKernel内核初始化这样的关键路标,但也只是
走马观花,不会像0号cpu那样面面俱到,比如只有0号cpu会创建入口为nt!Phase1Initialization的系统线程,其他的cpu
不会操心这件事,很快就返回到内核初始化函数KiInitializeKernel,然后又返回到内核入口函数KiSystemStartup,之后通过一个
跳转指令到KiIdleLoop,去执行自己的空闲线程了。
接下来是执行阶段1的漫长的I/O初始化的流程,执行体阶段1初始化函数nt!Phase1Initialization内部会调用以下两个函数,进行io初始化。
nt!IoInitSystem,它内部又把任务分派给两个子函数,IoInitSystemPreDrivers和IopInitializeSystemDrivers,前者负责初始化
内建的和启动类型的驱动(SERVICE_BOOT_START),后者负责初始化系统类型的驱动(SERVICE_SYSTEM_START)。

(图20 nt!IoInitSystem函数逻辑)

(图21 不同驱动加载时机)

(图22 SERVICE_BOOT_START启动类型的驱动)

(图23 SERVICE_SYSTEM_START系统类型的驱动)
I/O初始化的后续工作由函数nt!Phase1InitializationIoReady来完成,用来创建用户态的第一个进程会话管理器smss.exe
SMSS.EXE进程的启动代表这一个新阶段的开始,它肩负这内核的委托,要营造处一个繁荣的“用户”世界。
创建会话管理器进程smss.exe
Phase1InitializationIoReady
StartFirstUserProcess //创建第一个用户进程
RtlpCreateUserProcess //这里创建会话管理器进程smss.exe

备注:全局变量表示当前处于初始化的哪一阶段,这里也可以下内存断点:ba w4 nt!InitializationPhase
执行命令:bu nt!RtlpCreateUserProcess
执行命令:g
执行命令:!process 0 0
备注:查看输出结果,此时是没有会话管理进程smss.exe
执行命令:gu
执行命令:!process 0 0
备注:查看输出结果,此时出现了会话管理进程smss.exe,会话管理器进程创建成功!!!

会话管理器进程smss.exe的主要工作有
l 完成悬而未决的文件删除任务和改名任务
计算机\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager。
图中左侧的PendingFileRenameOperations 子健就是用来存放需要删除的文件的清单的,杀软经常用这个来删除难以删除的文件,因为SMSS.EXE启动时,
其他的用户态进程还没有运行,不会因为其他程序使用这个文件而无法删除。

l 执行BootExecute表键下定义的命令
System进程-> Smss.exe -> Smss.exe-> autochk.exe
通常这里定义的是磁盘检查程序,即autochk.exe
首先找到smss.exe的进程内核对象
执行命令:!process 0 0

执行命令: bu nt!NtCreateUserProcess
执行命令: bl
执行命令: g
执行命令: .reload /user
执行两次命令: kb
执行以上命令后,将完整的显示出smss.exe调用内核函数nt!NtCreateUserProcess时的堆栈(包括用户态和内核态的堆栈)。

执行命令: u smss!SmpExecuteImage+0x2c0
执行命令: bu 00007ff6`a9c614c4
备注:此命令是在ntdll!RtlCreateUserProcessEx的返回地址处下断点,bu后面的00007ff6`a9c614c4具体值要根据自己机器上的值。不能照抄我的
执行命令:g
执行命令:!process 0 0
备注:执行完此命令后,可以看到有了autochk.exe进程

l 建立环境子系统CSRSS.EXE
System进程-> Smss.exe -> Smss.exe(貌似这个进程很快就消亡了) -> CSRSS.EXE
SMSS.EXE会首先加载子系统的内核部分,即win32k.sys,然后根据下图的subSystems表键下的windows键值来创建windows子系统服务器进程CSRSS.EXE
这个键值的格式如下,其中ObjectDirectory是传递给CSRSS.exe进程的命令行参数,其中的ServerDll是CSRSS.exe进程要加载的服务模块。一些病毒可利用
这个特征把自己的模块加进来。以便加载到CSRSS.EXE进程中。
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16

接着上面的操作,
执行命令:g
执行命令:k
执行命令:g
执行命令:!process 0 0
发现了smss.exe(进程id为01e8)又创建了另外一个smss.exe(进程id为02bc, 这个进程貌似后面消亡了)

接着针对新创建的smss.exe下条件断点
执行命令:g
执行命令:g
执行命令:!process 0 0
发现第二个smss.exe(进程id为02bc)创建了环境环境子系统进程CSRSS.EXE(进程id为02cc)

建立初始化进程
wininit.exe进程的创建流程为:System进程-> Smss.exe -> Smss.exe(貌似这个进程很快就消亡了) -> wininit.exe
wininit.exe的主要主要任务是创建服务管理器进程services.exe(服务管理器进程SCM)和lsass.exe(本地安全授权子系统服务(LSASS),负责处理身份验证、用户登录等安全任务)和fontdrvhost.exe(管理字体驱动程序的进程,处理字体的加载和显示。)
services.exe负责运行所有的windows后台服务,lsass.exe用来验证登录时,输入的用户名和密码

继续执行wininit.exe又创建了服务管理器进程services.exe(服务管理器进程SCM)和lsass.exe(用来进行登录验证)
services.exe负责启动windows的后台服务进程。

l 建立登录进程winLogon.exe
winlogon.exe进程是Windows系统的核心进程,负责管理用户登录/注销、安全认证及系统安全策略执行
winlogon.exe进程的创建流程为:System进程-> Smss.exe -> Smss.exe -> winlogon.exe

(一)显示登录界面
显示登录界面winlogon.exe会创建LogonUI.exe和dwm.exe进程,其中LogonUI.exe就是我们的登录界面,
负责显示开机密码 输入、用户切换及背景界面。桌面窗口管理器进程,负责管理 Windows 窗口和桌面效果(如透明窗口、特效等)
用户输入登录凭证后,lsass.exe进程验证用户额登录信息成功后,winlogon.exe会将应用程序桌面激活,
接下来winlogon.exe 会执行所谓的用户初始化动作
下面是验证winlogon.exe 创建了LogonUI.exe和dwm.exe进程的过程。
①清除掉所有的断点
②执行命令: bu /p ffffbf0e2e189080 nt!NtCreateUserProcess
备注:ffffbf0e2e189080 是winlogon.exe的EPROCESS内核对象地址,需要通过!process 0 0查看
③执行命令: bl
④执行命令: g
⑤执行命令: .reload /user
⑥执行两次命令: kb

(二)进入桌面
进入桌面的流程为:winlogon.exe->userinit.exe->explorer.exe,也即是执行注册表中以下键值定义的命令:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit
通常这个键值的内容为:C:\WINDOWS\system32\userinit.exe,也就是userinit.exe程序,

userinit.exe的主要功能。
执行用户登录脚本(如组策略脚本)。
加载用户配置文件(桌面设置、环境变量等)。
启动默认Shell进程(通常为explorer.exe)
然后userinit.exe会启动操纵系统的外壳程序(shell)
它首先会在计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon中寻找Shell的值,
Shell值的内容是explorer.exe,explorer.exe程序运行后,我们就进入了windows的桌面。

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-8-26 22:20
被sanganlei编辑
,原因: