首页
社区
课程
招聘
[原创]逆向Windows内核启动流程
发表于: 2025-8-25 13:19 2239

[原创]逆向Windows内核启动流程

2025-8-25 13:19
2239

一. Windows开机整体过程


以下是按下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 操作系统加载过程)


二.Windows详细启动流程

2.1 bootmgfw启动阶段


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阶段

2.2 Winload启动阶段

winload.efi/winload.exeWindows引导加载程序,负责:

l  加载内核文件(ntoskrnl.exe)和核心依赖模块(如HAL.dllCI.dllBootVID.dll等)。

l  加载引导类型(Boot Start)的驱动程序。

l  准备内核执行环境(如GDT/IDT、内存映射)。

l  将控制权移交至内核入口点(KiSystemStartup

                                                      (图4,中断到winload


输入g进入内核调试阶段

2.3 内核启动阶段

2.3.1 内核初始化

中断到内核调试引擎

                                                                (图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);
//这个函数在初始化内核调试引擎之前,所以0cpu是无法调试这个函数的,其他的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,去执行自己的空闲线程了。


2.3.3.2 漫长I/O的初始化

接下来是执行阶段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系统类型的驱动)


2.3.3.2 I/O的初始化的后续工作(创建用户态世界)

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进程的命令行参数,其中的ServerDllCSRSS.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编辑 ,原因:
收藏
免费 14
支持
分享
最新回复 (7)
雪    币: 4600
活跃值: (5142)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
感谢分享。
2025-8-26 17:49
0
雪    币: 5657
活跃值: (6609)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
非常棒的帖子
2025-8-27 12:12
0
雪    币: 5615
活跃值: (3103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
写的很好,感谢分享
2025-8-28 00:06
0
雪    币: 1200
活跃值: (937)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
师傅请教一下怎么设置调试环境呢,我在网上找了很多都不是很全,我自己用edk2写了uefi组件,想像你一样调试,我该怎么做
2025-8-28 00:50
0
雪    币: 3160
活跃值: (5058)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
mb_duzzidie 师傅请教一下怎么设置调试环境呢,我在网上找了很多都不是很全,我自己用edk2写了uefi组件,想像你一样调试,我该怎么做
双机联调下
2025-8-29 17:56
0
雪    币: 203
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
1
2025-8-29 18:30
0
雪    币: 395
活跃值: (96)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这下对windows操作系统的启动流程有了深层次的了解,神贴啊感谢老师~
2025-9-1 08:17
0
游客
登录 | 注册 方可回帖
返回