Windows软件调试初探-启动过程

本篇没特殊说明就是按照时间先后顺序写。

BootMgr

系统上电后执行固化在系统主板上的固件代码,检测和初始化基本硬件,如CPU、内存、键盘、显卡和磁盘等。之后加载并将执行权移交给操作系统启动程序,即NTLDR。NTLDR依次分为BootMgr、WinLoad、WinResume。BootMgr从系统引导配置数据BCD中读取启动设置,当有多个启动选线则显示启动菜单让用户选择。现有图形界面启动程序BootIM.exe,启用经典启动菜单可以:

1
bcdedit /set bootmenupolicy Legacy

为了启用BootMgr和WinLoad的调试引擎,执行以下命令。调试时先用WinDBG监听,之后会自动挂靠。打开调试模式后无法正常进入系统,只能进入安全模式将on改为off。NT内核加载后调试引擎在配置双机调试时已打开。

1
2
3
4
5
6
7
bcdedit /set {bootmgr} bootdebug on
bcdedit /set {bootmgr} debugtype serial
bcdedit /set {bootmgr} debugport 1
bcdedit /set {bootmgr} baudrate 115200

bcdedit /enum ;获取WinLoad的GUID填到下面{GUID}处
bcdedit /set {GUID} bootdebug on

查看栈帧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> kn
# Child-SP RetAddr Call Site
00 00000000`0ffbe738 00000000`1010f5f7 bootmgfw!DebugService2+0x5
01 00000000`0ffbe740 00000000`100ce07b bootmgfw!DbgLoadImageSymbols+0x67
02 00000000`0ffbe790 00000000`100ce572 bootmgfw!BlBdStart+0x193
03 00000000`0ffbe880 00000000`1006fd91 bootmgfw!BlBdInitialize+0x342
04 00000000`0ffbe960 00000000`100201f6 bootmgfw!BlInitializeLibrary+0x95
05 00000000`0ffbe990 00000000`1001fbed bootmgfw!BmMain+0x4ae
06 00000000`0ffbeb10 00000000`0ffc24a2 bootmgfw!EfiEntry+0x1d
07 00000000`0ffbeb40 00000000`0ffbed20 0xffc24a2
08 00000000`0ffbeb48 00000000`0ffbeb88 0xffbed20
09 00000000`0ffbeb50 00000000`0ffbeba0 0xffbeb88
0a 00000000`0ffbeb58 00000000`0fcd8098 0xffbeba0
0b 00000000`0ffbeb60 00000000`0ffbeba0 0xfcd8098
0c 00000000`0ffbeb68 00000000`0ffbed28 0xffbeba0
0d 00000000`0ffbeb70 00000000`00000400 0xffbed28
0e 00000000`0ffbeb78 00000000`0ffbec48 0x400
0f 00000000`0ffbeb80 00000000`00000000 0xffbec48

其中bootmgfw!BmMain为BootMgr的64位代码入口函数,栈帧#07及往下为实模式执行痕迹。加载WinLoad.exe后,BootMgr启用新GDT和IDT,并把调用平台控制权移交给WinLoad,这一步在x86下用Archx86TransferTo32BitApplicationAsm函数。

此外NTLDR调用NTDETECT.COM做基本硬件检查并收集硬件信息,放入注册表。找不到该文件则直接重启,若发现缺少必须的硬件或固件如ACPI支持等则无法启动。

WinLoad

WinLoad启用CPU分页机制并改善运行环境,再初始化自己的支持库,当启用引导调试支持是初始化调试引擎。当用户按F8或上次没正常关机则显示“高级启动”菜单。函数OslpLoadSystemHive读取加载注册表System Hive。

WinLoad核心任务是加载操作系统内核文件和引导类型的设备驱动程序。首先加载NTOSKRNL.EXE。此时磁盘和文件系统驱动程序还没加载,WinLoad用的私有文件访问函数如FileIoOpen打开文件,打开失败则调用它的BlpFileOpen返回错误码0C000000Dh,否则成功返回0。

