从 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, SystemRoot2.2 调用 RtlCreateUserProcess第 557-580 行SmssNameProcessParams-ImagePathName;StatusRtlCreateUserProcess(SmssName,OBJ_CASE_INSENSITIVE,RtlDeNormalizeProcessParams(ProcessParams),NULL,NULL,NULL,FALSE,NULL,NULL,ProcessInformation);RtlCreateUserProcess内部依次调用RtlCreateUserProcess() ├─ NtCreateProcessEx() → PspCreateProcess() 创建 EPROCESS └─ NtCreateThread() → PspCreateThread() 创建主线程CREATE_SUSPENDED2.3 恢复线程运行第 582-596 行StatusZwResumeThread(ProcessInformation-ThreadHandle,NULL);使 SMSS 主线程变为可调度状态。2.4 等待 SMSS 初始化第 2019-2033 行Timeout.QuadPartInt32x32To64(5,-10000000);// 5 秒StatusZwWaitForSingleObject(ProcessInfo-ProcessHandle,FALSE,Timeout);if(StatusSTATUS_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 对象StatusObCreateObject(...,sizeof(ETHREAD),...,(PVOID*)Thread);3.2 用户线程 vs 系统线程分支用户线程SMSS系统线程Phase1SystemRoutinePspUserThreadStartupPspSystemThreadStartupStartRoutineNULL实际 C 函数StartContextThreadContext.Eip传入参数TEB有无// 用户线程第 346-360 行Thread-StartAddressKeGetContextPc(ThreadContext);StatusKeInitThread(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-RetAddrKiThreadStartup;CtxSwitchFrame-ApcBypassDisableTRUE;CtxSwitchFrame-ExceptionListEXCEPTION_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){InitFrameKeGetCurrentThread()-KernelStack;// 拿到 CtxSwitchFrameStartFrameInitFrame-StartFrame;TrapFrameInitFrame-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);ThreadPsGetCurrentThread();// 通知调试器DbgkCreateThread(Thread,StartContext);// ★ 排队 LdrInitializeThunk 作为用户 APCKiInitializeUserApc(KeGetExceptionFrame(Thread-Tcb),KeGetTrapFrame(Thread-Tcb),// KTRAP_FRAMELdrInitializeThunk,// ntdll 入口NULL,PspSystemDllBase,NULL);}LdrInitializeThunk是 ntdll.dll 的用户态初始化函数。它被排队成一个用户 APC异步过程调用将在KiServiceExit2→KiCommonExit→KiCheckForApcDelivery中被传递。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-SegCsMODE_MASK)←TRUE(CS0x1B,CPL3)// 进入用户态出口路径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 ; 开始执行 LdrInitializeThunk8. 最终状态资源值CS0x1BKGDT_R3_CODE | RPL_MASKSS0x23KGDT_R3_DATA | RPL_MASKEIPLdrInitializeThunkntdll 初始化入口ESPSMSS 用户栈指针CR3SMSS 进程的页目录地址空间已切换TSS.Esp0SMSS 线程的InitialStack下次系统调用入口PreviousModeUserModeSMSS.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: CS0x1B, EIPSMSS入口 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.RPL3 → Ring 3 EIP LdrInitializeThunk ESP 用户栈 → SMSS.EXE 开始运行关键设计要点栈上的初始化帧— KUINIT_FRAME 包含 CtxSwitchFrame StartFrame TrapFrameInitialStack到KernelStack的偏移决定了这些帧的位置iret 的 Ring 切换—KTRAP_FRAME.SegCs 0x1BCPL3导致iretd自动从 Ring 0 切换到 Ring 3用户 APC 传递—PspUserThreadStartup排队LdrInitializeThunk在KiCommonExit中通过KiCheckForApcDelivery在实际的iret前完成传递地址空间切换—KiSwapContextExit在切换到新线程时设置 CR3使 SMSS 运行在自己的地址空间中TSS.Esp0— 设置为InitialStack确保 SMSS 在用户态执行系统调用int 0x2E/sysenter时CPU 自动切换到正确的内核栈