当前位置: 首页 > news >正文

从 Phase 1 初始化到 SMSS 进程启动分析

从 Phase 1 初始化到 SMSS 进程启动分析

概述

本文档描述 ReactOS 内核在 Phase 1 初始化完成后,如何创建并切换到 SMSS.EXE(会话管理器子系统)进程运行。核心机制是利用线程调度器的上下文切换,通过iret指令从 Ring 0 切换到 Ring 3,将控制权交给用户态进程。


1. Phase 1 初始化入口

Phase1Initialization是一个系统线程,由PspInitPhase0在 Phase 0 末尾创建:

KiSystemStartup() └─ KiInitializeKernel() └─ ExpInitializeExecutive() └─ PspInitPhase0() └─ PsCreateSystemThread(Phase1Initialization)

KiIdleLoop通过调度器选中该线程时,执行进入Phase1Initialization([ex/init.c#L2058](file:///d:/reactos/ntoskrnl/ex/init.c#L2058)):

Phase1Initialization(IN PVOID Context){Phase1InitializationDiscard(Context);// 执行初始化核心逻辑MmZeroPageThread();// 完成后进入零页线程}

Phase1InitializationDiscard在第 2017 行调用最关键的函数:

ExpLoadInitialProcess(InitBuffer,&ProcessParameters,&Environment);

2. ExpLoadInitialProcess — 创建 SMSS 进程

文件:[ex/init.c#L389](file:///d:/reactos/ntoskrnl/ex/init.c#L389)

2.1 准备 ProcessParameters(第 408-527 行)

  • 分配RTL_USER_PROCESS_PARAMETERS结构
  • NtInitialUserProcessBuffer读取 SMSS 的映像路径
  • 构造命令行、环境变量(Path=, SystemDrive=, SystemRoot=)

2.2 调用 RtlCreateUserProcess(第 557-580 行)

SmssName=ProcessParams->ImagePathName;Status=RtlCreateUserProcess(&SmssName,OBJ_CASE_INSENSITIVE,RtlDeNormalizeProcessParams(ProcessParams),NULL,NULL,NULL,FALSE,NULL,NULL,ProcessInformation);

RtlCreateUserProcess内部依次调用:

RtlCreateUserProcess() ├─ NtCreateProcessEx() → PspCreateProcess() 创建 EPROCESS └─ NtCreateThread() → PspCreateThread() 创建主线程(CREATE_SUSPENDED)

2.3 恢复线程运行(第 582-596 行)

Status=ZwResumeThread(ProcessInformation->ThreadHandle,NULL);

使 SMSS 主线程变为可调度状态。

2.4 等待 SMSS 初始化(第 2019-2033 行)

Timeout.QuadPart=Int32x32To64(5,-10000000);// 5 秒Status=ZwWaitForSingleObject(ProcessInfo->ProcessHandle,FALSE,&Timeout);if(Status==STATUS_SUCCESS)KeBugCheck(SESSION5_INITIALIZATION_FAILED);

5 秒超时等待。如果 SMSS 在 5 秒内退出(Status == SUCCESS),触发 BugCheck。


3. PspCreateThread — 线程创建核心

文件:[ps/thread.c#L172](file:///d:/reactos/ntoskrnl/ps/thread.c#L172)

3.1 创建 ETHREAD 对象

Status=ObCreateObject(...,sizeof(ETHREAD),...,(PVOID*)&Thread);

3.2 用户线程 vs 系统线程分支

用户线程(SMSS)系统线程(Phase1)
SystemRoutinePspUserThreadStartupPspSystemThreadStartup
StartRoutineNULL实际 C 函数
StartContextThreadContext.Eip传入参数
TEB
// 用户线程(第 346-360 行)Thread->StartAddress=KeGetContextPc(ThreadContext);Status=KeInitThread(&Thread->Tcb,NULL,PspUserThreadStartup,// SystemRoutineNULL,// StartRoutine = NULLThread->StartAddress,// StartContext = EIPThreadContext,TebBase,&Process->Pcb);

3.3 启动线程

KeStartThread(&Thread->Tcb);// 设置优先级、亲和性KeReadyThread(&Thread->Tcb);// 插入就绪队列 → 可调度

4. KiInitializeContextThread — 内核栈上的初始化帧布局

文件:[ke/i386/thrdini.c#L92](file:///d:/reactos/ntoskrnl/ke/i386/thrdini.c#L92)

这是最关键的一步。它在线程的内核栈上布置特殊的初始化帧,使得调度器在第一次上下文切换到该线程时,能够自动进入用户态。

4.1 KUINIT_FRAME 结构(用户线程)

InitialStack (栈顶/最高地址) ↓ ┌─────────────────────────┐ ← 初始栈指针 (最高地址) │ (栈空间) │ ├─────────────────────────┤ │ FX_SAVE_AREA │ FPU/MMX/SSE 保存区 (0x210 字节) ├─────────────────────────┤ │ KTRAP_FRAME │ 陷阱帧 — 保存完整 CPU 上下文 (0x8C 字节) │ ├─ Eip = SMSS 入口 │ │ ├─ SegCs = 0x1B │ ← KGDT_R3_CODE | RPL_MASK │ ├─ SegSs = 0x23 │ ← KGDT_R3_DATA | RPL_MASK │ ├─ HardwareEsp │ 用户栈指针 │ └─ EFlags │ 用户态 EFLAGS ├─────────────────────────┤ │ KSTART_FRAME │ 启动帧 │ ├─ SystemRoutine │ = PspUserThreadStartup │ ├─ StartRoutine │ = NULL │ ├─ StartContext │ = SMSS 入口点 (EIP) │ └─ UserThread │ = TRUE ├─────────────────────────┤ │ KSWITCHFRAME │ 上下文切换帧 │ ├─ RetAddr │ = KiThreadStartup ← 切换后返回地址 │ ├─ ApcBypassDisable │ = TRUE │ └─ ExceptionList │ = EXCEPTION_CHAIN_END └─────────────────────────┘ ← Thread->KernelStack = &CtxSwitchFrame

关键设置(第 170-203 行):

// 从 CONTEXT 转换到 KTRAP_FRAMEKeContextToTrapFrame(Context,NULL,TrapFrame,Context->ContextFlags|ContextFlags,UserMode);// 段寄存器设为 Ring 3TrapFrame->HardwareSegSs|=RPL_MASK;// SS = 0x23TrapFrame->SegDs|=RPL_MASK;TrapFrame->SegEs|=RPL_MASK;// CS 在 KeContextToTrapFrame 中已设为 0x1B// CtxSwitchFrame.RetAddr = KiThreadStartupCtxSwitchFrame->RetAddr=KiThreadStartup;CtxSwitchFrame->ApcBypassDisable=TRUE;CtxSwitchFrame->ExceptionList=EXCEPTION_CHAIN_END;// 保存内核栈指针Thread->KernelStack=(PVOID)CtxSwitchFrame;

5. 调度器上下文切换 — 首次切换到 SMSS

KiIdleLoop或调度器选中 SMSS 线程时,执行上下文切换:

5.1 KiSwapContext 汇编([ctxswitch.S](file:///d:/reactos/ntoskrnl/ke/i386/ctxswitch.S))

KiIdleLoop (或 KiDispatchInterrupt) └─ KiSwapContext(APC_LEVEL, OldThread) └─ KiSwapContextInternal └─ KiSwapContextEntry(KSWITCHFRAME, OldThread~) 1. 保存 old_esp → OldThread->KernelStack 2. 处理 FPU 上下文 └─ KiSwitchThreads(OldThread, NewThread->KernelStack) mov esp, NewThread->KernelStack ← ★ 栈切换 # ESP 现在指向 SMSS 的 KSWITCHFRAME KiSwapContextExit ├─ 检查进程切换: OldProcess != NewProcess │ └─ mov cr3, NewProcess->DirectoryTableBase ← 切换地址空间 ├─ TSS.Esp0 = NewThread->InitialStack ├─ TEB 设置 └─ 异常链 恢复非易失寄存器 ret ──→ KiThreadStartup (因为 RetAddr = KiThreadStartup)

6. KiThreadStartup — 线程启动桩

文件:[ke/i386/thrdini.c#L63](file:///d:/reactos/ntoskrnl/ke/i386/thrdini.c#L63)

VOID NTAPIKiThreadStartup(VOID){InitFrame=KeGetCurrentThread()->KernelStack;// 拿到 CtxSwitchFrameStartFrame=&InitFrame->StartFrame;TrapFrame=&InitFrame->TrapFrame;KfLowerIrql(APC_LEVEL);// 对于 SMSS: 调用 PspUserThreadStartup(NULL, SMSS_EntryPoint)StartFrame->SystemRoutine(StartFrame->StartRoutine,StartFrame->StartContext);if(!StartFrame->UserThread)KeBugCheck(NO_USER_MODE_CONTEXT);// 通过 iret 切换到用户态KiServiceExit2(TrapFrame);// ---- 永不返回 ----}

6.1 PspUserThreadStartup — 排队 LdrInitializeThunk APC

文件:[ps/thread.c#L26](file:///d:/reactos/ntoskrnl/ps/thread.c#L26)

VOID NTAPIPspUserThreadStartup(PKSTART_ROUTINE StartRoutine,PVOID StartContext){KeLowerIrql(PASSIVE_LEVEL);Thread=PsGetCurrentThread();// 通知调试器DbgkCreateThread(Thread,StartContext);// ★ 排队 LdrInitializeThunk 作为用户 APCKiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),KeGetTrapFrame(&Thread->Tcb),// KTRAP_FRAMELdrInitializeThunk,// ntdll 入口NULL,PspSystemDllBase,NULL);}

LdrInitializeThunk是 ntdll.dll 的用户态初始化函数。它被排队成一个用户 APC(异步过程调用),将在KiServiceExit2KiCommonExitKiCheckForApcDelivery中被传递。


7. KiServiceExit2 — iret 到用户态

文件:[ke/i386/traphdlr.c#L189](file:///d:/reactos/ntoskrnl/ke/i386/traphdlr.c#L189)

DECLSPEC_NORETURN VOID FASTCALLKiServiceExit2(IN PKTRAP_FRAME TrapFrame){KiCommonExit(TrapFrame,FALSE);// 关闭中断、传递 APC、恢复异常链// 检查 CS 的 CPL 位if(TrapFrame->SegCs&MODE_MASK)TRUE(CS=0x1B,CPL=3)// 进入用户态出口路径KiTrapReturn(TrapFrame);}

7.1 KiTrapReturn([asmmacro.S](file:///d:/reactos/ntoskrnl/include/internal/i386/asmmacro.S#L313))

KiTrapReturn: lea esp, [ecx + KTRAP_FRAME_EIP] ; ESP 指向 TrapFrame.Eip ; 恢复非易失寄存器 mov ebx, ...; mov esi, ...; mov edi, ...; mov ebp, ... ; 恢复段寄存器 mov ds, ...; mov es, ...; mov gs, ...; mov fs, ... ; 此时栈顶布局 (从低到高): ; [EIP] = LdrInitializeThunk (ntdll 入口) ; [SegCs] = 0x1B (Ring 3 代码段) ; [EFlags] = 用户态 EFLAGS ; [HardwareEsp] = 用户态栈指针 ; [HardwareSegSs] = 0x23 (Ring 3 数据段) iretd ; ↑ 弹出 EIP, CS, EFLAGS, ESP, SS ; CS.RPL = 3 → CPU 从 Ring 0 切换到 Ring 3 ; 开始执行 LdrInitializeThunk

8. 最终状态

资源
CS0x1BKGDT_R3_CODE | RPL_MASK
SS0x23KGDT_R3_DATA | RPL_MASK
EIPLdrInitializeThunk(ntdll 初始化入口)
ESPSMSS 用户栈指针
CR3SMSS 进程的页目录(地址空间已切换)
TSS.Esp0SMSS 线程的InitialStack(下次系统调用入口)
PreviousModeUserMode

SMSS.EXE 在 ntdll 初始化完成后调用它的main入口点,开始作为会话管理器运行。


9. 完整调用链

Phase1InitializationDiscard() [ex/init.c] └─ ExpLoadInitialProcess() [ex/init.c:389] └─ RtlCreateUserProcess() ├─ NtCreateProcessEx() → PspCreateProcess() [ps/process.c:347] │ └─ EPROCESS 创建 + 地址空间初始化 └─ NtCreateThread() → PspCreateThread() [ps/thread.c:172] └─ KeInitThread() → KiInitializeContextThread() [ke/i386/thrdini.c:92] └─ 布局 KUINIT_FRAME: KTRAP_FRAME: CS=0x1B, EIP=SMSS入口 KSWITCHFRAME.RetAddr = KiThreadStartup Thread->KernelStack = &CtxSwitchFrame └─ KeStartThread() + KeReadyThread() [调度器切换] KiIdleLoop() └─ KiSwapContext() └─ KiSwitchThreads(mov esp, NewThread->KernelStack) └─ KiSwapContextExit (CR3 切换, TSS.Esp0, TEB) └─ ret → KiThreadStartup() [ke/i386/thrdini.c:63] └─ PspUserThreadStartup() [ps/thread.c:26] └─ KiInitializeUserApc(LdrInitializeThunk) [排队 APC] └─ KiServiceExit2(TrapFrame) [ke/i386/traphdlr.c:189] └─ KiCommonExit() └─ KiCheckForApcDelivery → 执行 LdrInitializeThunk └─ KiTrapReturn() └─ iretd └─ ★ CS.RPL=3 → Ring 3 EIP = LdrInitializeThunk ESP = 用户栈 → SMSS.EXE 开始运行

关键设计要点

  1. 栈上的初始化帧— KUINIT_FRAME 包含 CtxSwitchFrame + StartFrame + TrapFrame,InitialStackKernelStack的偏移决定了这些帧的位置

  2. iret 的 Ring 切换KTRAP_FRAME.SegCs = 0x1B(CPL=3)导致iretd自动从 Ring 0 切换到 Ring 3

  3. 用户 APC 传递PspUserThreadStartup排队LdrInitializeThunk,在KiCommonExit中通过KiCheckForApcDelivery在实际的iret前完成传递

  4. 地址空间切换KiSwapContextExit在切换到新线程时设置 CR3,使 SMSS 运行在自己的地址空间中

  5. TSS.Esp0— 设置为InitialStack,确保 SMSS 在用户态执行系统调用(int 0x2E/sysenter)时,CPU 自动切换到正确的内核栈

http://www.jsqmd.com/news/1218215/

相关文章:

  • 3个维度教你选对深圳企业AI搜索排名优化服务商:2026年GEO推荐榜单
  • 嵌入式低功耗设计:硬件引脚状态管理与唤醒机制深度解析
  • VB6实现超大文件断点下载
  • Android水滴下拉刷新控件CircleRefreshLayout实现与优化
  • 嵌入式电源管理三域架构:电压域、功率域与时钟域协同设计
  • Android定时器实现方案对比与选型指南
  • 百达翡丽中国官方售后服务中心|全新服务热线及详细维修地址权威信息公告(2026年7月更新) - 百达翡丽服务中心
  • 天梭中国官方售后服务中心|服务热线及门店地址权威信息通告(2026年7月更新) - 天梭服务中心
  • 面向工业级落地的ML模型开发提效工具链设计
  • x86与ARM嵌入式处理器核心技术对比与应用实践
  • Django用户认证组件深度解析与实战应用
  • 理论篇| Loop Engineering综述
  • 最小曼哈顿距离法
  • 基于YOLO的人脸表情识别系统开发与部署实战
  • Npcap安装失败Failed to create the npcap service:0x8007007e
  • 越降重重复率越高?2026论文降重致命误区|PaperXie精准控重,不毁逻辑不翻车
  • TI UART3模块CIR模式配置详解:从寄存器到红外遥控实战
  • 宝珀中国官方售后服务中心|官方电话及详细维修地址权威信息声明(2026年7月最新) - 宝珀官方售后服务中心
  • 欧米茄中国专柜热线2026年7月最新官方,客服服务电话客户必读 - 欧米茄官方服务中心
  • 数字电路设计与Logisim仿真工具入门指南
  • C++并发编程实战指南:从线程基础到无锁优化与性能调优
  • 基于YOLOv8的行人跌倒检测系统设计与优化
  • Python内置模块详解与应用实践
  • Sqribble:面向内容创作者的云原生文档操作系统
  • 人脸识别技术解析与主流平台对比实践
  • 开题直接被打回重写?2026开题报告严查标准|PaperXie一键搞定高分开题
  • 2015年Android开发工具链与框架演进回顾
  • ARM嵌入式RTC时钟模块配置与调试实战
  • 有客户端形式的AI短篇小说写作软件吗?原来这几款值得一试!
  • 深入理解编程中的局部变量与全局变量