接下来加载硬件抽象层模块HAL.DLL、调试KDCOM.DLL以及依赖模块如PSHED.DLL、引导期间蓝屏显示BOOTVID.DLL、日志CLFS.SYS、模块完整性检查CI.DLL。然后加载引导类型的设备驱动程序。

其次用OslArchpKernelSetupPhase0位内核准备新GDT和IDT,用OslBuildKernelMemoryMap建立内存映射,用OslArchTranserToKernel把GDT和IDT地址加载到CPU并调用内核入口,把控制权移交给内核。

内核初始化

KiSystemStartup为NT内核模块入口函数,之前WinLoad为它传入LOADER_PARAMETER_BLOCK结构。该结构地址保存在全局变量KeLoaderBlock中但启动后被释放为0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
3: kd> dt !_LOADER_PARAMETER_BLOCK
nt!_LOADER_PARAMETER_BLOCK
+0x000 OsMajorVersion : Uint4B
+0x004 OsMinorVersion : Uint4B
+0x008 Size : Uint4B
+0x00c OsLoaderSecurityVersion : Uint4B
+0x010 LoadOrderListHead : _LIST_ENTRY
+0x020 MemoryDescriptorListHead : _LIST_ENTRY
+0x030 BootDriverListHead : _LIST_ENTRY
+0x040 EarlyLaunchListHead : _LIST_ENTRY
+0x050 CoreDriverListHead : _LIST_ENTRY
+0x060 CoreExtensionsDriverListHead : _LIST_ENTRY
+0x070 TpmCoreDriverListHead : _LIST_ENTRY
+0x080 KernelStack : Uint8B
+0x088 Prcb : Uint8B
+0x090 Process : Uint8B
+0x098 Thread : Uint8B
+0x0a0 KernelStackSize : Uint4B
+0x0a4 RegistryLength : Uint4B
+0x0a8 RegistryBase : Ptr64 Void
+0x0b0 ConfigurationRoot : Ptr64 _CONFIGURATION_COMPONENT_DATA
+0x0b8 ArcBootDeviceName : Ptr64 Char
+0x0c0 ArcHalDeviceName : Ptr64 Char
+0x0c8 NtBootPathName : Ptr64 Char
+0x0d0 NtHalPathName : Ptr64 Char
+0x0d8 LoadOptions : Ptr64 Char
+0x0e0 NlsData : Ptr64 _NLS_DATA_BLOCK
+0x0e8 ArcDiskInformation : Ptr64 _ARC_DISK_INFORMATION
+0x0f0 Extension : Ptr64 _LOADER_PARAMETER_EXTENSION
+0x0f8 u : <anonymous-tag>
+0x108 FirmwareInformation : _FIRMWARE_INFORMATION_LOADER_BLOCK
+0x148 OsBootstatPathName : Ptr64 Char
+0x150 ArcOSDataDeviceName : Ptr64 Char
+0x158 ArcWindowsSysPartName : Ptr64 Char
1: kd> dq nt!KeLoaderBlock L1
fffff806`184fc460 00000000`00000000

接下来检测CPU特征和初始化CPU、设置中断描述符表、建立和初始化处理器控制区PCR、建立任务状态段TSS、设置用户调用内核服务的MSR,这些操作在nt!KiInitializeBootStructures中,则nt!KiSystemStartup伪代码为:

1
2
3
4
5
6
7
8
void KiSystemStartup(LOADER_PARAMETER_BLOCK* pLoaderParaBlock) {
KeLoaderBlock = pLoaderParaBlock;
KiInitializeBootStructures(KeLoaderBlock);
KdInitSystem();
KiInitializeKernel();
ExpSecurityCookieRandomData = <RDTSC>;
KiIdleLoop();
};

KiInitializeBootStructures会第一次用-1参数调用MmInitSystem,即只做最基本初始化以满足启动阶段内存分配需求。KiInitializebootStructures返回后可以动态分配内存且已初始化PRC、GDT、IDT、TSS等。接下来用KdInitSystem初始化内核调试引擎。用Ctrl+Alt+K启用/停用内核调试引擎初始化断点,此时在启动进入NT内核时快速挂靠WinDBG,则主动挂靠到调试器,运行则需调试器脱离。

1
2
3
4
5
6
kd> kc
# Call Site
00 nt!DebugService2
01 nt!DbgLoadImageSymbols
02 nt!KdInitSystem
03 nt!KiSystemStartup

接下来KiInitializeKernel初始化NT内核。调试方法是用bp对它下断点,恢复执行并再次命中后,即可单步跟踪。每个CPU唤醒后都执行KiInitializeKernel但最先执行的0号CPU即启动处理器的工作较全面,主要工作有:

  • HvlPhase0Initialize检查是否在虚拟机中,如果是则检查虚拟机监视器VMM是否是微软公司的Hyper-V,是的话检查版本号,再用HvlEnglightenments得到VMM的优待以提高性能。
  • KiDetectFpuLeakage检查与浮点指令有关的安全漏洞。
  • KiSetPageAttributesTable初始化页属性表。
  • KiConfigureInitialNodesKiConfigureProcessorBlock检查系统拓扑结构,配置初始节点和处理器块。
  • KeAddProcessorAffinityEx不知道干啥。
  • KeCompactServiceTable初始化系统服务表。
  • KiSetCacheInformation初始化CPU高速缓存信息。
  • KiInitSystem初始化内核部件全局数据结构,如蓝屏回调函数链表KeBugCheckCallbackListHead、性能堪察器列表KiProfileListHead和各种同步对象。
  • HviGetHypervisorFeatures获取VMM特征。
  • KeInitializeProcess初始化全局结构KiInitialProcess描述的初始进程。
  • KiEnableXSave通过设置IA32_XSS寄存器,索引0xDA0设置CPU浮点协处理器状态保存选项。
  • KiInitializeIdleThread创建空闲进程。
  • HalInitSystem为当前CPU做硬件抽象层初始化。
  • InitBootProcessor执行只需要启动处理器(0号处理器)执行的动作。调用每个执行体初始化函数,称为阶段0初始化。
  • KiCompleteKernelInit中:用nt!PsInitialSystemProcess把一个初始化线程附加到系统进程中让其接班,等当前线程跳入空闲循环后CPU转去执行新系统线程,开始新一轮执行体初始化;用KeInitializeDpc初始化延迟过程调用DPC对象KiProcessPendingForegroundBoostsKiTriggerForegroundBoostDpc
  • KiIdleLoop进入空闲循环休息,永不返回。

执行体的阶段0初始化

InitBootProcessor调用进程管理器的阶段0初始化函数PspInitPhase0

1
2
3
4
5
6
7
8
9
kd> k
# Child-SP RetAddr Call Site
00 fffff806`4107f528 fffff806`3f8500e8 nt!PspInitPhase0
01 fffff806`4107f530 00000000`6342694b CLFS!CClfsLogFcbPhysical::DeleteBaseFileAndContainers <PERF> (CLFS+0xe8)
02 fffff806`4107f538 fffff806`3c266f07 0x6342694b
03 fffff806`4107f540 fffff806`3c237ffd nt!InbvDriverInitialize+0x7f
04 fffff806`4107f570 fffff806`3c198fb0 nt!InitBootProcessor+0x569
05 fffff806`4107f7b0 fffff806`3c18b219 nt!KiInitializeKernel+0x610
06 fffff806`4107fad0 00000000`00000000 nt!KiSystemStartup+0x209

内核全局变量InitializationPhase记录当前哪一阶段初始化,0代表阶段0:

1
2
3: kd> dd nt!InitializationPhase L1
fffff806`3c44c078 00000000

InitBootProcessor主要动作有:

  • 解析内核启动参数字符串,寻找是否包含用于测试和验证使用的选项,如“PERFMEM”、“BURNMEMORY”、“PORCEGROUPAWARE”等。
  • RtlInitNlsTableRtlResetRtlTranslations初始化支持多语言设施。
  • WheaInitializeServices初始化WHEA服务。
  • 以参数0调用HalInitSystem
  • KeInitializeClock设置CPU时钟中断并判断是否启用动态时钟,不启用则原因写入KiDynamicTickDisableReason中,动态时钟指系统空闲时减少时钟中断次数来降低系统功耗。
  • PsInitializeQuotaSystem初始化配额管理设备。
  • CmGetSystemControlValues读取注册表系统控制数据。
  • KeInitializeTimerTable初始化定时器表。
  • ExComputeTickCountMultiplier计算时钟计数器换算因子。
  • ExInitSystem对执行体运行时库初始化。
  • KeNumaInitialize初始化NUMA有关设施。
  • VerifierInitSystem初始化内核验证器。
  • 再次用MmInitSystem初始化内存管理器,其中并内存管理器调用MiReloadBootLoadedDrivers重新加载WinLoad加载到内存中引导类型驱动程序。
  • HalInitializeBios初始化固件有关信息。
  • InbvDriverInitialize初始化系统自带显示驱动程序。
  • 若启动了内核调试,则反复用DbgLoadImageSymbols向内核调试器发送WinLoad模块加载通知。
  • HeadlessInit不知道干啥。
  • BootApplicationPersistentDataInitialize处理固件等早期启动程序希望持久化的数据。某些启动程序没有磁盘这样的持久存储设备,所以在ACPI标准中定义了接口让操作系统帮助固件保存一些信息。
  • HalQueryMaximumProcessorCount查询当前处理器组所支持的逻辑处理器总数。
  • ObInitSystem初始化对象管理器。
  • SeInitSystem初始化安全管理器。
  • PspInitPhase0使进程管理器阶段0初始化,下面详细讲。
  • DbgkInitialize初始化支持用户态调试的内核设施。

PspInitPhase0初始化进程链表结构,链表头结构记录到全局变量PsActiveProcessHead中,这也是!process命令的原理。之后创建进程和线程对象类型,有了类型后才能创建该内核类型的内核对象。KiInitializeKernelKeInitializeProcess初始化非正式进程对象KiInitialProcess,这个进程对象是直接静态变量定义的。之后又有PspInitializeJobStructuresPspInitializeSiloStructuresExCreateHandleTablePspInitializeSystemPartitionPhase等,然后用PspCreateProcess创建第一个真正的进程对象即系统进程,ID为4。然后给这个新进程赋予名字System,进程对象地址赋给全局变量PsInitialSystemProcess,把KiInitialProcess地址赋给PsIdleProcess。然后为System进程创建第一个线程,起始地址为Phase1Initialization函数。

1
2
3
4
5
6
7
8
9
fffff804`03a3aa4e 488d054b9cd5ff         lea     rax, [nt!Phase1Initialization (fffff804`037946a0)]
fffff804`03a3aa55 4533c9 xor r9d, r9d
fffff804`03a3aa58 0f1101 movups xmmword ptr [rcx], xmm0
fffff804`03a3aa5b 4889442428 mov qword ptr [rsp+28h], rax
fffff804`03a3aa60 488d4de8 lea rcx, [rbp-18h]
fffff804`03a3aa64 4533c0 xor r8d, r8d
fffff804`03a3aa67 4c89742420 mov qword ptr [rsp+20h], r14
fffff804`03a3aa6c baffff1f00 mov edx, 1FFFFFh
fffff804`03a3aa71 e81adac5ff call nt!PsCreateSystemThread (fffff804`03698490)

该线程中断请求级别IRQL较高而无法执行。在KiInitializeKernel返回后,KiSystemStartup将当前CPU的IRQL降到DISPATCH_LEVEL,并跳转到KiIdleLoop退化成空闲进程第一个空闲线程。下次发生时钟中断、内核调度线程时系统线程执行,开始阶段1初始化。虽然空闲线程属于空闲进程,但生活在系统进程的进程空间中。

执行体阶段1初始化

系统线程中第一个线程的工作函数为Phase1Initialization,冗长的部分在其中Phase1InitializationDiscard中执行,然后又调用nt!IoInitSystemnt!Phase1InitializationIoReadynt!MmFreeBootDriverInitializationCode

1
2
3
4
 # Child-SP          RetAddr               Call Site
00 ffff8309`efc06c08 fffff806`556a29a5 nt!Phase1Initialization
01 ffff8309`efc06c10 fffff806`557fc868 nt!PspSystemThreadStartup+0x55
02 ffff8309`efc06c60 00000000`00000000 nt!KiStartSystemThread+0x28

Phase1InitializationDiscardHalInitSystem再一次给HAL初始化机会,用KeInitializeClock初始化时钟和系统时间。之后开始用KeStartAllProcessors来唤醒同伴,简称KSAP。KSAP用HAL的HalEnumerateProcessors来枚举系统里所有处理器。对于每个处理器都提供IDT、PCR、ISR栈。ISR栈指中断服务例程栈,专门负责处理NMI、双误、机器检查异常等特殊中断情况,因为CPU需切换到全新线程上下文。最后KSAP用HalStartNextProcessor唤醒一个新CPU。

1
2
3
4
5
6
7
kd> k
# Child-SP RetAddr Call Site
00 ffff8309`efc06a28 fffff806`55e3880e nt!KeStartAllProcessors
01 ffff8309`efc06a30 fffff806`55b946c3 nt!Phase1InitializationDiscard+0x546
02 ffff8309`efc06be0 fffff806`556a29a5 nt!Phase1Initialization+0x23
03 ffff8309`efc06c10 fffff806`557fc868 nt!PspSystemThreadStartup+0x55
04 ffff8309`efc06c60 00000000`00000000 nt!KiStartSystemThread+0x28

唤醒后的CPU都会从内核入口开始执行,如KiInitializeKernel等,但只有第一个CPU会执行所有逻辑如KiInitSystem。初始化空闲进程也只有0号CPU执行,但每个CPU都需要用KiInitializeIdleThread为字节创建一个空闲线程。全局变量KeNumberProcessors为系统CPU个数,初始值为0,每初始化结束一个CPU该值自增1。其他CPU依次返回KiInitializeKernelKiSystemStartup直接开始KiIdleLoop执行自己的空闲线程。

1
2
3
4
5
1: kd> k
# Child-SP RetAddr Call Site
00 ffff8309`efc297a8 fffff806`55d98f86 nt!KiInitializeIdleThread
01 ffff8309`efc297b0 fffff806`55d8b219 nt!KiInitializeKernel+0x5e6
02 ffff8309`efc29ad0 00000000`00000000 nt!KiSystemStartup+0x209

下一步进行I/O初始化,即建立设备树,枚举系统各种设备并加载驱动程序。总线驱动程序还需要进一步枚举自己的子设备。例如观察设备树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
3: kd> !devnode
Dumping IopRootDeviceNode (= 0xffff880670108c30)
DevNode 0xffff880670108c30 for PDO 0xffff880670168e10
Parent 0000000000 Sibling 0000000000 Child 0xffff88067010ac30
InstancePath is "HTREE\ROOT\0"
State = DeviceNodeStarted (0x308)
Previous State = DeviceNodeEnumerateCompletion (0x30d)
StateHistory[09] = DeviceNodeEnumerateCompletion (0x30d)
StateHistory[08] = DeviceNodeEnumeratePending (0x30c)
StateHistory[07] = DeviceNodeStarted (0x308)
StateHistory[06] = DeviceNodeEnumerateCompletion (0x30d)
StateHistory[05] = DeviceNodeEnumeratePending (0x30c)
StateHistory[04] = DeviceNodeStarted (0x308)
StateHistory[03] = DeviceNodeEnumerateCompletion (0x30d)
StateHistory[02] = DeviceNodeEnumeratePending (0x30c)
StateHistory[01] = DeviceNodeStarted (0x308)
StateHistory[00] = DeviceNodeUninitialized (0x301)
StateHistory[19] = Unknown State (0x0)
StateHistory[18] = Unknown State (0x0)
StateHistory[17] = Unknown State (0x0)
StateHistory[16] = Unknown State (0x0)
StateHistory[15] = Unknown State (0x0)
StateHistory[14] = Unknown State (0x0)
StateHistory[13] = Unknown State (0x0)
StateHistory[12] = Unknown State (0x0)
StateHistory[11] = Unknown State (0x0)
StateHistory[10] = Unknown State (0x0)
Flags (0x00000131) DNF_MADEUP, DNF_ENUMERATED,
DNF_IDS_QUERIED, DNF_NO_RESOURCE_REQUIRED
UserFlags (0x0000000a) DNUF_DONT_SHOW_IN_UI, DNUF_NOT_DISABLEABLE
CapabilityFlags (0x000001c0) UniqueID, SilentInstall,
RawDeviceOK
DisableableDepends = 5 (including self)

设备树以及子节点在注册表中HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root,一级子节点中必然有ACPI_HAL。如ACPI_HAL的Service键值为“\Driver\ACPI_HAL”,告诉I/O管理器为该设备安装ACPI驱动程序ACPI.sys。该驱动程序读取系统固件中设备表并枚举,分别安装驱动程序。其中PCI总线设备会触发加载其驱动PCL.sys,加载后按照PCI协议枚举PCI设备如子总线控制器、USB总线控制器等,由此不断加入设备树。该I/O阶段1初始化 的函数为IoInitSystem,其中IoInitSystemPreDrivers负责初始化内建的和启动类型的驱动程序,IopInitializeSystemDrivers初始化系统类型驱动程序。

nt!InbvIndicateProgress返回启动总进程,但功能几乎丧失。

至此内核空间初始化完毕。执行体的阶段1初始化结束前,Phase1Initialization创建第一个用映像文件创建的进程,即会话管理器进程SMSS.EXE。

创建用户空间

StartFirstUserProcess创建会话管理器进程,SMSS会话管理器程序全程叫会话管理器子系统。

1
2
3
4
5
6
7
8
9
0: kd> k
# Child-SP RetAddr Call Site
00 ffff838b`07206948 fffff800`28a67e61 nt!RtlpCreateUserProcess
01 ffff838b`07206950 fffff800`28a67d4a nt!RtlCreateUserProcessEx+0x6d
02 ffff838b`07206990 fffff800`28a3d469 nt!StartFirstUserProcess+0x136
03 ffff838b`07206a70 fffff800`287946ee nt!Phase1InitializationIoReady+0x139
04 ffff838b`07206be0 fffff800`282a29a5 nt!Phase1Initialization+0x4e
05 ffff838b`07206c10 fffff800`283fc868 nt!PspSystemThreadStartup+0x55
06 ffff838b`07206c60 00000000`00000000 nt!KiStartSystemThread+0x28

失败则触发0x6F蓝屏SESSION3_INITIALIZATION_FAILED。Phase1Initialization通过等待SMSS进程句柄确保它持续运行。等待超时则SMSS进程还在运行,否则SMSS意外退出,这时引发0x71号蓝屏SESSION5_INITIALIZAION_FAILED。等待SMSS几秒没发现退出则确信正常启动了。此后Phase1Initialization直接返回导致该线程退出。

SMSS初始化后创建一个LPC端口\SmApiPort用于对外提供服务,如切换或建立新会话请求。注册表中配置SMSS的表键为HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ControlSet\Control\Session Manager。其中PendingFileRenameOperations子键记录之前挂起的文件删除/改名任务。然后执行BootExecute表键的命令,一般是磁盘检查程序autochk.exe。之后SMSS建立虚拟内存机制所需的页面交换文件。然后建立SubSystems表键下的环境子系统,先加载Win32K.sys,再根据键值内容创建Windows子系统服务进程CSRSS.exe,如下:

1
%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

ServerDll是CSRSS要加载的服务模块,病毒或恶意软件可能会把自己模块加进来。

建立Windows子系统后,SMSS创建WinLoad,负责安全登录工作,掌控登录、重启、关机和屏幕保护程序等。

WinLogon创建0号窗口站和默认桌面对象。窗口站是会话的下一层组织结构,一个会话中可有多个窗口站,同一时刻每个会话只有一个窗口站可与用户交互。每个窗口站有自己的剪贴板,可有多个桌面。WinLogon用Windows子系统内核模块Win32K服务的NtUserCreateWindowStation创建窗口站,之后用NtUserCreateDesktop创建桌面。首先创建一个WinLogon桌面供自己使用,再创建Default桌面供应用程序使用。之后WinLogon用SetActiveDesktop将自己桌面设为当前活动桌面,于是登录桌面便呈现在用户面前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0: kd> sxe ld:win32k
0: kd> g
nt!DebugService2+0x5:
fffff804`78dfd105 cc int 3
3: kd> x win32k!NtUserCreateWindowStation
fffff8d8`4186e48c win32k!NtUserCreateWindowStation (NtUserCreateWindowStation)
3: kd> bp win32k!NtUserCreateWindowStation
WARNING: Software breakpoints on session addresses can cause bugchecks.
Use hardware execution breakpoints (ba e) if possible.
1: kd> k
# Child-SP RetAddr Call Site
00 ffffa604`f25bfa88 fffff804`78e06bb5 win32k!NtUserCreateWindowStation
01 ffffa604`f25bfa90 00007ff9`82fe8844 nt!KiSystemServiceCopyEnd+0x25
02 000000d4`fec8f238 00007ff9`8384d2c9 0x00007ff9`82fe8844
03 000000d4`fec8f240 00000000`00000000 0x00007ff9`8384d2c9
1: kd> x win32k!NtUserCreateDesktopEx
fffff8d8`4186dd98 win32k!NtUserCreateDesktopEx (NtUserCreateDesktopEx)
1: kd> bp win32k!NtUserCreateDesktopEx
WARNING: Software breakpoints on session addresses can cause bugchecks.
Use hardware execution breakpoints (ba e) if possible.
1: kd> g
Breakpoint 5 hit
win32k!NtUserCreateDesktopEx:
fffff8d8`4186dd98 4883ec48 sub rsp,48h
2: kd> k
# Child-SP RetAddr Call Site
00 ffffa604`f25bfa88 fffff804`78e06bb5 win32k!NtUserCreateDesktopEx
01 ffffa604`f25bfa90 00007ff9`82fe87a4 nt!KiSystemServiceCopyEnd+0x25
02 000000d4`fec8f798 00007ff9`8385c261 0x00007ff9`82fe87a4
03 000000d4`fec8f7a0 00000000`00000000 0x00007ff9`8385c261

当用户退出或锁定屏幕时,WinLogon将自己桌面切到前台(设为活动的),该桌面又称安全桌面。创建桌面后WinLogon创建服务管理器Services.exe和本地安全认证子系统LSASS.exe。

Credential Provider模型接收用户登录信息,如用户名、密码或指纹,与LSASS进程交互。接下来WinLogon引发用户初始化动作,执行“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\UserInit”,内容一般是UserInit程序:

1
C:\Windows\system32\userinit.exe,

UserInit启动后运行“HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Windows\SystemScripts”和“HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\System\Scripts”表键下定义的登录脚本。接下来UserInit启动外壳程序,先后在“HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon”和“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon”表键中找Shell键值,内容是Explorer.exe。