Windows NT引导过程源代码分析

Windows 引导过程

Windows 内核中的各个组件和各种机制在起作用以前,必须首先被初始化。此初始化工作是在系统引导时完成的。当用户打开计算机的电源开关时,计算机便开始运行,但操作系统并不立即获得控制权,而是BIOS 代码首先获得控制,它执行必要的硬件检测工作,并允许用户通过一些功能键来配置当前系统中的硬件设置,甚至诊断硬件问题,然后才将控制权交给操作系统。

1.1 内核加载

在Intel x86 系统上,Windows 操作系统获得控制首先从硬盘的主引导记录(MBR,Master Boot Record)开始,Windows Setup 程序在安装Windows 时填充MBR(其他的磁盘管理程序也可能填充MBR)。MBR 包含代码和数据,其代码称为引导代码,在系统引导时首先获得控制;MBR 中的数据是一张分区表,指定了每个分区在磁盘上的位置和大小,以及分区的类型。当MBR 中的引导代码被执行时,它检查分区表中的每一个分区,若找到一个已被标记为可引导的分区(称为引导分区),则将该分区的第一个扇区(称为引导扇区)读到内存中。由于分区表包含了每一个分区的磁盘位置,所以,引导扇区的位置很容易被确定。然后MBR 的代码将控制权交给引导扇区中的代码。

;此处代码摘自NT4代码的\private\ntos\boot\bootcode\x86mboot.asm

relocated_org   equ 0600h

buildtime_org   equ 0100h

org_delta       equ (relocated_org - buildtime_org)

_data   segment public

        assume  cs:_data,ds:_data

        org buildtime_org

; 这段代码读出位于主引导记录末尾的分区表,找到标志为可引导的分区,把它的引导扇区拷贝到内存中并执行

start:

        cli             ;开始的时候并没有中断

        xor ax,ax

        mov ss,ax

        mov sp,7c00h    ;位于地址0:7c00处的新堆栈

        mov si,sp        ; 0:7c00为标准引导地址  

        push ax

        pop es          

        push ax        

        sti             ;允许中断

        cld

        mov di,relocated_org         mov cx,100h

        rep movsw       

 ;重定位到地址 0000:0600,跳到这里从分区表中读取可引导分区的入口,把引导分区拷贝到内存的标准引导地址(0000:7C00)

;       jmp entry2 + org_delta

        db   0eah

        dw   $+4+org_delta,0

entry2:

        mov si,(offset tab) + org_delta  ;表示分区表

        mov bl,4        ;分区表项的个数

next:

        cmp byte ptr[si],80h  ;判断是否是可以引导的入口

        je boot         ;yes

        cmp byte ptr[si],0    ;再次判断是否为0

        jne bad         ;不是,只有 x"00" 或者x"80" 是有效的

        add si,16       ;执行到下一个入口点

        dec bl

        jnz next

        int 18h         ;未检测到可引导的入口,返回

boot:

        mov dx,[si]     ;引导开始处

        mov cx,[si+2]  

        mov bp,si       ;保存表入口地址并传给分区引导记录

next1:

        add si,16       ;下一个表项

        dec bl          ;表项数目递减

        jz tabok        

        cmp byte ptr[si],0    ;所有剩余的表入口都要从0开始

        je next1        ;满足上述判断条件

bad:

        mov si,(offset m1) + org_delta ;无法找到一个从0开始的表项入口,该表为坏表

msg:

        lodsb           ;获取显示信息的字符

        cmp al,0

        je  hold

        push si

        mov bx,7

        mov ah,14

        int 10h          ;显示信息

        pop si

        jmp msg         ;循环打印完整信息

hold:   jmp hold        ;此处自旋,不做任何事

tabok:

        mov di,5        ;计数值

rdboot:

        mov bx,7c00h    ;读取系统引导记录的位置

        mov ax,0201h    ;读取一个扇区

        push di

        int 13h          ; 获取引导记录

        pop di

        jnc goboot      ;成功得到引导记录,交与控制权

        xor ax,ax        ;出现错误

        int 13h          ;重新校准

        dec di           ;递减计数值

        jnz rdboot       ;只要计数值仍大于0,就继续尝试

        mov si,(offset m2) + org_delta ;所有的入口都已检测完毕,错误无法避免

        jmp msg          ;跳转到显示错误信息

goboot:

        mov si,(offset m3) + org_delta 

        mov di,07dfeh

        cmp word ptr [di],0aa55h ;判断引导记录是否有效

        jne msg         ;无效,则显示无效的系统引导记录信息

        mov si,bp       ;有效,则将分区表入口地址传给它

        db 0eah

        dw 7c00h,0

include x86mboot.msg 

        org 2beh     ;此处显示了主引导记录的结构

tab:                     ;partition table

        dw 0,0          ;partition 1 begin

        dw 0,0          ;partition 1 end

        dw 0,0          ;partition 1 relative sector (low, high parts)

        dw 0,0          ;partition 1 # of sectors (low, high parts)

        dw 0,0          ;partition 2 begin

        dw 0,0          ;partition 2 end

        dw 0,0          ;partition 2 relative sector

        dw 0,0          ;partition 2 # of sectors

        dw 0,0          ;partition 3 begin

        dw 0,0          ;partition 3 end

        dw 0,0          ;partition 3 relative sector

        dw 0,0          ;partition 3 # of sectors

        dw 0,0          ;partition 4 begin

        dw 0,0          ;partition 4 end

        dw 0,0          ;partition 4 relative sector

        dw 0,0          ;partition 4 # of sectors

signa   db 55h,0aah     ;引导区有效签名值

_data   ends

        end  start

Windows Setup 程序在确定了要将Windows 系统安装到哪个分区中以后,除了可能会写入MBR 以外,还会写入该分区的引导扇区。所以,严格意义上讲,Windows 操作系统的真正入口点应该是引导扇区中的代码。引导分区必须被格式化成Windows 所支持的文件系统,典型的文件系统格式是NTFS 和FAT,其中NTFS 是Windows NT 的原生文件系统,而FAT 则是从MS-DOS 时代继承和发展过来的。

引导扇区中的代码随硬盘文件系统格式的不同而有所不同,其职责是,给Windows提供有关该硬盘上卷的结构和格式方面的信息,并且从该卷的根目录中读入Windows 的加载程序,即ntldr 文件;然后将控制权交给ntldr 的入口函数。为了能够从根目录中读入加载程序,引导扇区包含了能理解文件系统结构和读取文件的代码,这通常只是文件系统极其简单的一部分功能,而并非完整的实现。尽管引导扇区的职责相对简单,但是单个扇区(512 B)的代码和数据往往不足以完成其功能,为此,Windows 的做法是,让引导扇区中的代码读入其他扇区的数据,然后跳转到下一个扇区的代码区。这样就可以不受单个引导扇区长度的限制,这种做法相当于将第一个引导扇区当做一个加载器(loader),而真正完成引导扇区功能的扇区随后被加载进来并执行。这一过程对于MBR 是透明的,从而保持良好的兼容性。

; 此处代码摘自NT4代码的\private\ntos\boot\bootcode\ntfs\i386\ntfsboot.asm

MASM    equ     1

        .xlist

        .286

A_DEFINED EQU 1

    include ntfs.inc

DoubleWord      struc

lsw     dw      ?

msw     dw      ?

DoubleWord      ends

; 下面的代码显示了几个引导加载器使用的不同区段,最开始的两个分别是引导扇区最先加载的位置以及之后重定位的位置

; 第三个则是NTLDR加载的静态地址

BootSeg segment at 07c0h          ; ROM 起先加载的位置.

BootSeg ends

NewSeg  segment at 0d00h        重定位的位置.

NewSeg  ends                     

LdrSeg segment at 2000h           将要在地址 2000:0000处加载加载器

LdrSeg ends

;/********************** START OF SPECIFICATIONS ************************/

;/*                                                                   */

;/* SUBROUTINE NAME: ntfsboot                                      */

;/*                                                                    */

;/* DESCRIPTIVE NAME: Bootstrap loader                               */

;/*                                                                     */

;/* FUNCTION:    To load NTLDR into memory.                             */

;/*                                                                     */

;/* NOTES:       ntfsboot is loaded by the ROM BIOS (Int 19H) at        */

;/*              physical memory location 0000:7C00H.                   */

;/*              ntfsboot runs in real mode.                            */

;/*              This boot record is for NTFS volumes only.             */

;/*                                                                     */

;/* ENTRY POINT: ntfsboot                                               */

;/* LINKAGE:     Jump (far) from Int 19H                                */

;/*                                                                     */

;/* INPUT:       CS:IP = 0000:7C00H                                     */

;/*              SS:SP = 0030:00FAH (CBIOS dependent)                   */

;/*                                                                     */

;/* EXIT-NORMAL: DL = INT 13 drive number we booted from                */

;/*              Jmp to main in NTLDR                                   */

;/*                                                                     */

;/* EXIT-ERROR:  None                                                   */

;/*                                                                     */

;/* EFFECTS:     NTLDR is loaded into the physical memory               */

;/*                location 00020000H                                   */

;/* MESSAGES:    A disk read error occurred.                            */

;/*              The file NTLDR cannot be found.                        */

;/*              Insert a system diskette and restart the system.       */

BootCode segment        

        assume  cs:BootCode,ds:nothing,es:nothing,ss:nothing

        org     0               

        public  _ntfsboot

_ntfsboot proc   far

        jmp     start

    .errnz  ($-_ntfsboot) GT (3),<FATAL PROBLEM: JMP is more than three bytes>

    org 3

;  这是一个参数块的模版  任何调用者将引导代码写入磁盘以后都应该保存一个存在的参数块以及NTFS信息或者创建一个新的

Version                 db      "NTFS    "      ; Must be 8 characters

BPB                     label   byte

BytesPerSector          dw      0               ; Size of a physical sector

SectorsPerCluster       db      0               ; Sectors per allocation unit

ReservedSectors         dw      0               ; Number of reserved sectors

Fats                    db      0               ; Number of fats

DirectoryEntries        dw      0               ; Number of directory entries

Sectors                 dw      0               ; No. of sectors - no. of hidden sectors

Media                   db      0               ; Media byte

FatSectors              dw      0               ; Number of fat sectors

SectorsPerTrack         dw      0               ; Sectors per track

Heads                   dw      0               ; Number of surfaces

HiddenSectors           dd      0               ; Number of hidden sectors

SectorsLong             dd      0               ; Number of sectors iff Sectors = 0

; The following is the rest of the NTFS Sector Zero information.

; The position and order of DriveNumber and CurrentHead are especially important

since those two variables are loaded into a single 16-bit register for the BIOS with one instruction.

DriveNumber         db      80h             ; Physical drive number (0 or 80h)

CurrentHead         db      ?               ; Variable to store current head no.

SectorZeroPad1      dw      0

SectorsOnVolume     db (size LARGE_INTEGER) dup (0)

MftStartLcn         db (size LARGE_INTEGER) dup (0)

Mft2StartLcn        db (size LARGE_INTEGER) dup (0)

ClustersPerFrs      dd      0

DefClustersPerBuf   dd      0

SerialNumber        db (size LARGE_INTEGER) dup (0)

CheckSum            dd      0

; The following variables are not part of the Extended BPB;  they're just scratch variables for the boot code.

SectorBase      dd      ?               ; next sector to read

CurrentTrack    dw      ?               ; current track

CurrentSector   db      ?               ; current sector

SectorCount     dw      ?               ; number of sectors to read

start:

首先设置需要用到的区段(堆栈和数据).

        cli

        xor     ax, ax                    设置堆栈起点为该代码的前一句,在重定位之后将会被转移

        mov     ss, ax                           

mov     sp, 7c00h               . 

        Sti

; BIOS把可引导磁盘(磁道0,磁头0,扇区1)的第一个物理扇区映射到内存中物理地址为7C00处

        mov     ax, Bootseg             

        mov     ds, ax

        assume  ds:BootCode

;  开始将引导块内容读入内存,然后跳转至新版本的引导块,位于第二扇区开始处

        mov     SectorBase.lsw, 0       读取扇区0.

        mov     SectorBase.msw, 0

        mov     word ptr [SectorCount], 16 读取引导区域代码

        mov     ax, NewSeg               ; NewSeg处读取.

        mov     es, ax

        sub     bx, bx                    定位NewSeg:0000.

        call    DoReadLL                 调用底层的DoRead例程,该部分读取扇区的代码从略

        push    NewSeg                 调整到 NewSeg:0200h.

        push    offset mainboot          压入第二个扇区的地址

        ret                               返回到第二个扇区

_ntfsboot endp

Intel x86 处理器支持实模式和保护模式,在实模式下,处理器的寄存器都是16 位的,而且不支持虚拟地址转译,只能访问物理内存空间中最低的1 MB 内存。计算机系统的BIOS 工作在实模式下,并且,当ntldr 获得控制权时,处理器仍然在实模式下运行。Ntldr文件实际上是由两部分组成的:第一部分是实模式代码,即首先获得控制的代码区;第二部分是一个标准的Windows 可执行二进制文件,在ntldr 中这部分被称为os loader。

; 此处代码摘自NT4代码的\private\\ntos\boot\startup\i386\su.asm

; _EnableProtectPaging

; 加载386保护模式寄存器

; 启用386保护模式

; 加载分页寄存器

; 启用386分页机制

public _EnableProtectPaging

_EnableProtectPaging  proc near

        push     dword ptr 0

        popfd

        mov      bx,sp

        mov      dx,[bx+2]  检测是否是第一次开启保护模式以及分页机制

        xor      ax,ax

        mov      gs,ax

        mov      es,ax

; 当调用内核的时候FS必须包含PCR的选择字

        push    PCR_Selector

        pop     fs

;加载gdtr和idtr

;在这里禁用中断,因为无法在转换到保护模式之前位于实模式且idt已被载入的情况下处理中断

        cli

        lgdt     fword ptr [_GDTregister]

        lidt     fword ptr [_IDTregister]

; We have to stamp the segment portion of any real-mode far pointer with the corresponding selector values before we go protected.

        mov      si,offset _ScreenStart

        mov      word ptr [si+2],VideoSelector

        mov      si,offset _vp

        mov      word ptr [si+2],VideoSelector

开启保护模式和分页机制

        mov      eax,cr0

; 如果是第一次开启保护模式,那么无需开启分页机制,因为osloader已经做好一切

; 如果代码是返回保护模式,分页表已经设置完毕,同样无需开启

        or      dx,dx

        jz      only_prot

        or      eax,PROT_MODE + ENABLE_PAGING

        mov     cr0,eax

; 接下来代码中的JMP必须是双字对齐,为了避免触发一个i386的硬件bug

; 否则有可能使得预取队列混乱

ALIGN 4

        jmp     flush

only_prot:

        or       eax,PROT_MODE

        mov      cr0,eax

刷新预取队列

ALIGN 4

        jmp     flush

flush:

将寄存器CS作为SU模块的代码选择子

        push    SuCodeSelector

        push    offset cs:restart

        retf

将寄存器DS和SS作为SU模块的保护模式数据选择子.

restart:

        mov      ax,SuDataSelector

        mov      ds,ax

        mov      ss,ax

加载LDT为0,因为从未被使用.

        xor      bx,bx

        lldt     bx

加载任务寄存器

        or       dx,dx

        jnz      epp10

        mov      bx,TSS_Selector

        ltr      bx

epp10:

        ret    ;返回之后介绍的su.asm中的模块

_EnableProtectPaging endp

public _RealMode

_RealMode proc near

转换到实模式

        sgdt     fword ptr [_GDTregister]

        sidt     fword ptr [_IDTregister]

        push     [saveDS]          将saveDS入栈,方便之后的跳转

        mov      ax,SuDataSelector

        mov      es,ax

        mov      fs,ax

        mov      gs,ax

        mov      eax,cr0

        and      eax, not (ENABLE_PAGING + PROT_MODE)

        mov      cr0,eax

刷新流水线

        jmp     far ptr here

here:

; Flush TLB

; We don't know where the page directory is, since it was allocated in the osloader.  

So we don't want to clear out cr3, but we DO want to flush the TLB....

        mov     eax,cr3

        nop                             ; Fill - Ensure 13 non-page split

        nop                             ; accesses before CR3 load

        nop                             

        nop

        mov     cr3,eax

转换为实模式地址

; 此处需要一个远跳转而不是指令retf,因为retf不能正确地重置访问权限为CS

        db      0EAh                    ; JMP FAR PTR

        dw      offset _TEXT:rmode      ; 2000:rmode

        dw      02000h

rmode:

        pop      ax

        mov      ds,ax

        mov      ss,ax

; Stamp video pointers for real-mode use

        mov     si,offset _ScreenStart

        mov     word ptr [si+2],0b800h

        mov     si,offset _vp

        mov     word ptr [si+2],0b800h

        lidt    fword ptr [_IDTregisterZero]

        sti

        ret

_RealMode endp

Ntldr 的实模式代码首先获得控制,它的任务是,完成需在16 位模式下执行的初始化工作,例如清除键盘缓冲区,然后为切换到保护模式做好基本的环境准备,之后将处理器切换到保护模式(32 位模式)下,这样它就可以访问整个32 位地址空间了。最后它将控制权交给os loader。

; _TransferToLoader     ;该子程序将控制权交给osloader

public _TransferToLoader

_TransferToLoader proc near

        mov      ebx,dword ptr [esp+2]      获取入口点参数

        xor      eax,eax

        mov      ax,[saveDS]

; 设置osloader的堆栈

        mov      cx,KeDataSelector

        mov      ss,cx

        mov      esp,LOADER_STACK  

加载ds和es作为内核数据选择子

        mov      ds,cx

        mov      es,cx

; 设置指向文件系统和引导上下文记录的指针

        shl      eax,4

        xor      ecx,ecx

        mov      cx,offset _BootRecord

        add      eax,ecx

        push     eax

        push     1010h       压入假的返回地址

将一个48位的地址传给loader的入口点

        db OVERRIDE

        push    KeCodeSelector

        push    ebx

将控制权交还OS loader

        db OVERRIDE

        retf

_TransferToLoader endp

Os loader 刚接获控制时,处理器虽然已经工作在保护模式下,但是它的虚拟地址转译机制尚未开启,所以,处理器仍然直接使用物理地址。Os loader 首先做的工作是把物理内存管起来,用一个内存描述符(memory descriptor)数组把每一段内存的大小和用途记录下来,然后构造页目录和页表,使得16 MB 以下的内存能够通过页面映射(paging)机制进行访问,再设置好页目录寄存器,并打开页面映射机制。之后,os loader 继续执行其他的初始化工作,包括I/O 设备的初始化等。如果它还需要调用BIOS 中的服务(比如中断13h、中断15h 等),则必须保护好保护模式下的设置,并暂时切换回到实模式,待服务完成以后再切换到保护模式,并恢复设置。

Windows 的引导选项可以用来指示当前这次引导的各种参数,包括内核模块的文件名称、HAL 的文件名称、CPU 参数、各种内存参数、调试参数,等等。关于这些引导选项的全面列表和介绍,可参考[MSDN-BOOT]。接下来os loader 加载并执行NTDETECT.COM 程序,这是一个16 位实模式程序,它利用系统的BIOS 来查询系统的基本设备和配置信息,包括系统的日期和时间、总线的类型、磁盘的信息、输入/输出的接口信息等。这些信息被收集起来,在引导过程的后期被存放到注册表HKLM\HARDWARE\DESCRIPTION 键的下面。

代码摘自\ntos\boot\startup\i386\main.c

VOID

SuMain(

    IN FPVOID BtRootDir,

    IN FPDISKBPB BtBiosBlock,

    IN SHORT BtBootDrive

)

/*++

Routine Description:

    Main entrypoint of the SU module. Control is passed from the boot

    sector to startup.asm which does some run-time fixups on the stack

    and data segments and then passes control here.

Arguments:

    BtRootDir - Address of root directory left in memory by boot sector

    BtBiosBlock - Address of bios parameter block.

    BtBootDrive - Drive that we booted from.

Returns:

    Does not return. Passes control to the OS loader

--*/

{

    ULONG LoaderEntryPoint;

    ULONG EisaNumPages;

    USHORT IsaNumPages;

    MEMORY_LIST_ENTRY _far *CurrentEntry;

    PIMAGE_OPTIONAL_HEADER OptionalHeader;

    ULONG BlockEnd;

    ULONG ImageSize;

    ULONG ImageBase;

    // 保存文件系统上下文信息

    FsContext.BootDrive = (ULONG)BtBootDrive;

    FsContext.PointerToBPB = MAKE_FLAT_ADDRESS(BtBiosBlock);

// 初始化视频子系统以使得错误和异常信息可以被显示

    InitializeVideoSubSystem();

// 如果系统由软盘引导,那么关掉软盘驱动器

    TurnMotorOff();

    PatchDiskBaseTable();

    // 基于总线类型设置机器类型.

    if (BtIsEisaSystem()) {

        MachineType = MACHINE_TYPE_EISA;

    } else {

        if (BtIsMcaSystem()) {

            MachineType = MACHINE_TYPE_MCA;

        } else {

            MachineType = MACHINE_TYPE_ISA;

        }

}

    if (!ConstructMemoryDescriptors()) {

        if (MachineType == MACHINE_TYPE_EISA) {

            IsaNumPages = IsaConstructMemoryDescriptors();

            EisaNumPages = EisaConstructMemoryDescriptors();

            if (EisaNumPages + 0x80 < IsaNumPages) {

                IsaConstructMemoryDescriptors();

            }

        } else {

            if (MachineType == MACHINE_TYPE_MCA) {

                McaConstructMemoryDescriptors();

            } else {

                IsaConstructMemoryDescriptors();

            }

        }

}

     // 搜索内存描述符来表示低内存

    CurrentEntry = MemoryDescriptorList;

    while ((CurrentEntry->BlockBase != 0) &&

           (CurrentEntry->BlockSize != 0)) {

        CurrentEntry++;

    }

    if ((CurrentEntry->BlockBase == 0) &&

        (CurrentEntry->BlockSize < (ULONG)512 * (ULONG)1024)) {

        BlPrint(SU_NO_LOW_MEMORY,CurrentEntry->BlockSize/1024);

        while (1) {

        }

}

// 确保os loader映像文件包含一个内存描述符

    OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)&edata + sizeof(IMAGE_FILE_HEADER));

    ImageBase = OptionalHeader->ImageBase;

    ImageSize = OptionalHeader->SizeOfImage;

    OsLoaderBase = ImageBase;

    OsLoaderExports = ImageBase + OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    CurrentEntry = MemoryDescriptorList;

    while (ImageSize > 0) {

        while (CurrentEntry->BlockSize != 0) {

            BlockEnd = CurrentEntry->BlockBase + CurrentEntry->BlockSize;

            if ((CurrentEntry->BlockBase <= ImageBase) &&

                (BlockEnd > ImageBase)) {

                // 该描述符至少得包含osloader的一部分代码

                if (BlockEnd-ImageBase > ImageSize) {

                    ImageSize = 0;

                } else {

                    ImageSize -= (BlockEnd-ImageBase);

                    ImageBase = BlockEnd;

                }

                // 寻找剩余一部分的代码

                CurrentEntry = MemoryDescriptorList;

                break;

            }

            CurrentEntry++;

        }

        if (CurrentEntry->BlockSize == 0) {

            break;

        }

    }

if (ImageSize > 0) {

        // 不能将osloader重定位到高内存位置,否则输出错误信息

        BlPrint(SU_NO_EXTENDED_MEMORY);

        CurrentEntry = MemoryDescriptorList;

        while (CurrentEntry->BlockSize != 0) {

            BlPrint("    %lx - %lx\n",

                    CurrentEntry->BlockBase,

                    CurrentEntry->BlockBase + CurrentEntry->BlockSize);

            CurrentEntry++;

        }

        while (1) {

        }

    }

// 启用A20线

    EnableA20();

// 重定位保护模式中使用的IDT和GDT结构

    Relocatex86Structures();

// 首次开启保护模式和分页模式

    EnableProtectPaging(ENABLING);

    // 重定位代码段并建立页面表项

    LoaderEntryPoint = RelocateLoaderSections(&OsLoaderStart, &OsLoaderEnd);

// 将控制权交给OS loader

    TransferToLoader(LoaderEntryPoint);

}

VOID

NtProcessStartup(

    IN PBOOT_CONTEXT BootContextRecord

    )

/*++

Routine Description:

    Main entry point for setup loader. Control is transferred here by the

    start-up (SU) module.

Arguments:

    BootContextRecord - Supplies the boot context, particularly the

        ExternalServicesTable.

Returns:

    Does not return. Control eventually passed to the kernel.

--*/

{

    ARC_STATUS Status;

    // 初始化引导加载器的显示功能

    DoGlobalInitialization(BootContextRecord);

    BlFillInSystemParameters(BootContextRecord);

    if (BootContextRecord->FSContextPointer->BootDrive == 0) {

        // 从磁盘A:开始尝试引导

        strcpy(BootPartitionName,"multi(0)disk(0)fdisk(0)");

        GET_SECTOR(0,0,0,0,0,0,NULL);

#if defined(ELTORITO)

    } else if (BlIsElToritoCDBoot(BootContextRecord->FSContextPointer->BootDrive)) {

        // CD开始尝试引导

        sprintf(BootPartitionName"multi(0)disk(0)cdrom(%u)"BootContextRecord->FSContextPointer->BootDrive);

        ElToritoCDBoot = TRUE;

#endif

    } else {

        //检查引导成功的分区是哪一个 

        BlGetActivePartition(BootPartitionName);

}

// 初始化内存描述符列表,OS loader的堆和参数块

    Status = BlMemoryInitialize();

    if (Status != ESUCCESS) {

        BlPrint("Couldn't initialize memory\n");

        while (1) {

        }

    }

// 初始化OS loader和I/O系统

    Status = BlIoInitialize();

    if (Status != ESUCCESS) {

        BlPrint("Couldn't initialize I/O\n");

    }

    BlStartup(BootPartitionName);

    // 永远不应该运行到这里!

    do {

        GET_KEY();

    } while ( 1 );

}

BOOLEAN

BlDetectHardware(

    IN ULONG DriveId,

    IN PCHAR LoadOptions

    )

/*++

Routine Description:

    Loads and runs NTDETECT.COM to populate the ARC configuration tree.

    NTDETECT is assumed to reside in the root directory.

Arguments:

    DriveId - Supplies drive id where NTDETECT is located.

    LoadOptions - Supplies Load Options string to ntdetect.

Return Value:

    TRUE - NTDETECT successfully run.

    FALSE - Error

--*/

{

    ARC_STATUS Status;

    PCONFIGURATION_COMPONENT_DATA TempFwTree;

    ULONG TempFwHeapUsed;

    extern BOOLEAN FwDescriptorsValid;

    ULONG FileSize;

    ULONG DetectFileId;

    FILE_INFORMATION FileInformation;

    PUCHAR DetectionBuffer;

    PUCHAR Options;

    UCHAR Buffer[100];

    LARGE_INTEGER SeekPosition;

    ULONG Read;

// 检查在根目录下是否存在文件ntdetect.com,如果有的话就将其加载到预定义的位置并将控制权转交给他

#if defined(ELTORITO)

    if (ElToritoCDBoot) {

        // 假设ntdetect.comi386目录下

        Status = BlOpenDriveId,

                         "\\i386\\ntdetect.com",

                         ArcOpenReadOnly,

                         &DetectFileId );

    } else {

#endif

    Status = BlOpenDriveId,

                     "\\ntdetect.com",

                     ArcOpenReadOnly,

                     &DetectFileId );

#if defined(ELTORITO)

    }

#endif

    DetectionBuffer = (PUCHAR)DETECTION_LOADED_ADDRESS;

    if (Status != ESUCCESS) {

#if DBG

        BlPrint("Error opening NTDETECT.COM, status = %x\n"Status);

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

// 获取ntdetect.com文件信息

    Status = BlGetFileInformation(DetectFileId, &FileInformation);

    if (Status != ESUCCESS) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error getting NTDETECT.COM file information, status = %x\n"Status); //获取文件信息失败

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    FileSize = FileInformation.EndingAddress.LowPart;

    if (FileSize == 0) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error: size of NTDETECT.COM is zero.\n"); //获取文件末尾失败

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    SeekPosition.QuadPart = 0;

    Status = BlSeek(DetectFileId,&SeekPosition,SeekAbsolute);

    if (Status != ESUCCESS) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error seeking to start of NTDETECT.COM file\n");  //获取文件开头失败

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    Status = BlRead(DetectFileId,DetectionBuffer,FileSize,&Read );

    BlClose(DetectFileId);

    if (Status != ESUCCESS) {

#if DBG

        BlPrint("Error reading from NTDETECT.COM\n");  //读取文件失败

        BlPrint("Read %lx bytes\n",Read);

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

// 必须传递小于1MB的NTDETECT指针,因此使用堆栈中的本地存储

    if (LoadOptions) {

        strcpy(BufferLoadOptions);

        Options = Buffer;

    } else {

        Options = NULL;

    }

    DETECT_HARDWARE((ULONG)(TEMPORARY_HEAP_START - 0x10) * PAGE_SIZE,

                    (ULONG)0x10000,         // 堆大小

                    (PVOID)&TempFwTree,

                    (PULONG)&TempFwHeapUsed,

                    (PCHAR)Options,

                    (ULONG)(LoadOptions ? strlen(LoadOptions) : 0)

                    );

    FwConfigurationTree = TempFwTree;

    FwHeapUsed = TempFwHeapUsed;

    FwDescriptorsValid = FALSE;

    return(TRUE);

}

VOID

DoGlobalInitialization(

    IN PBOOT_CONTEXT BootContextRecord

    )

/*++

Routine Description

    This routine calls all of the subsytem initialization routines.

Arguments:

    None

Returns:

    Nothing

--*/

{

    ARC_STATUS Status;

    Status = InitializeMemorySubsystem(BootContextRecord);  //初始化内存子系统

    if (Status != ESUCCESS) {

        BlPrint("InitializeMemory failed %lx\n",Status);

        while (1) {

        }

    }

    ExternalServicesTable=BootContextRecord->ExternalServicesTable;

    MachineType = BootContextRecord->MachineType;

    // 此处开启光标支持

    HW_CURSOR(0,127);

    BlpResourceDirectory = (PUCHAR)(BootContextRecord->ResourceDirectory);

    BlpResourceFileOffset = (PUCHAR)(BootContextRecord->ResourceOffset);

    OsLoaderBase = BootContextRecord->OsLoaderBase;

    OsLoaderExports = BootContextRecord->OsLoaderExports;

    InitializeMemoryDescriptors ();  //初始化内存描述符

}

VOID

BlGetActivePartition(

    OUT PUCHAR BootPartitionName

    )

/*++

Routine Description:

    Determine the ARC name for the partition NTLDR was started from

Arguments:

    BootPartitionName - Supplies a buffer where the ARC name of the partition will be returned.

Return Value:

    Name of the partition is in BootPartitionName.

    Must always succeed.

--*/

{

    UCHAR SectorBuffer[512];

    UCHAR NameBuffer[80];

    ARC_STATUS Status;

    ULONG FileId;

    ULONG Count;

    int i;

// 尝试打开所有的分区并将其读为引导扇区,并与之前使用的引导扇区进行对比.

// 如果相同,则找到,否则尝试分区1

    i=1;

    do {

        sprintf(NameBuffer"multi(0)disk(0)rdisk(0)partition(%u)",i);

        Status = ArcOpen(NameBuffer,ArcOpenReadOnly,&FileId);

        if (Status != ESUCCESS) {

            // 遍历所有分区未找到合适的对象,故设置默认值.

            i=1;

            break;

        } else {

            // 读取前512个字节

            Status = ArcRead(FileIdSectorBuffer, 512, &Count);

            ArcClose(FileId);

           

 if (Status==ESUCCESS) {

                // 只需要比较前36个字节

                // 跳转标识 3 bytes

                // Oem位段 8字节

                // 参数块 25字节

                if (memcmp(SectorBuffer, (PVOID)0x7c00, 36)==0) {

                    // 找到匹配对象.

                    break;

                }

            }

        }

        ++i;

    } while ( TRUE );

    sprintf(BootPartitionName"multi(0)disk(0)rdisk(0)partition(%u)",i);

    return;

}

#if defined(ELTORITO)    

接下来,os loader 从系统分区(即引导分区)的根目录下读入boot.ini 文件。注意,os loader 包含了读取当前文件系统的代码,它能够读取NTFS 文件系统的子目录。然后,os loader 清除屏幕,并检查在系统分区的根目录下是否有一个有效的hiberfil.sys 文件,如果存在的话,这一次引导过程转移到休眠系统的恢复过程。因此,os loader 将控制权交给一段能恢复休眠系统的内核代码。

如果当前这次引导不是休眠恢复,那么,os loader 解析boot.ini 文件,如果该文件中有多个引导选项,则os loader 显示一个引导选择菜单;如果boot.ini 文件中只包含一个引导选项,那么,此菜单不显示,而是立即应用该引导选项。

代码摘自\ntos\boot\bldr\i386\initx86.c

//负责打开驱动和读boot.ini文件

VOID

BlStartup(

    IN PCHAR PartitionName

    )

/*++

Routine Description:

    Does x86-specific initialization, particularly presenting the boot.ini

    menu and running NTDETECT, then calls to the common osloader.

Arguments:

    PartitionName - Supplies the ARC name of the partition (or floppy) that

        setupldr was loaded from.

Return Value:

    Does not return

--*/

{

    PUCHAR Argv[10];

    ARC_STATUS Status;

    ULONG BootFileId;

    PCHAR BootFile;

    ULONG Read;

    PCHAR p;

    ULONG i;

    ULONG DriveId;

    ULONG FileSize;

    ULONG Count;

    LARGE_INTEGER SeekPosition;

    PCHAR LoadOptions = NULL;

    BOOLEAN UseTimeOut=TRUE;

    BOOLEAN AlreadyInitialized = FALSE;

    extern BOOLEAN FwDescriptorsValid;

    // 打开引导分区以便加载引导驱动.

    Status = ArcOpen(PartitionNameArcOpenReadOnly, &DriveId);

    if (Status != ESUCCESS) {

        BlPrint("Couldn't open drive %s\n",PartitionName);

        BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);

        goto BootFailed;

    }

    // 初始化双字节内码系统以及显示支持.

    TextGrInitialize(DriveId);

    do {

        Status = BlOpenDriveId,

                         "\\boot.ini",

                         ArcOpenReadOnly,

                         &BootFileId );  //此处开始打开boot.ini

        BootFile = MyBuffer;

        RtlZeroMemory(MyBufferSECTOR_SIZE+32);  

        if (Status != ESUCCESS) {

            BootFile[0]='\0';

        } else {

           

 // 通过从头到尾读取boot.ini文件获取大小

            FileSize = 0;

            do {

                Status = BlRead(BootFileIdBootFileSECTOR_SIZE, &Count);

                if (Status != ESUCCESS) {

                    BlClose(BootFileId);

                    BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                    BootFile[0] = '\0';   //结束符

                    FileSize = 0;

                    Count = 0;

                    goto BootFailed;

                }

                FileSize += Count;

            } while (Count != 0);

            if (FileSize >= SECTOR_SIZE) {

                // 如果boot.ini文件大于一个扇区的大小,那么就需要重新分配更大的缓冲区

                BootFile=FwAllocateHeap(FileSize);

            }

            if (BootFile == NULL) {

                BlPrint(BlFindMessage(BL_READ_ERROR),ENOMEM);

                BootFile = MyBuffer;

                BootFile[0] = '\0';

                goto BootFailed;

            } else {

                SeekPosition.QuadPart = 0;

                Status = BlSeek(BootFileId,

                                &SeekPosition,

                                SeekAbsolute);

                if (Status != ESUCCESS) {

                    BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                    BootFile = MyBuffer;

                    BootFile[0] = '\0';

                    goto BootFailed;

                } else {

                    Status = BlReadBootFileId,

                                     BootFile,

                                     FileSize,

                                     &Read );

                    SeekPosition.QuadPart = 0;

                    Status = BlSeek(BootFileId,

                                    &SeekPosition,

                                    SeekAbsolute);

                    if (Status != ESUCCESS) {

                        BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                        BootFile = MyBuffer;

                        BootFile[0] = '\0';

                        goto BootFailed;

                    } else {

                        BootFile[Read]='\0';

                    }

                }

            }

            // 搜索Ctrl-Z

            p=BootFile;

            while ((*p!='\0') && (*p!=26)) {

                ++p;

            }

            if (*p != '\0') {

                *p='\0';

            }

            BlClose(BootFileId);

        }

        if (!AlreadyInitialized) {

            AbiosInitDataStructures();

        }

        MdShutoffFloppy();  //关闭软驱

        TextClearDisplay();   //清除显示文本

        p=BlSelectKernel(DriveId,BootFile, &LoadOptionsUseTimeOut);

        if (!AlreadyInitialized) {

            BlPrint(BlFindMessage(BL_NTDETECT_MSG));

            if (!BlDetectHardware(DriveIdLoadOptions)) {

                BlPrint(BlFindMessage(BL_NTDETECT_FAILURE));

                return;

            }

            TextClearDisplay();

            // 初始化SCSI引导驱动

            if(!_strnicmp(p,"scsi(",5)) {

                AEInitializeIo(DriveId);

            }

            ArcClose(DriveId);

            // 设置标志位表示内存描述符不能被改变.

            FwDescriptorsValid = FALSE;

        } else {

            TextClearDisplay();

        }

      

        //设置该标志位用于表示ntdetect和abios的初始化例程已经运行

        AlreadyInitialized = TRUE;

        //设置引导菜单无等待操作的时间,即用户如不操作则一直停留

        UseTimeOut=FALSE;

        i=0;

        while (*p !='\\') {

            KernelBootDevice[i] = *p;

            i++;

            p++;

        }

        KernelBootDevice[i] = '\0';

        strcpy(OsLoadFilename,"osloadfilename=");

        strcat(OsLoadFilename,p);

        // 这里只能使用参数”osloader= variable 来指定从哪里加载HAL.DLL.  

// 因为x86系统未指定系统分区 ” 所以将从路径\nt\system\HAL.DLL加载HAL.DLL

        strcpy(OsLoaderFilename,"osloader=");

        strcat(OsLoaderFilename,p);

        strcat(OsLoaderFilename,"\\System32\\NTLDR");

        strcpy(SystemPartition,"systempartition=");

        strcat(SystemPartition,KernelBootDevice);

        strcpy(OsLoadPartition,"osloadpartition=");

        strcat(OsLoadPartition,KernelBootDevice);

        strcpy(OsLoadOptions,"osloadoptions=");

        if (LoadOptions) {

            strcat(OsLoadOptions,LoadOptions);

        }

        strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");

        strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");

        strcpy(X86SystemPartition,"x86systempartition=");

        strcat(X86SystemPartition,PartitionName);

        Argv[0]="load";

        Argv[1]=OsLoaderFilename;

        Argv[2]=SystemPartition;

        Argv[3]=OsLoadFilename;

        Argv[4]=OsLoadPartition;

        Argv[5]=OsLoadOptions;

        Argv[6]=ConsoleInputName;

        Argv[7]=ConsoleOutputName;

        Argv[8]=X86SystemPartition;

        Status = BlOsLoader(9,Argv,NULL);

    BootFailed:

        if (Status != ESUCCESS) {

            // 引导失败就重新启动

            while (TRUE) {

                GET_KEY();

            }

        } else {

            //重新打开设备

            Status = ArcOpen(BootPartitionNameArcOpenReadOnly, &DriveId);

            if (Status != ESUCCESS) {

                BlPrint(BlFindMessage(BL_DRIVE_ERROR),BootPartitionName);

                goto BootFailed;

            }

        }

    } while (TRUE);

}

然后,os loader 加载内核模块映像文件,默认为ntoskrnl.exe,以及HAL 映像文件,默认为hal.dll。再加载注册表的SYSTEM 储巢,即\WINDOWS\system32\config\system 文件。通过检查SYSTEM 储巢中的设置信息,它可以知道哪些设备驱动程序必须被加载进来,即被标记为“引导-启动”(SERVICE_BOOT_START)的设备驱动程序。然后它加载所有这些被标记为“引导-启动”的设备驱动程序,以及访问系统目录所必需的文件系统驱动程序。注意,在此之前os loader 也可以访问系统分区,但并非通过文件系统驱动程序。

代码摘自\ntos\boot\bldr\osloader.c

// 定义扩展静态数据

ULONG BlConsoleOutDeviceId = 0;

ULONG BlConsoleInDeviceId = 0;

ULONG BlDcacheFillSize = 32;

#if DBG

BOOLEAN BlOutputDots=FALSE;

#else

BOOLEAN BlOutputDots=TRUE;

#endif

CHAR KernelFileName[8+1+3+1]="ntoskrnl.exe";

CHAR HalFileName[8+1+3+1]="hal.dll";

//此处定义名为"ntoskrnl.exe"和"hal.dll"的文件

ARC_STATUS

BlOsLoader (

    IN ULONG Argc,

    IN PCHAR Argv[],

    IN PCHAR Envp[]

    )

/*++

Routine Description:

    This is the main routine that controls the loading of the NT operating

    system on an ARC compliant system. It opens the system partition,

    the boot partition, the console input device, and the console output

    device. The NT operating system and all its DLLs are loaded and bound

    together. Control is then transfered to the loaded system.

Arguments:

    Argc - Supplies the number of arguments that were provided on the

        command that invoked this program.

    Argv - Supplies a pointer to a vector of pointers to null terminated

        argument strings.

    Envp - Supplies a pointer to a vector of pointers to null terminated

        environment variables.

Return Value:

    EBADF is returned if the specified OS image cannot be loaded.

--*/

{

    CHAR BootDirectoryPath[256];

    ULONG CacheLineSize;

    PCHAR ConsoleOutDevice;

    PCHAR ConsoleInDevice;

    ULONG Count;

    PCONFIGURATION_COMPONENT_DATA DataCache;

    CHAR DeviceName[256];

    CHAR DevicePrefix[256];

    PCHAR DirectoryEnd;

    CHAR DllName[256];

    CHAR DriverDirectoryPath[256];

    PCHAR FileName;

    ULONG FileSize;

    PLDR_DATA_TABLE_ENTRY HalDataTableEntry;

    CHAR HalDirectoryPath[256];

    CHAR KernelDirectoryPath[256];

    PVOID HalBase;

    PVOID SystemBase;

    ULONG Index;

    ULONG Limit;

    ULONG LinesPerBlock;

    PCHAR LoadDevice;

    ULONG LoadDeviceId;

    PCHAR LoadFileName;

    PCHAR LoadOptions;

    ULONG i;

    CHAR OutputBuffer[256];

    ARC_STATUS Status;

    PLDR_DATA_TABLE_ENTRY SystemDataTableEntry;

    PCHAR SystemDevice;

    ULONG SystemDeviceId;

    PTRANSFER_ROUTINE SystemEntry;

    PIMAGE_NT_HEADERS NtHeaders;

    PWSTR BootFileSystem;

    PCHAR LastKnownGood;

    BOOLEAN BreakInKey;

    CHAR BadFileName[128];

    PBOOTFS_INFO FsInfo;

// 获取控制台输出设备的名字并获取写入权限

    ConsoleOutDevice = BlGetArgumentValue(ArgcArgv"consoleout");

    if (ConsoleOutDevice == NULL) {

        return ENODEV;

    }

    Status = ArcOpen(ConsoleOutDeviceArcOpenWriteOnly, &BlConsoleOutDeviceId);

    if (Status != ESUCCESS) {

        return Status;

    }

// 获取控制台输入设备的名字并获取读取权限

    ConsoleInDevice = BlGetArgumentValue(ArgcArgv"consolein");

    if (ConsoleInDevice == NULL) {

        return ENODEV;

    }

    Status = ArcOpen(ConsoleInDeviceArcOpenReadOnly, &BlConsoleInDeviceId);

    if (Status != ESUCCESS) {

        return Status;

    }

    // 声明OS Loader.

    strcpy(&OutputBuffer[0], "OS Loader V4.00\r\n");

    ArcWrite(BlConsoleOutDeviceId, &OutputBuffer[0],

             strlen(&OutputBuffer[0]),

             &Count);

// 初始化内存描述符列表,OSloader系统堆和参数块

    Status = BlMemoryInitialize();

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_MEM_CLASS,

                          DIAG_BL_MEMORY_INIT,

                          LOAD_HW_MEM_ACT);

        goto LoadFailed;

    }

// 计算数据缓存大小.该值用来对齐I/O缓冲区以防主机系统不支持连续的缓存

    DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                         CacheClass,

                                         SecondaryCache,

                                         NULL);

    if (DataCache == NULL) {

        DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                             CacheClass,

                                             SecondaryDcache,

                                             NULL);

        if (DataCache == NULL) {

            DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                                 CacheClass,

                                                 PrimaryDcache,

                                                 NULL);

        }

    }

    if (DataCache != NULL) {

        LinesPerBlock = DataCache->ComponentEntry.Key >> 24;

        CacheLineSize = 1 << ((DataCache->ComponentEntry.Key >> 16) & 0xff);

        BlDcacheFillSize = LinesPerBlock * CacheLineSize;

    }

    // 初始化OS loader的I/O系统

    Status = BlIoInitialize();

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_IO_INIT,

                          LOAD_HW_DISK_ACT);

        goto LoadFailed;

    }

// 初始化资源节

    Status = BlInitResources(Argv[0]);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_IO_INIT,

                          LOAD_HW_DISK_ACT);

    }

// 初始化NT配置树.

    BlLoaderBlock->ConfigurationRoot = NULL;

    Status = BlConfigurationInitialize(NULLNULL);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                     DIAG_BL_CONFIG_INIT,

                     LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    // 复制osloadoptions参数给LoaderBlock

    LoadOptions = BlGetArgumentValue(ArgcArgv"osloadoptions");

    if (LoadOptions != NULL) {

        FileSize = strlen(LoadOptions)+1;

        FileName = (PCHAR)BlAllocateHeap(FileSize);

        strcpy(FileNameLoadOptions);

        BlLoaderBlock->LoadOptions = FileName;

        //检测标志值判断是否应该输出文件名而不是单独的原点

        if ((strstr(FileName,"SOS")!=NULL) ||

            (strstr(FileName,"sos")!=NULL)) {

            BlOutputDots=FALSE;

        }

        FileName=strstr(BlLoaderBlock->LoadOptions,"HAL=");

        if (FileName) {

            for (i=0; i<sizeof(HalFileName); i++) {

                if (FileName[4+i]==' ') {

                    HalFileName[i]='\0';

                    break;

                }

                HalFileName[i]=FileName[4+i];

            }

        }

        HalFileName[sizeof(HalFileName)-1]='\0';

        FileName=strstr(BlLoaderBlock->LoadOptions,"KERNEL=");

        if (FileName) {

            for (i=0; i<sizeof(KernelFileName); i++) {

                if (FileName[7+i]==' ') {

                    KernelFileName[i]='\0';

                    break;

                }

                KernelFileName[i]=FileName[7+i];

            }

        }

        KernelFileName[sizeof(KernelFileName)-1]='\0';

    } else {

        BlLoaderBlock->LoadOptions = NULL;

    }

    // 获取加载设备的名称以及读取的权限.

    LoadDevice = BlGetArgumentValue(ArgcArgv"osloadpartition");

    if (LoadDevice == NULL) {

             Status = ENODEV;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_BOOT_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    Status = ArcOpen(LoadDeviceArcOpenReadOnly, &LoadDeviceId);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_OPEN_BOOT_DEVICE,

                          LOAD_HW_DISK_ACT);

        goto LoadFailed;

    }

    // 获取系统设备的名称以及读取的权限.

    SystemDevice = BlGetArgumentValue(ArgcArgv"systempartition");

    if (SystemDevice == NULL) {

        Status = ENODEV;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_SYSTEM_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    Status = ArcOpen(SystemDeviceArcOpenReadOnly, &SystemDeviceId);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_OPEN_SYSTEM_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    // 初始化调试系统

    BlLogInitialize(SystemDeviceId);

//显示系统提示符,给用户更多的准备时间

    BlStartConfigPrompt();

#if defined(_PPC_)

    Status = BlPpcInitialize();

    if (Status != ESUCCESS) {

        goto LoadFailed;

    }

#endif // defined(_PPC_)

    // 获取系统根目录的路径名称.

    LoadFileName = BlGetArgumentValue(ArgcArgv"osloadfilename");

    if (LoadFileName == NULL) {

        Status = ENOENT;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_BOOT_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

// 生成SYSTEM32目录名称 

 strcpy(BootDirectoryPathLoadFileName);

    strcat(BootDirectoryPath"\\System32\\");

    // 生成ntoskrnl.exe的全路径

    //      "\winnt\system32\ntoskrnl.exe"

    strcpy(KernelDirectoryPathBootDirectoryPath);

    strcat(KernelDirectoryPathKernelFileName);

    // 加载内核模块映像文件.

    BlOutputLoadMessage(LoadDeviceKernelDirectoryPath);

    Status = BlLoadImage(LoadDeviceId,

                         LoaderSystemCode,

                         KernelDirectoryPath,

                         TARGET_IMAGE,

                         &SystemBase);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                                         DIAG_BL_LOAD_SYSTEM_IMAGE,

                                         LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

// 无论文件系统是什么,都需要与引导驱动一起加载

    FsInfo = BlGetFsInfo(LoadDeviceId);

    if (FsInfo != NULL) {

        BootFileSystem = FsInfo->DriverName;

    } else {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                                         DIAG_BL_LOAD_SYSTEM_IMAGE,

                                         LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

// 获取OS loader文件的路径名称,并除去目录名称,用来加载HAL.DLL

    FileName = BlGetArgumentValue(ArgcArgv"osloader");

    if (FileName == NULL) {

        Status = ENOENT;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FIND_HAL_IMAGE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    DirectoryEnd = strrchr(FileName'\\');

    FileName = strchr(FileName'\\');

    HalDirectoryPath[0] = 0;

    if (DirectoryEnd != NULL) {

        Limit = (ULONG)DirectoryEnd - (ULONG)FileName + 1;

        for (Index = 0; Index < LimitIndex += 1) {

            HalDirectoryPath[Index] = *FileName++;

        }

        HalDirectoryPath[Index] = 0;

    }

    // 生成完整的路径名称并将HAL加载入系统内存中去.

    strcpy(&DllName[0], &HalDirectoryPath[0]);

    strcat(&DllName[0], HalFileName);

    BlOutputLoadMessage(SystemDevice, &DllName[0]);

    Status = BlLoadImage(SystemDeviceId,

                         LoaderHalCode,

                         &DllName[0],

                         TARGET_IMAGE,

                         &HalBase);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                          DIAG_BL_LOAD_HAL_IMAGE,

                          LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

    // ntoskrnl.exe生成数据表入口.

    Status = BlAllocateDataTableEntry("ntoskrnl.exe",

                                      KernelDirectoryPath,

                                      SystemBase,

                                      &SystemDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_SYSTEM_IMAGE,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

    // 为hal.dll生成数据表入口.

    Status = BlAllocateDataTableEntry("hal.dll",

                                      &DllName[0],

                                      HalBase,

                                      &HalDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_HAL_IMAGE,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

#if defined(_ALPHA_)

    Status = BlLoadPal(SystemDeviceId,

                       LoaderSystemCode,

                       &HalDirectoryPath[0],

                       TARGET_IMAGE,

                       &BlLoaderBlock->u.Alpha.PalBaseAddress,

                       SystemDevice);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                          DIAG_BL_LOAD_SYSTEM_DLLS,

                          LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

#endif // _ALPHA_

// 扫描系统映像的导入表以及加载相关的dll

    Status = BlScanImportDescriptorTable(LoadDeviceId,

                                         LoadDevice,

                                         &BootDirectoryPath[0],

                                         SystemDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_SYSTEM_DLLS,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 扫描HAL.dll的导入表以及加载相关的dll

    Status = BlScanImportDescriptorTable(SystemDeviceId,

                                         SystemDevice,

                                         &HalDirectoryPath[0],

                                         HalDataTableEntry);

   

 if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_HAL_DLLS,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 重定位系统入口点并设置系统指定的信息

    NtHeaders = RtlImageNtHeader(SystemBase);

    SystemEntry = (PTRANSFER_ROUTINE)((ULONG)SystemBase +

                                NtHeaders->OptionalHeader.AddressOfEntryPoint);

#ifdef MIPS

    BlLoaderBlock->u.Mips.GpBase = (ULONG)SystemBase +

        NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress;

#endif

#if defined(_ALPHA_)

    BlLoaderBlock->u.Alpha.GpBase = (ULONG)SystemBase +

        NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress;

#endif

// 复制所有的设备驱动的目录路径

    strcpy(&DriverDirectoryPath[0], &BootDirectoryPath[0]);

    strcat(&DriverDirectoryPath[0], "\\Drivers\\");

    // NLS数据分配结构体.  这将通过BlLoadAndScanSystemHive函数填充并加载.

    BlLoaderBlock->NlsData = BlAllocateHeap(sizeof(NLS_DATA_BLOCK));

    if (BlLoaderBlock->NlsData == NULL) {

        Status = ENOMEM;

        BlFatalError(LOAD_HW_MEM_CLASS,

                          DIAG_BL_LOAD_SYSTEM_HIVE,

                          LOAD_HW_MEM_ACT);

        goto LoadFailed;

}

// 加载注册表的SYSTEM储巢

    Status = BlLoadAndScanSystemHive(LoadDeviceId,

                                     LoadDevice,

                                     LoadFileName,

                                     BootFileSystem,

                                     BadFileName);

    if (Status != ESUCCESS) {

        if (BlRebootSystem) {

            Status = ESUCCESS;

        } else {

            BlBadFileMessage(BadFileName);

        }

        goto LoadFailed;

    }

    // 生成符合ARC命名规范的引导设备名称以及NT路径名.

    Status = BlGenerateDeviceNames(LoadDevice, &DeviceName[0], &DevicePrefix[0]);

    if (Status != ESUCCESS) {

             BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_ARC_BOOT_DEV_NAME,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    FileSize = strlen(&DeviceName[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &DeviceName[0]);

    BlLoaderBlock->ArcBootDeviceName = FileName;

    FileSize = strlen(LoadFileName) + 2;

    FileName = (PCHAR)BlAllocateHeapFileSize);

    strcpy(FileNameLoadFileName);

    strcat(FileName"\\");

BlLoaderBlock->NtBootPathName = FileName;

// 生成符合ARC命名规范的HAL设备名称以及NT路径名.

#ifdef i386

    

    strcpy(&DeviceName[0], BlGetArgumentValue(ArgcArgv"x86systempartition"));

#else

    Status = BlGenerateDeviceNames(SystemDevice, &DeviceName[0], &DevicePrefix[0]);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_ARC_BOOT_DEV_NAME,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

#endif //i386

    FileSize = strlen(&DeviceName[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &DeviceName[0]);

    BlLoaderBlock->ArcHalDeviceName = FileName;

#ifdef i386

    //此处硬编码osloader的地址

    FileName = (PCHAR)BlAllocateHeap(2);

    FileName[0] = '\\';

    FileName[1] = '\0';

#else

    FileSize = strlen(&HalDirectoryPath[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &HalDirectoryPath[0]);

#endif //i386

BlLoaderBlock->NtHalPathName = FileName;

// 获取NTFS设备的签名信息以允许内核创建正确的ARC名称

    BlGetArcDiskInformation();

    // 此处执行特定的安装代码.

    Status = BlSetupForNt(BlLoaderBlock);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,DIAG_BL_SETUP_FOR_NT,LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 关闭调试系统

    BlLogTerminate();

// 将控制权转换给已加载的映像

    (SystemEntry)(BlLoaderBlock);

    Status = EBADF;

    BlFatalError(LOAD_SW_BAD_FILE_CLASS,DIAG_BL_KERNEL_INIT_XFER,LOAD_SW_FILE_REINST_ACT);

LoadFailed:

return Status;

}

至此,引导系统所需的模块(包括内核映像、HAL,以及必要的设备驱动程序)都已经被加载到内存中。而且,在此过程中os loader 也已经构造了一个参数块,记录下了这次引导过程中加载器所获得的各种参数信息。参数块的类型为LOADER_PARAMETER_BLOCK,Windows 内核在初始化过程中将会用到这些参数信息。WRK 中包含有它的定义,如下(见public\internal\base\inc\arc.h 文件):

typedef struct _LOADER_PARAMETER_BLOCK {

    LIST_ENTRY LoadOrderListHead; //加载的模块链表,每个元素都为KLDR_DATA_TABLE_ENTRY

LIST_ENTRY MemoryDescriptorListHead;

//内存描述符链表,每个元素都为MEMORY_ALLOCATION_DESCRIPTOR    

    LIST_ENTRY BootDriverListHead//引导驱动程序链表,每个元素都为BOOT_DRIVER_LIST_ENTRY

    ULONG_PTR KernelStack; //内核栈顶

    ULONG_PTR Prcb; //进程环境,指向一个进程控制块

    ULONG_PTR Process; //初始进程,EPROCESS

    ULONG_PTR Thread; //初始线程,ETHREAD

    ULONG RegistryLength; //系统储巢的长度

    PVOID RegistryBase; //系统储巢的基地址

PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;

//配置树,包含ISA,磁盘和ACPI的配置数据

    PCHAR ArcBootDeviceName; //引导分区的ARC名称

    PCHAR ArcHalDeviceName;                //系统分区的ARC名称

    PCHAR NtBootPathName;                  //OS目录的路径名称,比如"\Windows"

    PCHAR NtHalPathName; //OD加载器的路径名称,比如"\"

    PCHAR LoadOptions; //引导选项,来自boot.ini

    PNLS_DATA_BLOCK NlsData; //包含ANSI代码页,OEM代码页和Unicode码表

    PARC_DISK_INFORMATION ArcDiskInformation;    //所有磁盘的签名结构

    PVOID OemFontFile; //OEM字体文件

    struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;  //网络引导或文字模式安装引导

    PLOADER_PARAMETER_EXTENSION Extension;      //扩展部分

    

union {

        I386_LOADER_BLOCK I386;

        // ALPHA_LOADER_BLOCK Alpha;

        // IA64_LOADER_BLOCK Ia64;

u;

LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;

由上述代码的注解可以看出,LOADER_PARAMETER_BLOCK 参数块中包含了有关这次引导的各种参数信息和系统配置,这里ARC 名称是指符合ARC(Advanced RISCComputing)命名规范的字符串,例如“multi(0)disk(0)rdisk(0)partition(1)”是指0 号磁盘控制器第一块硬盘上的第一个分区。注意,参数块中的绝大多数信息由os loader 来填充,而在接下来的内核初始化过程中使用,但也有例外,比如有关线程和进程的信息需要在内核初始化过程中填充。

最后,os loader 将控制权交给内核模块的入口函数,该函数将不再返回,所以,接下来的引导过程由内核模块继续进行,引导扇区和系统加载器(ntldr 或os loader)的使命已经完成。下图显示了以上介绍的引导步骤。

我们已经看到,ntldr 构造了一个类型为LOADER_PARAMETER_BLOCK 的参数块,把与系统初始化相关的参数信息包装到此结构中,然后将控制权传递给内核模块ntoskrnl.exe 的入口函数。因此,内核的初始化从内核模块的入口函数开始,WRK 包含了内核初始化过程的绝大多数代码。此入口函数为KiSystemStartup,它是一个汇编函数,位于base\ntos\ke\i386\newsysbg.asm 文件中。

cPublicProc _KiSystemStartup        ,1

        push    ebp

        mov     ebp, esp

        sub     esp, 32                       ;分配空间给全局变量

        mov     ebx, dword ptr KissLoaderBlock

        mov     _KeLoaderBlock, ebx         获取加载器的参数

        movzx   ecx, _KeNumberProcessors    ;获取处理器的个数

        mov     KissPbNumber, ecx

        or      ecx, ecx                       ;判断是否为引导处理器

        jnz     @f                            ;不是引导处理器

        ; 初始化0阶段使用静态的内存

        mov     dword ptr [ebx].LpbThread,      offset _KiInitialThread ;初始化线程

        mov     dword ptr [ebx].LpbKernelStack, offset P0BootStack     ;内核堆栈

        push    KGDT_R0_PCR                 ; P0 needs FS set

        pop     fs

        ; Prcb中存储处理器序号

        mov     byte ptr PCR[PcPrcbData+PbNumber], cl   

;此处开始构造PCR (Processor Control Region)

@@:

        mov     eax, dword ptr [ebx].LpbThread

        mov     dword ptr KissIdleThread, eax

        lea     ecx, [eax].ThApcState.AsApcListHead  ;初始化内核APC链表头

        mov     [eax].ThApcState.AsApcListHead, ecx ;

        mov     [eax].ThApcState.AsApcListHead+4, ecx ;

        mov     eax, dword ptr [ebx].LpbKernelStack

        mov     dword ptr KissIdleStack, eax

        stdCall   _KiInitializeMachineType

        cmp     byte ptr KissPbNumber, 0    

        jne     kiss_notp0                    

初始化GDT,PCR,TSS,IDT

        stdCall   GetMachineBootPointers

;   (edi) -> gdt

;   (esi) -> pcr

;   (edx) -> tss

;   (eax) -> idt

保存相关参数到相应的寄存器

        mov     KissGdt, edi

        mov     KissPcr, esi

        mov     KissTss, edx

        mov     KissIdt, eax

;将TSS转换为32位,因为ntloader传递的tss为16位

        lea     ecx,[edi]+KGDT_TSS      ; (ecx) -> TSS descriptor

        mov     byte ptr [ecx+5],089h   ; 32bit, dpl=0, present, TSS32, not busy

; KiInitializeTSS2(

;       TSS的线性地址

;       TSS描述符的线性地址

;       );

        stdCall   _KiInitializeTSS2, <KissTss, ecx>

        stdCall   _KiInitializeTSS, <KissTss>

        mov     cx,KGDT_TSS

        ltr     cx   ;从GDT中取出相应的TSS段描述符

;   设置32位双重故障任务门去获取双重故障异常

        mov     eax,KissIdt

        lea     ecx,[eax]+40h            异常向量号8

        mov     byte ptr [ecx+5],085h   描述符特权级别dpl=0, present, taskgate

        mov     word ptr [ecx+2],KGDT_DF_TSS

        lea     ecx,[edi]+KGDT_DF_TSS

        mov     byte ptr [ecx+5],089h   ; 32, 描述符特权级别dpl=0, present, TSS32, not busy

        mov     edx,offset FLAT:_KiDoubleFaultTSS

        mov     eax,edx

        mov     [ecx+KgdtBaseLow],ax

        shr     eax,16

        mov     [ecx+KgdtBaseHi],ah

        mov     [ecx+KgdtBaseMid],al

        mov     eax, MINIMUM_TSS_SIZE

        mov     [ecx+KgdtLimitLow],ax

; KiInitializeTSS(

;       双重故障任务状态段

;       );

        push      edx

        stdCall   _KiInitializeTSS, <edx>

        pop       edx

        mov     eax,cr3

        mov     [edx+TssCr3],eax

        mov     eax, offset FLAT:_KiDoubleFaultStack

        mov     dword ptr [edx+TssEsp],eax

        mov     dword ptr [edx+TssEsp0],eax

        mov     dword ptr [edx+020h],offset FLAT:_KiTrap08

        mov     dword ptr [edx+024h],0                 ; eflags

        mov     word ptr [edx+04ch],KGDT_R0_CODE    设置CS的值

        mov     word ptr [edx+058h],KGDT_R0_PCR      设置FS的值

        mov     [edx+050h],ss

        mov     word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es

        mov     word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds

;  设置32位不可屏蔽中断任务门去获取不可屏蔽中断异常

        mov     eax,KissIdt

        lea     ecx,[eax]+10h           ; Descriptor 2

        mov     byte ptr [ecx+5],085h   ; dpl=0, present, taskgate

        mov     word ptr [ecx+2],KGDT_NMI_TSS

        lea     ecx,[edi]+KGDT_NMI_TSS

        mov     byte ptr [ecx+5],089h   ; 32bit, dpl=0, present, TSS32, not busy

        mov     edx,offset FLAT:_KiNMITSS

        mov     eax,edx

        mov     [ecx+KgdtBaseLow],ax

        shr     eax,16

        mov     [ecx+KgdtBaseHi],ah

        mov     [ecx+KgdtBaseMid],al

        mov     eax, MINIMUM_TSS_SIZE

        mov     [ecx+KgdtLimitLow],ax

        push    edx

        stdCall _KiInitializeTSS,<edx> 

 ; KiInitializeTSS(

     ;       TSS地址

     ;       );

        pop     edx

; We are using the DoubleFault stack as the DoubleFault stack and the NMI Task Gate stack and briefly, it is the DPC stack for the first

; processor.

        mov     eax,cr3

        mov     [edx+TssCr3],eax

        mov     eax, offset FLAT:_KiDoubleFaultTSS

        mov     eax, dword ptr [eax+038h]           ; get DF stack

        mov     dword ptr [edx+TssEsp0],eax         ; use it for NMI stack

        mov     dword ptr [edx+038h],eax

        mov     dword ptr [edx+020h],offset FLAT:_KiTrap02

        mov     dword ptr [edx+024h],0                ; eflags

        mov     word ptr [edx+04ch],KGDT_R0_CODE    ; set value for CS

        mov     word ptr [edx+058h],KGDT_R0_PCR     ; set value for FS

        mov     [edx+050h],ss

        mov     word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es

        mov     word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds

        stdCall   _KiInitializePcr, <KissPbNumber,KissPcr,KissIdt,KissGdt,KissTss,KissIdleThread,offset FLAT:_KiDoubleFaultStack>

在当前线程对象中设置当前进程指针

        mov     edx, KissIdleThread

        mov     ecx, offset FLAT:_KiInitialProcess ; (ecx)-> idle process obj

        mov     [edx]+ThApcState+AsProcess, ecx ; set addr of thread's process

设置 PCR: Teb, Prcb 指针.  

;  Prcb 相关参数将在函数 _KiInitializeKernel中设置

        mov     dword ptr PCR[PcTeb], 0   ; PCR->Teb = 0

初始化KernelDr7KernelDr60.  该操作必须在调试器调用前完成.

mov     dword ptr PCR[PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr6],0

mov     dword ptr PCR[PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr7],0

; 内核IDT重新设置,转换成i386可以识别的次序.该操作只能由引导处理器完成

        stdCall   _KiSwapIDT                  

        mov     eax,KGDT_R3_DATA OR RPL_MASK    设置请求特权级RPL为ring 3

        mov     ds,ax

        mov     es,ax

;  复制陷阱处理器替换内核调试处理器

        mov     eax, KissIdt              ; (eax)-> Idt

        push    dword ptr [eax+40h]     保存双重故障描述符

        push    dword ptr [eax+44h]

        push    dword ptr [eax+10h]     保存不可屏蔽中断故障描述符

        push    dword ptr [eax+14h]

        mov     edi,KissIdt

        mov     esi,offset FLAT:_IDT

        mov     ecx,offset FLAT:_IDTLEN ; 

        shr     ecx,2

        rep     movsd

        pop     dword ptr [eax+14h]     恢复双重故障描述符        

pop     dword ptr [eax+10h]

        pop     dword ptr [eax+44h]     恢复不可屏蔽中断故障描述符

        pop     dword ptr [eax+40h]

ifdef QLOCK_STAT_GATHER

        EXTRNP  KiCaptureQueuedSpinlockRoutines,0,,FASTCALL

        fstCall KiCaptureQueuedSpinlockRoutines

endif

kiss_notp0:

ifndef NT_UP

告知引导处理器该处理器已经开始运行.

        stdCall _KiProcessorStart

endif

; A new processor can't come online while execution is frozen

; Take freezelock while adding a processor to the system

; NOTE: don't use SPINLOCK macro - it has debugger stuff in it

@@:     test    _KiFreezeExecutionLock, 1

        jnz     short @b

        lock bts _KiFreezeExecutionLock, 0

        jc      short @b

添加当前处理器到活动列表,并更新BroadcastMasks

        mov     ecx, dword ptr KissPbNumber 标记该处理器为活动的

        mov     byte ptr PCR[PcNumber], cl

        mov     eax, 1

        shl     eax, cl                         关联字段

        mov     PCR[PcSetMember], eax

        mov     PCR[PcSetMemberCopy], eax

        mov     PCR[PcPrcbData.PbSetMember], eax

初始化处理器间的中断向量表并自增就绪处理器计数值以开启内核调试器.

        stdCall   _HalInitializeProcessor, <dword ptr KissPbNumber, KissLoaderBlock> 

//为当前处理器初始化其HAL中的PCR和处理器间中断向量

ifdef _APIC_TPR_

; 通过hal记录IRQL表,并传递过来

        mov     eax, KissLoaderBlock

        mov     eax, [eax]+LpbExtension

        mov     ecx, [eax]+LpeHalpIRQLToTPR

        mov     _HalpIRQLToTPR, ecx

        mov     ecx, [eax]+LpeHalpVectorToIRQL

        mov     _HalpVectorToIRQL, ecx

endif

        mov     eax, PCR[PcSetMember]

        or      _KeActiveProcessors, eax    活动处理器的新关联值

初始化ABIOS数据结构.

;  KiInitializeAbios例程必须在KeLoaderBlock初始化完成之后调用

        stdCall   _KiInitializeAbios, <dword ptr KissPbNumber>

        inc     _KeNumberProcessors         又有新的处理器被激活

        xor     eax, eax                       释放执行锁

        mov     _KiFreezeExecutionLock, eax

        cmp     byte ptr KissPbNumber, 0

        jnz     @f

不能在调试器中暂停

        stdCall   _KdInitSystem, <0,_KeLoaderBlock>

if  DEVL

给予调试器获得控制权的机会.

        POLL_DEBUGGER

endif   ; DEVL

@@:

        nop                             留一个位置给int3指令

设置初始化的IRQL = HIGH_LEVEL

        RaiseIrql HIGH_LEVEL

        mov     KissIrql, al

        or      _KiBootFeatureBits, KF_CMPXCHG8B ; CMPXCHG8B是XP的一个标识

; 初始化ebp,esp和其他参数寄存器,为初始化内核做准备

        mov     ebx, KissIdleThread

        mov     edx, KissIdleStack

        mov     eax, KissPbNumber

        and     edx, NOT 3h             4字节边界对齐

        xor     ebp, ebp                ; (ebp) = 0.   没有更多的栈帧了

        mov     esp, edx

; 为空闲线程栈NPX_SAVE_AREA预留空间并初始化

       sub    esp, NPX_FRAME_LENGTH+KTRAP_FRAME_LENGTH+KTRAP_FRAME_ALIGN

       push    CR0_EM+CR0_TS+CR0_MP    Cr0NpxState预留空间

; arg6 - LoaderBlock

; arg5 - processor number

; arg4 - addr of prcb

; arg3 - idle thread's stack

; arg2 - addr of current thread obj

; arg1 - addr of current process obj

初始化系统数据结构和HAL

stdCall    _KiInitializeKernel,<offset _KiInitialProcess,ebx,edx,dword ptr PCR[PcPrcb],eax,_KeLoaderBlock>

//执行内核初始化

; 设置空闲线程的优先级.

        mov     ebx,PCR[PcPrcbData+PbCurrentThread] 设置空闲线程的地址

        mov     byte ptr [ebx]+ThPriority, 0 设置优先级为0

; Control is returned to the idle thread with IRQL at HIGH_LEVEL. Lower IRQL

; to DISPATCH_LEVEL and set wait IRQL of idle thread.

        sti

        LowerIrql DISPATCH_LEVEL

        mov     byte ptr [ebx]+ThWaitIrql, DISPATCH_LEVEL   

// KiInitializeKernel函数返回以后,启动中断,将IRQL降低为DISPATCH_LEVEL

// 从而允许线程调度器选择新的线程   

        mov     ebx, PCR[PcSelfPcr]     获取PCR的地址

; 在一个多处理器系统中,引导处理器直接进入空闲循环.而其他的处理器不会直接进入空闲循环;

; 而是等到所有处理器已经开启并且引导主扇区允许进入为止;

; 屏障KiBarrierWait对于系统的第一个处理器并不起作用,而仅对后续的处理器起作用

ifndef NT_UP

@@:     cmp     _KiBarrierWait, 0       判断是否设置了KiBarrierWait

        YIELD

        jnz     short @b              

endif

        push    0                       

        jmp     @KiIdleLoop@0           进入空闲循环

stdENDP _KiSystemStartup

1.2 内核初始化

内核的初始化主要是内核各个组件的初始化,但由于这些内核组件之间有紧密的耦合关系,所以它们的初始化并不是简单地顺序执行初始化。为了解决在初始化过程中的相互依赖性问题,内核的初始化分两个阶段进行,称为阶段0和阶段1。大多数内核组件的初始化函数相应地带有一个整数参数,以指明一次调用是阶段0 初始化还是阶段1 初始化,而有些组件的初始化函数通过检查一个全局变量InitializationPhase 的值来判断当前处于哪个阶段。

我们首先来看阶段0 初始化。阶段0 初始化的目的是,将阶段1 初始化所要用到的基本数据结构建立起来。在阶段0 初始化过程中,中断被禁止,因此处理器可以顺序地执行自己的初始化逻辑。KiSystemStartup 函数首先初始化处理器的状态,包括调整它的IDT,初始化TSS(Task State Segment),以及构造PCR(Processor Control Region)。然后,调用HalInitializeProcessor 函数,为当前处理器初始化其HAL 中的PCR 和处理器间中断向量;接着调用KiInitializeKernel 函数,执行内核初始化。最后,当前线程蜕变成一个空闲线程。

因此,KiInitializeKernel 函数是实际执行内核初始化的函数,其代码位于base\ntos\ke\i386\kernlini.c 文件中。它的职责是:初始化内核数据结构,初始化空闲线程和进程对象,初始化PCR,然后调用执行体初始化函数ExpInitializeExecutive,最后返回。

VOID

KiInitializeKernel (

    IN PKPROCESS Process,

    IN PKTHREAD Thread,

    IN PVOID IdleStack,

    IN PKPRCB Prcb,

    IN CCHAR Number,

    PLOADER_PARAMETER_BLOCK LoaderBlock

    )

{

    ULONG DirectoryTableBase[2];

    PVOID DpcStack;

    KIRQL OldIrql;

    PKPCR Pcr;

    BOOLEAN NpxFlag;

#if !defined(NT_UP)

    BOOLEAN FxsrPresent;

    BOOLEAN XMMIPresent;

#endif

    ULONG FeatureBits;

#if defined(KE_MULTINODE)

    LONG  Index;

#endif

    KiSetProcessorType();

    KiSetCR0Bits();

    NpxFlag = KiIsNpxPresent();

    Pcr = KeGetPcr();

// 初始化处理器的电源状态

    PoInitializePrcb(Prcb);

// 检测不支持的处理器的版本

    if (Prcb->CpuType == 3) {

        KeBugCheckEx(UNSUPPORTED_PROCESSOR,0x386,0,0,0);

    }

// 获取处理器相关特性

    FeatureBits = KiGetFeatureBits();

    //如果主机处理器支持特性,那么在加载选项中设置开启不可执行保护

    SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTIN;

    if (strstr(KeLoaderBlock->LoadOptions"NOEXECUTE=ALWAYSON") != NULL) {

        SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSON;

        FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;

    } else if (strstr(KeLoaderBlock->LoadOptions"NOEXECUTE=OPTOUT") != NULL) {

        SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_OPTOUT;

        FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;

    } else if (strstr(KeLoaderBlock->LoadOptions"NOEXECUTE=OPTIN") != NULL) {

        FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;

    } else if (strstr(KeLoaderBlock->LoadOptions"NOEXECUTE=ALWAYSOFF") != NULL) {

        SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;

        FeatureBits |= KF_GLOBAL_32BIT_EXECUTE;

    } else if (strstr(KeLoaderBlock->LoadOptions"NOEXECUTE") != NULL) {

        FeatureBits |= KF_GLOBAL_32BIT_NOEXECUTE;

    } else if (strstr(KeLoaderBlock->LoadOptions"EXECUTE") != NULL) {

        SharedUserData->NXSupportPolicy = NX_SUPPORT_POLICY_ALWAYSOFF;

        FeatureBits |= KF_GLOBAL_32BIT_EXECUTE;

    }

#if defined (_X86PAE_)

    if ((FeatureBits & KF_NOEXECUTE) == 0) {

        FeatureBits &= ~KF_GLOBAL_32BIT_NOEXECUTE;

    }

    if ((FeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) != 0) {

        KiEnableNXSupport();

    }

#endif

    Prcb->FeatureBits = FeatureBits;

// 初始化PRCB中的ProcessorControlSpace内容,使得本地内核调试器可以像GDT一样获得数据

    KiSaveProcessorControlState(&Prcb->ProcessorState);

    // 获得处理器缓存大小信息

    KiGetCacheInformation();

    // 初始化每个处理器的锁数据

    KiInitSpinLocks(PrcbNumber);

// 如果初始的处理器已经被初始化,那么初始化每个系统数据结构

    if (Number == 0) {

        KeNodeBlock[0] = &KiNode0;

#if defined(KE_MULTINODE)

        for (Index = 1; Index < MAXIMUM_CCNUMA_NODES; Index++) {

            //

            // Set temporary node.

            //

            KeNodeBlock[Index] = &KiNodeInit[Index];

        }

#endif

        Prcb->ParentNode = KeNodeBlock[0];

        KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;

        // Initial setting for global Cpu & Stepping levels

        KeI386NpxPresent = NpxFlag;

        KeI386CpuType = Prcb->CpuType;

        KeI386CpuStep = Prcb->CpuStep;

        KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;

        KeProcessorLevel = (USHORT)Prcb->CpuType;

        if (Prcb->CpuID == 0) {

            KeProcessorRevision = 0xFF00 |

                                  (((Prcb->CpuStep >> 4) + 0xa0 ) & 0x0F0) |

                                  (Prcb->CpuStep & 0xf);

        } else {

            KeProcessorRevision = Prcb->CpuStep;

        }

        KeFeatureBits = FeatureBits;

        KeI386FxsrPresent = ((KeFeatureBits & KF_FXSR) ? TRUE:FALSE);

        KeI386XMMIPresent = ((KeFeatureBits & KF_XMMI) ? TRUE:FALSE);

        // As of Windows XP, cmpxchg8b is a required instruction.

        if ((KeFeatureBits & KF_

) == 0) {

            ULONG Vendor[3];

            RtlCopyMemory(VendorPrcb->VendorStringsizeof(Vendor));

            KeBugCheckEx(UNSUPPORTED_PROCESSOR,

                         (1 << 24 )     

                          | (Prcb->CpuType << 16) | Prcb->CpuStep,

                         Vendor[0],

                         Vendor[1],

                         Vendor[2]);

        }

   // 降低IRQL到APC级别

        KeLowerIrql(APC_LEVEL);

   // 初始化内核自旋锁

        KeInitializeSpinLock(&KiFreezeExecutionLock);

   // 初始化兼容锁

        KeInitializeSpinLock(&Ki486CompatibilityLock);

//此处省略部分无关代码

        // Performance architecture independent initialization.

        KiInitSystem();

        // 初始化空闲线程和进程对象

        DirectoryTableBase[0] = 0;

        DirectoryTableBase[1] = 0;

        InitializeListHead(&KiProcessListHead);

        KeInitializeProcess(Process,

                            (KPRIORITY)0,

                            (KAFFINITY)(0xffffffff),

                            &DirectoryTableBase[0],

                            FALSE);

        Process->QuantumReset = MAXCHAR;  //此处设置为最大值

#if !defined(NT_UP)

//此处都是设置处理器的相关特性,非重点

    } else {

        // Adjust global cpu setting to represent lowest of all processors

        FxsrPresent = ((FeatureBits & KF_FXSR) ? TRUE:FALSE);

        if (FxsrPresent != KeI386FxsrPresent) {

            // FXSR support must be available on all processors or on none

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_FXSR, 0, 0, 0);

        }

        XMMIPresent = ((FeatureBits & KF_XMMI) ? TRUE:FALSE);

        if (XMMIPresent != KeI386XMMIPresent) {

            // XMMI support must be available on all processors or on none

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_XMMI, 0, 0, 0);

        }

        if (NpxFlag != KeI386NpxPresent) {

            // NPX support must be available on all processors or on none

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, 0x387, 0, 0, 0);

        }

        if ((ULONG)(Prcb->CpuType) != KeI386CpuType) {

            if ((ULONG)(Prcb->CpuType) < KeI386CpuType) {

                // What is the lowest CPU type

                KeI386CpuType = (ULONG)Prcb->CpuType;

                KeProcessorLevel = (USHORT)Prcb->CpuType;

            }

        }

        if ((KiBootFeatureBits & KF_CMPXCHG8B)  &&  !(FeatureBits & KF_CMPXCHG8B)) {

            // cmpxchg8b must be available on all processors, if installed at boot

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_CMPXCHG8B, 0, 0, 0);

        }

        if ((KeFeatureBits & KF_GLOBAL_PAGE)  &&  !(FeatureBits & KF_GLOBAL_PAGE)) {

            // Global page support must be available on all processors, if on boot processor

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_GLOBAL_PAGE, 0, 0, 0);

        }

        if ((KeFeatureBits & KF_PAT)  &&  !(FeatureBits & KF_PAT)) {

            // PAT must be available on all processors, if on boot processor

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_PAT, 0, 0, 0);

        }

        if ((KeFeatureBits & KF_MTRR)  &&  !(FeatureBits & KF_MTRR)) {

            // MTRR must be available on all processors, if on boot processor

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTEDKF_MTRR, 0, 0, 0);

        }

        if ((KeFeatureBits & KF_NOEXECUTE) && !(FeatureBits & KF_NOEXECUTE)) {

            // KF_NOEXECUTE must be available on all processors, if on boot processor.

            KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED,

                         KF_NOEXECUTE,

                         0,

                         0,

                         0);

if ((KeFeatureBits & KF_FAST_SYSCALL) != (FeatureBits & KF_FAST_SYSCALL)) {

            // If this feature is not available on all processors don't use it at all.

            KiFastSystemCallDisable = 1;

        }

        if ((KeFeatureBits & KF_XMMI64) != (FeatureBits & KF_XMMI64)) {

            // If not all processors support Streaming SIMD Extensions 64bit FP don't use it at all.

            KeFeatureBits &= ~KF_XMMI64;

        }

        // Use lowest stepping value

        if (Prcb->CpuStep < KeI386CpuStep) {

            KeI386CpuStep = Prcb->CpuStep;

            if (Prcb->CpuID == 0) {

                KeProcessorRevision = 0xFF00 |

                                      ((Prcb->CpuStep >> 8) + 'A') |

                                      (Prcb->CpuStep & 0xf);

            } else {

                KeProcessorRevision = Prcb->CpuStep;

            }

        }

        // Use subset of all NT feature bits available on each processor

        KeFeatureBits &= FeatureBits;

        // 降低IRQLDISPATCH级别.

        KeLowerIrql(DISPATCH_LEVEL);

#endif

    }

    //更新处理器特性信息

    SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =

        (KeFeatureBits & KF_MMX) ? TRUE : FALSE;

    SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =

        (KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;

    SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =

        ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE : FALSE;

    SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =

        ((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE : FALSE;

    SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =

        (KeFeatureBits & KF_3DNOW) ? TRUE : FALSE;

    SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =

        (KeFeatureBits & KF_RDTSC) ? TRUE : FALSE;

//初始化空闲线程对象

//1.初始化内核堆栈为指定的空闲堆栈

 //2.设置下一个处理器的序号

 //3.设置线程的优先级为最高

 //4.设置线程的状态为活动的

 //5.将线程关联到指定的处理器上

 //6.设置线程的等待中断级别

//7.在进程活动处理器集中设置指定的处理器成员

KeInitializeThread(Thread, (PVOID)((ULONG)IdleStack),(PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,

                     (PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULLProcess);

    Thread->NextProcessor = Number;

    Thread->Priority = HIGH_PRIORITY;

    Thread->State = Running;

    Thread->Affinity = (KAFFINITY)(1<<Number);

    Thread->WaitIrql = DISPATCH_LEVEL;

    SetMember(NumberProcess->ActiveProcessors);

// 初始化进程块信息PCR

    Prcb->CurrentThread = Thread;

    Prcb->NextThread = (PKTHREAD)NULL;

    Prcb->IdleThread = Thread;

// 调用执行体初始化函数例程

    try {

        ExpInitializeExecutive(NumberLoaderBlock);

    } except(KeBugCheckEx(PHASE0_EXCEPTION, (ULONG)GetExceptionCode(), (ULONG_PTR)GetExceptionInformation(),

                          0,0), EXCEPTION_EXECUTE_HANDLER) {

        ; // 永远不要运行到这里

    }

        // If the initial processor is being initialized, then compute the timer table reciprocal value and reset the PRCB values for the

        // controllable DPC behavior in order to reflect any registry overrides.

    if (Number == 0) {

        KiTimeIncrementReciprocal = KeComputeReciprocal((LONG)KeMaximumIncrement,&KiTimeIncrementShiftCount);

        Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;

        Prcb->MinimumDpcRate = KiMinimumDpcRate;

        Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;

        // Processor 0's DPC stack was temporarily allocated on the Double Fault Stack, switch to a proper kernel stack now.

        DpcStack = MmCreateKernelStack(FALSE, 0);

        if (DpcStack == NULL) {

            KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);

        }

        Prcb->DpcStack = DpcStack;

        // Allocate 8k IOPM bit map saved area to allow BiosCall swap bit maps.

        Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,PAGE_SIZE * 2,'  eK');

        if (Ki386IopmSaveArea == NULL) {

            KeBugCheckEx(NO_PAGES_AVAILABLE, 2, PAGE_SIZE * 2, 0, 0);

        }

    }

    // 先提高中断级别,然后再设置指定的空闲进程的优先级别为0

    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 

    KeSetPriorityThread(Thread, (KPRIORITY)0);

    //如果线程并未被选中运行在当前的处理器上,那么检查是否还有就绪的线程,否则就将此处理器设置为IdleSummary

    KiAcquirePrcbLock(Prcb);

    if (Prcb->NextThread == NULL) {

        SetMember(NumberKiIdleSummary);

    }

    KiReleasePrcbLock(Prcb);  //释放PRCB锁

    KeRaiseIrql(HIGH_LEVEL, &OldIrql);  //提高IRQL

    // 到此处为止处理器的初始化完成

    LoaderBlock->Prcb = (ULONG)NULL;

    return;

}

ExpInitializeExecutive 函数的代码位于base\ntos\init\initos.c 文件中,它调用HalInitSystem以初始化HAL,调用ExInitSystem 以初始化执行体组件的各种数据结构,调用MmInitSystem 以初始化内存管理器和内存池,调用ObInitSystem 以初始化对象管理器,调用SeInitSystem 以初始化安全子系统,调用PsInitSystem 以初始化进程/线程管理器,调用PpInitSystem 以初始化即插即用管理器,调用DbgkInitialize 以初始化调试子系统。通常,这些执行体组件的阶段0 初始化相对简单,以初始化组件的内部状态为主,因而经过阶段0 初始化以后仅可以提供最基本的服务。

VOID

ExpInitializeExecutive(

    IN ULONG Number,

    IN PLOADER_PARAMETER_BLOCK LoaderBlock

    )

{

    PFN_COUNT PagesToBurn;

    PCHAR Options;

    PCHAR MemoryOption;

    NTSTATUS Status;

    PKLDR_DATA_TABLE_ENTRY DataTableEntry;

    PLIST_ENTRY NextEntry;

    ANSI_STRING AnsiString;

    STRING NameString;

    CHAR Buffer[ 256 ];

    BOOLEAN BufferSizeOk;

    ULONG ImageCount;

    ULONG i;

    ULONG_PTR ResourceIdPath[3];

    PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;

    PMESSAGE_RESOURCE_DATA  MessageData;

    PLIST_ENTRY NextMd;

    PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;

    if (!ExpIsLoaderValid(LoaderBlock)) {

        KeBugCheckEx(MISMATCHED_HAL,

                     3,

                     LoaderBlock->Extension->Size,

                     LoaderBlock->Extension->MajorVersion,

                     LoaderBlock->Extension->MinorVersion

                     );

    }

// 初始化处理器控制块池旁视指针

#if !defined(_AMD64_)

    ExInitPoolLookasidePointers();

#endif

    if (Number == 0) {

        extern BOOLEAN ExpInTextModeSetup;

// 判断是否为文本模式的安装进程祸远程的引导客户端

        ExpInTextModeSetup = FALSE;

        IoRemoteBootClient = FALSE;

        if (LoaderBlock->SetupLoaderBlock != NULL) {

            if ((LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_TEXTMODE) != 0) {

                ExpInTextModeSetup = TRUE;

            }

            if ((LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_REMOTE_BOOT) != 0) {

                IoRemoteBootClient = TRUE;

                ASSERT_memicmpLoaderBlock->ArcBootDeviceName"net(0)", 6 ) == 0 );

            }

        }

#if defined(REMOTE_BOOT)

        SharedUserData->SystemFlags = 0;

        if (IoRemoteBootClient) {

            SharedUserData->SystemFlags |= SYSTEM_FLAG_REMOTE_BOOT_CLIENT;

        }

#endif // defined(REMOTE_BOOT)

// 暗示该代码处于初始化0阶段中

        InitializationPhase = 0L;

        Options = LoaderBlock->LoadOptions;

        if (Options != NULL) {

            // If in BBT mode, remove the requested amount of memory from the loader block and use it for BBT purposes instead.

            _strupr(Options);

            MemoryOption = strstr(Options"PERFMEM");

            if (MemoryOption != NULL) {

                MemoryOption = strstr (MemoryOption,"=");

                if (MemoryOption != NULL) {

                    PagesToBurn = (PFN_COUNTatol (MemoryOption + 1);

// 将MB转换为页数

                    PagesToBurn *= ((1024 * 1024) / PAGE_SIZE);

                    if (PagesToBurn != 0) {

                        PERFINFO_INIT_TRACEFLAGS(OptionsMemoryOption);

                        BBTPagesToReserve = ExBurnMemory (LoaderBlock,

                                                          PagesToBurn,

                                                          LoaderBBTMemory,

                                                          &BBTMemoryDescriptor);

                    }

                }

            }

            // Burn memory - consume the amount of memory specified in the OS Load Options.  

            // This is used for testing reduced memory configurations.

            MemoryOption = strstr(Options"BURNMEMORY");

            if (MemoryOption != NULL) {

                MemoryOption = strstr(MemoryOption,"=");

                if (MemoryOption != NULL ) {

                    PagesToBurn = (PFN_COUNTatol (MemoryOption + 1);

                    PagesToBurn *= ((1024 * 1024) / PAGE_SIZE);

                    if (PagesToBurn != 0) {

                        ExBurnMemory (LoaderBlock,

                                      PagesToBurn,

                                      LoaderBad,

                                      NULL);

                    }

                }

            }

        }

// 使用loader加载过的表初始化翻译表

        InitNlsTableBase = LoaderBlock->NlsData->AnsiCodePageData;

        InitAnsiCodePageDataOffset = 0;

        InitOemCodePageDataOffset = (ULONG)((PUCHAR)LoaderBlock->NlsData->OemCodePageData - (PUCHAR)LoaderBlock->NlsData->AnsiCodePageData);

        InitUnicodeCaseTableDataOffset = (ULONG)((PUCHAR)LoaderBlock->NlsData->UnicodeCaseTableData - (PUCHAR)LoaderBlock->NlsData->AnsiCodePageData);

        RtlInitNlsTables(

            (PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),

            (PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),

            (PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),

            &InitTableInfo

            );

        RtlResetRtlTranslations(&InitTableInfo);

// 1.初始化硬件抽象层

        if (HalInitSystem(InitializationPhaseLoaderBlock) == FALSE) {

            KeBugCheck(HAL_INITIALIZATION_FAILED);

        }

#if defined(_APIC_TPR_)

        HalpIRQLToTPR = LoaderBlock->Extension->HalpIRQLToTPR;

        HalpVectorToIRQL = LoaderBlock->Extension->HalpVectorToIRQL;

#endif

// HAL已经初始化成功,所以可以开启中断

#if defined(_X86_)

        _enable();

#endif

        // Set the interrupt time forward so the Win32 tick count will wrap

        // within one hour to make rollover errors show up in fewer than 49.7

        // days.

#if DBG

        KeAdjustInterruptTime((LONGLONG)(MAXULONG - (60 * 60 * 1000)) * 10 * 1000);

#endif

        SharedUserData->CryptoExponent = 0;

#if DBG

        NtGlobalFlag |= FLG_ENABLE_CLOSE_EXCEPTIONS |

                        FLG_ENABLE_KDEBUG_SYMBOL_LOAD;

#endif

        Status = RtlFormatBuffer2Buffer,

                                  sizeof(Buffer),

                                  "C:%s",

                                  LoaderBlock->NtBootPathName,

                                  0

                                 );

        if (! NT_SUCCESS(Status)) {

            KeBugCheck(SESSION3_INITIALIZATION_FAILED);

        }

        RtlInitString( &AnsiStringBuffer );

        Buffer[ --AnsiString.Length ] = '\0';

        NtSystemRoot.Buffer = SharedUserData->NtSystemRoot;

        NtSystemRoot.MaximumLength = sizeofSharedUserData->NtSystemRoot );

        NtSystemRoot.Length = 0;

        Status = RtlAnsiStringToUnicodeString( &NtSystemRoot,

                                               &AnsiString,

                                               FALSE

                                             );

        if (!NT_SUCCESSStatus )) {

            KeBugCheck(SESSION3_INITIALIZATION_FAILED);

            }

        // Find the address of BugCheck message block resource and put it in KiBugCodeMessages.

        // WARNING: This code assumes that the KLDR_DATA_TABLE_ENTRY for ntoskrnl.exe is always the first in the loaded module list.

        DataTableEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,

                                           KLDR_DATA_TABLE_ENTRY,

                                           InLoadOrderLinks);

        ResourceIdPath[0] = 11;

        ResourceIdPath[1] = 1;

        ResourceIdPath[2] = 0;

        Status = LdrFindResource_U (DataTableEntry->DllBase,

                                    ResourceIdPath,

                                    3,

                                    (VOID *) &ResourceDataEntry);

        if (NT_SUCCESS(Status)) {

            Status = LdrAccessResource (DataTableEntry->DllBase,

                                        ResourceDataEntry,

                                        &MessageData,

                                        NULL);

            if (NT_SUCCESS(Status)) {

                KiBugCodeMessages = MessageData;

            }

        }

#if !defined(NT_UP)

        // Verify that the kernel and HAL images are suitable for MP systems.

        // N.B. Loading of kernel and HAL symbols now occurs in kdinit.

        ImageCount = 0;

        NextEntry = LoaderBlock->LoadOrderListHead.Flink;

        while ((NextEntry != &LoaderBlock->LoadOrderListHead) && (ImageCount < 2)) {

            DataTableEntry = CONTAINING_RECORD(NextEntry,

                                               KLDR_DATA_TABLE_ENTRY,

                                               InLoadOrderLinks);

            ImageCount += 1;

            if ( !MmVerifyImageIsOkForMpUse(DataTableEntry->DllBase) ) {

                KeBugCheckEx(UP_DRIVER_ON_MP_SYSTEM,

                            (ULONG_PTR)DataTableEntry->DllBase,

                            0,

                            0,

                            0);

            }

            NextEntry = NextEntry->Flink;

        }

#endif // !defined(NT_UP)

        CmpInitSystemVersion(0, LoaderBlock);

   // 2.初始化执行体组件的各种数据结构

        if (!ExInitSystem()) {

            KeBugCheck(PHASE0_INITIALIZATION_FAILED);

        }

        // Get multinode configuration (if any).

        KeNumaInitialize();

   // 3.初始化内存管理器和内存池

        if (MmInitSystem (0, LoaderBlock) == FALSE) {

            KeBugCheck(PHASE0_INITIALIZATION_FAILED);

        }

        // 扫描已经加载的模块列表并加载驱动映像符号

        ImageCount = 0;

        NextEntry = LoaderBlock->LoadOrderListHead.Flink;

        while (NextEntry != &LoaderBlock->LoadOrderListHead) {

            BufferSizeOk = TRUE;

            if (ImageCount >= 2) {

                ULONG Count;

                WCHAR *Filename;

                ULONG Length;

                // 获得下一个组件的数据表入口的地址

                DataTableEntry = CONTAINING_RECORD(NextEntry,

                                                   KLDR_DATA_TABLE_ENTRY,

                                                   InLoadOrderLinks);

      

// 通过下一个组件的内核调试器加载符号

                if (DataTableEntry->FullDllName.Buffer[0] == L'\\') {

                    // 完整的全路径名称已经可用

                    Filename = DataTableEntry->FullDllName.Buffer;

                    Length = DataTableEntry->FullDllName.Length / sizeof(WCHAR);

                    if (sizeof(Buffer) < Length + sizeof(ANSI_NULL)) {

                        // DllName太长了.

                        BufferSizeOk = FALSE;

                    } else {

                        Count = 0;

                        do {

                            Buffer[Count++] = (CHAR)*Filename++;

                        } while (Count < Length);

                        Buffer[Count] = 0;

                    }

                } else {

                    // 假设组件为驱动

                    if (sizeof(Buffer) < 18 + NtSystemRoot.Length / sizeof(WCHAR) - 2

                                            + DataTableEntry->BaseDllName.Length / sizeof(WCHAR)

                                            + sizeof(ANSI_NULL)) {

                        // ignore the driver entry, it must have been corrupt.

                        BufferSizeOk = FALSE;

                    } else {

                        Status = RtlFormatBuffer2 (Buffer,

                                                     sizeof(Buffer),

                                                     "%ws\\System32\\Drivers\\%wZ",

                                                     &SharedUserData->NtSystemRoot[2],

                                                     &DataTableEntry->BaseDllName);

                        if (! NT_SUCCESS(Status)) {

                            KeBugCheck(PHASE0_INITIALIZATION_FAILED);

                        }

                    }

                }

                if (BufferSizeOk) {

                    RtlInitString (&NameStringBuffer );

                    DbgLoadImageSymbols (&NameString,

                                         DataTableEntry->DllBase,

                                         (ULONG)-1);

#if !defined(NT_UP)

                    if (!MmVerifyImageIsOkForMpUse(DataTableEntry->DllBase)) {

                        KeBugCheckEx(UP_DRIVER_ON_MP_SYSTEM,(ULONG_PTR)DataTableEntry->DllBase,0,0,0);

                    }

#endif // NT_UP

                }

            }

            ImageCount += 1;

            NextEntry = NextEntry->Flink;

        }

        // If break after symbol load is specified, then break into the debugger.

        if (KdBreakAfterSymbolLoad != FALSE) {

            DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);

        }

        // Turn on the headless terminal now, if we are of a sufficiently new vintage of loader

        if (LoaderBlock->Extension->Size >= sizeof (LOADER_PARAMETER_EXTENSION)) {

            HeadlessInit(LoaderBlock);

        }

        // 以上的这些字段只被合法的第三方的32位软件所支持

// New code should call NtQueryInformationSystem() to get them.

#if defined(_WIN64)

        SharedUserData->Reserved1 = 0x7ffeffff; // 2gb HighestUserAddress

        SharedUserData->Reserved3 = 0x80000000; // 2gb SystemRangeStart

#else

        // Set the highest user address and the start of the system range in the shared memory block.

        // N.B. This is not a constant value if the target system is an x86 with 3gb of user virtual address space.

        SharedUserData->Reserved1 = (ULONG)MM_HIGHEST_USER_ADDRESS;

        SharedUserData->Reserved3 = (ULONG)MmSystemRangeStart;

#endif

        // 将NLS表映射到分页池并重置翻译表.

        // 遍历内存描述符并获取NLS数据大小.

        NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;

        while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {

            MemoryDescriptor = CONTAINING_RECORD(NextMd,

                                                 MEMORY_ALLOCATION_DESCRIPTOR,

                                                 ListEntry);

            if (MemoryDescriptor->MemoryType == LoaderNlsData) {

                InitNlsTableSize += MemoryDescriptor->PageCount*PAGE_SIZE;

            }

            NextMd = MemoryDescriptor->ListEntry.Flink;

        }

        InitNlsTableBase = ExAllocatePoolWithTag (NonPagedPool,

                                                  InitNlsTableSize,

                                                  ' slN');

        if (InitNlsTableBase == NULL) {

            KeBugCheck(PHASE0_INITIALIZATION_FAILED);

        }

        // Copy the NLS data into the dynamic buffer so that we can

        // free the buffers allocated by the loader. The loader guarantees

        // contiguous buffers and the base of all the tables is the ANSI

        // code page data.

        RtlCopyMemory (InitNlsTableBase,

                       LoaderBlock->NlsData->AnsiCodePageData,

                       InitNlsTableSize);

        RtlInitNlsTables ((PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),

            (PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),

            (PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),

            &InitTableInfo);

        RtlResetRtlTranslations (&InitTableInfo);

        CmpInitSystemVersion(1, NULL);

        if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {

            PVOID StackTraceDataBase;

            ULONG StackTraceDataBaseLength;

            NTSTATUS Status;

            StackTraceDataBaseLength =  512 * 1024;

            switch ( MmQuerySystemSize() ) {

                case MmMediumSystem :

                    StackTraceDataBaseLength = 1024 * 1024;

                    break;

                case MmLargeSystem :

                    StackTraceDataBaseLength = 2048 * 1024;

                    break;

            }

            StackTraceDataBase = ExAllocatePoolWithTagNonPagedPool,

                                         StackTraceDataBaseLength,

                                         'catS');

            if (StackTraceDataBase != NULL) {

                KdPrint(( "INIT: Kernel mode stack back trace enabled "

                          "with %u KB buffer.\n"StackTraceDataBaseLength / 1024 ));

                Status = RtlInitializeStackTraceDataBase (StackTraceDataBase,

                                                          StackTraceDataBaseLength,

                                                          StackTraceDataBaseLength);

            } else {

                Status = STATUS_NO_MEMORY;

            }

            if (!NT_SUCCESSStatus )) {

                KdPrint(( "INIT: Unable to initialize stack trace data base - Status == %lx\n"Status ));

            }

        }

        if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {

            RtlInitializeExceptionLog(MAX_EXCEPTION_LOG);

        }

        ExInitializeHandleTablePackage();

#if DBG

        // Allocate and zero the system service count table.

        //

        KeServiceDescriptorTable[0].Count = (PULONG)ExAllocatePoolWithTag(NonPagedPool,KiServiceLimit * sizeof(ULONG),'llac');

        KeServiceDescriptorTableShadow[0].Count = KeServiceDescriptorTable[0].Count;

        if (KeServiceDescriptorTable[0].Count != NULL ) {

            RtlZeroMemory((PVOID)KeServiceDescriptorTable[0].Count,

                          KiServiceLimit * sizeof(ULONG));

        }

#endif

        if (!ObInitSystem()) {

            KeBugCheck(OBJECT_INITIALIZATION_FAILED); //初始化对象管理器

        }

        if (!SeInitSystem()) {

            KeBugCheck(SECURITY_INITIALIZATION_FAILED);//初始化安全子系统

        }

        if (PsInitSystem(0, LoaderBlock) == FALSE) {

            KeBugCheck(PROCESS_INITIALIZATION_FAILED); //初始化进程/线程管理器

        }

        if (!PpInitSystem()) {

            KeBugCheck(PP0_INITIALIZATION_FAILED);  //初始化即插即用管理器

        }

        DbgkInitialize ();   //初始化调试子系统

   //这些执行体组件的阶段初始化以初始化组件内部状态为主,仅可以提供最基本的服务

        // Compute the tick count multiplier that is used for computing the

        // windows millisecond tick count and copy the resultant value to

        // the memory that is shared between user and kernel mode.

        ExpTickCountMultiplier = ExComputeTickCountMultiplier(KeMaximumIncrement);

        SharedUserData->TickCountMultiplier = ExpTickCountMultiplier;

        // 在共享内存中设置OS的版本号

        SharedUserData->NtMajorVersion = NtMajorVersion;

        SharedUserData->NtMinorVersion = NtMinorVersion;

        // Set the supported image number range used to determine by the loader if a particular image can be executed on the host system.

        // Eventually this will need to be dynamically computed. Also set the architecture specific feature bits.

#if defined(_AMD64_)

        SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_AMD64;

        SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;

#elif defined(_X86_)

        SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_I386;

        SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_I386;

#else

#error "no target architecture"

#endif

}

    else {

        // 初始化硬件抽象层

        if (HalInitSystem(InitializationPhaseLoaderBlock) == FALSE) {

            KeBugCheck(HAL_INITIALIZATION_FAILED);

        }

    }

    return;

}

阶段0 初始化完成以后,系统的线程调度器开始工作。特别值得一提的是,进程管理器在阶段0 初始化过程(PspInitPhase0)中,除了初始化其内部的状态变量,它也为初始进程创建一个进程对象,并将其命名为“Idle”。另外,它还创建了“System”进程,以及一个系统线程,此系统线程的开始例程为Phase1Initialization 函数。然而,此线程并不立即被执行,因为在阶段0 初始化过程中中断是禁止的。

BOOLEAN

PspInitPhase0 (

    IN PLOADER_PARAMETER_BLOCK LoaderBlock

    )

{

    UNICODE_STRING NameString;

    OBJECT_ATTRIBUTES ObjectAttributes;

    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;

    HANDLE ThreadHandle;

    PETHREAD Thread;

    MM_SYSTEMSIZE SystemSize;

    ULONG i;

#if DBG

    NTSTATUS Status;

#endif

    SystemSize = MmQuerySystemSize ();

    PspDefaultPagefileLimit = (ULONG)-1;

#ifdef _WIN64

    if (sizeof (TEB) > 8192 || sizeof (PEB) > 4096) {

#else

    if (sizeof (TEB) > 4096 || sizeof (PEB) > 4096) {

#endif

        KeBugCheckEx (PROCESS_INITIALIZATION_FAILED, 99, sizeof (TEB), sizeof (PEB), 99);

    }

    switch (SystemSize) {

        case MmMediumSystem :

            PsMinimumWorkingSet += 10;

            PsMaximumWorkingSet += 100;

            break;

        case MmLargeSystem :

            PsMinimumWorkingSet += 30;

            PsMaximumWorkingSet += 300;

            break;

        case MmSmallSystem :

        default:

            break;

    }

// 初始化所有的回调数据结构

    for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFYi++) {

        ExInitializeCallBack (&PspCreateThreadNotifyRoutine[i]);   //线程创建回调函数

    }

    for (i = 0; i < PSP_MAX_CREATE_PROCESS_NOTIFYi++) {

        ExInitializeCallBack (&PspCreateProcessNotifyRoutine[i]);  //进程创建回调函数

    }

    for (i = 0; i < PSP_MAX_LOAD_IMAGE_NOTIFYi++) {

        ExInitializeCallBack (&PspLoadImageNotifyRoutine[i]);     //映像加载回调函数

    }

    PsChangeQuantumTable (FALSEPsRawPrioritySeparation);

    // 某些限额会根据需要自动增加

    if (PspDefaultNonPagedLimit == 0 && PspDefaultPagedLimit == 0) {

        PspDoingGiveBacks = TRUE;

    } else {

        PspDoingGiveBacks = FALSE;

    }

    PspDefaultPagedLimit *= PSP_1MB;

    PspDefaultNonPagedLimit *= PSP_1MB;

    if (PspDefaultPagefileLimit != -1) {

        PspDefaultPagefileLimit *= PSP_1MB;

    }

// 初始化活动进程列表和互斥体

    InitializeListHead (&PsActiveProcessHead);

    PspInitializeProcessListLock();

// 初始化进程安全锁

    PsIdleProcess = PsGetCurrentProcess();

    PspInitializeProcessLock (PsIdleProcess);

    ExInitializeRundownProtection (&PsIdleProcess->RundownProtect);

    InitializeListHead (&PsIdleProcess->ThreadListHead);

    PsIdleProcess->Pcb.KernelTime = 0;

    PsIdleProcess->Pcb.KernelTime = 0;

    // 初始化关机线程指针

    PspShutdownThread = NULL;

    // 初始化对象类型原型纪录的相关字段

    RtlZeroMemory (&ObjectTypeInitializersizeof (ObjectTypeInitializer));

    ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);

    ObjectTypeInitializer.SecurityRequired = TRUE;

    ObjectTypeInitializer.PoolType = NonPagedPool;

    ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |

                                              OBJ_EXCLUSIVE |

                                              OBJ_OPENIF;

    // 创建线程和进程对象.

    RtlInitUnicodeString (&NameStringL"Process");

    ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_PROCESS_PAGED_CHARGE;

    ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_PROCESS_NONPAGED_CHARGE;

    ObjectTypeInitializer.DeleteProcedure = PspProcessDelete;

    ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;

    ObjectTypeInitializer.GenericMapping = PspProcessMapping;

    if (!NT_SUCCESS (ObCreateObjectType (&NameString,

                                         &ObjectTypeInitializer,

                                         (PSECURITY_DESCRIPTORNULL,

                                         &PsProcessType))) {

        return FALSE;

    }

    RtlInitUnicodeString (&NameStringL"Thread");

    ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_THREAD_PAGED_CHARGE;

    ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_THREAD_NONPAGED_CHARGE;

    ObjectTypeInitializer.DeleteProcedure = PspThreadDelete;

    ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;

    ObjectTypeInitializer.GenericMapping = PspThreadMapping;

    

if (!NT_SUCCESS (ObCreateObjectType (&NameString,

                                         &ObjectTypeInitializer,

                                         (PSECURITY_DESCRIPTORNULL,

                                         &PsThreadType))) {

        return FALSE;

    }

//创建作业对象

    RtlInitUnicodeString (&NameStringL"Job");

    ObjectTypeInitializer.DefaultPagedPoolCharge = 0;

    ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof (EJOB);

    ObjectTypeInitializer.DeleteProcedure = PspJobDelete;

    ObjectTypeInitializer.CloseProcedure = PspJobClose;

    ObjectTypeInitializer.ValidAccessMask = JOB_OBJECT_ALL_ACCESS;

    ObjectTypeInitializer.GenericMapping = PspJobMapping;

    ObjectTypeInitializer.InvalidAttributes = 0;

    if (!NT_SUCCESS (ObCreateObjectType (&NameString,

                                         &ObjectTypeInitializer,

                                         (PSECURITY_DESCRIPTORNULL,

                                         &PsJobType))) {

        return FALSE;

    }

// 初始化作业列表头和互斥体

    PspInitializeJobStructures ();

    

    InitializeListHead (&PspWorkingSetChangeHead.Links);

    PspInitializeWorkingSetChangeLock ();

PspCidTable = ExCreateHandleTable (NULL);  // 此处创建PspCidTable.

    if (PspCidTable == NULL) {

        return FALSE;

    }

    // Set PID and TID reuse to strict FIFO. This isn't absolutely needed but it makes tracking audits easier.

    ExSetHandleTableStrictFIFO (PspCidTable);

    ExRemoveHandleTable (PspCidTable);

#if defined(i386)

    // Ldt Initialization

    //

    if ( !NT_SUCCESS (PspLdtInitialize ()) ) {

        return FALSE;

    }

    //

    // Vdm support Initialization

    //

    if (!NT_SUCCESS (PspVdmInitialize ())) {

        return FALSE;

    }

#endif

    // Initialize Reaper Data Structures

    PsReaperListHead.Next = NULL;

    ExInitializeWorkItem (&PsReaperWorkItemPspReaperNULL);

// 获取系统访问令牌的指针

// 该令牌被引导进程所使用,因此可以从中获取指针

    PspBootAccessToken = ExFastRefGetObject (PsIdleProcess->Token);

    InitializeObjectAttributes (&ObjectAttributes,

                                NULL,

                                0,

                                NULL,

                                NULL);

    if (!NT_SUCCESS (PspCreateProcess (&PspInitialSystemProcessHandle,

                                       PROCESS_ALL_ACCESS,

                                       &ObjectAttributes,

                                       NULL,

                                       0,

                                       NULL,

                                       NULL,

                                       NULL,

                                       0))) {

        return FALSE;

    }

    if (!NT_SUCCESS (ObReferenceObjectByHandle (PspInitialSystemProcessHandle,

                                                0L,

                                                PsProcessType,

                                                KernelMode,

                                                &PsInitialSystemProcess,

                                                NULL))) {

        return FALSE;

    }

    strcpy((char *) &PsIdleProcess->ImageFileName[0], "Idle");

    strcpy((char *) &PsInitialSystemProcess->ImageFileName[0], "System");

// 将进程对象命名为"Idle"和"System"

// The system process can allocate resources, and its name may be queried by NtQueryInfomationProcess and various audits.  

// We must explicitly allocate memory for this field of the System EPROCESS, and initialize it appropriately.  

// In this case, appropriate initialization means zeroing the memory.

    PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName =

        ExAllocatePoolWithTag (PagedPool

                               sizeof(OBJECT_NAME_INFORMATION), 

                               'aPeS');

    if (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName != NULL) {

        RtlZeroMemory (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName

                       sizeof (OBJECT_NAME_INFORMATION));

    } else {

        return FALSE;

}

    // 此处还创建了一个系统线程,此系统线程的开始例程为Phase1Initialization函数

// 但是因为阶段0初始化过程中中断是被禁止的,所以该线程并不会被立即执行

    if (!NT_SUCCESS (PsCreateSystemThread (&ThreadHandle,

                                           THREAD_ALL_ACCESS,

                                           &ObjectAttributes,

                                           0L,

                                           NULL,

                                           Phase1Initialization,

                                           (PVOID)LoaderBlock))) {

        return FALSE;

    }

    if (!NT_SUCCESS (ObReferenceObjectByHandle (ThreadHandle,

                                                0L,

                                                PsThreadType,

                                                KernelMode,

                                                &Thread,

                                                NULL))) {

        return FALSE;

    }

    ZwClose (ThreadHandle);

// On checked systems install an image callout routine

#if DBG

    Status = PsSetLoadImageNotifyRoutine (PspImageNotifyTest);

    if (!NT_SUCCESS (Status)) {

        return FALSE;

    }

#endif

    return TRUE;

}

KiInitializeKernel 函数返回以后,KiSystemStartup 启动中断,将当前的中断请求级别(IRQL,Interrupt Request Level)降低到DISPATCH_LEVEL,从而允许线程调度器选择新的线程。因此,阶段0 初始化完成以后,阶段1 初始化例程Phase1Initialization 得以运行。注意,如果仔细观察KiSystemStartup 汇编函数的代码,可以发现它在跳转到空闲循环KiIdleLoop 以前,要经过一个屏障KiBarrierWait。此屏障对于系统的第一个处理器并不起作用,而仅对后续的处理器起作用。后面我们在讨论其他处理器的初始化过程时将会看到此屏障的影响。

现在我们知道,阶段1 初始化是在System 进程的一个系统线程中运行的。Phase1Initialization 函数调用Phase1InitializationDiscard 执行阶段1 初始化,然后调用MmZeroPageThread 函数,从而此线程蜕变成内存管理器的零页面线程(内存管理器中负责在后台将空闲页面清零的辅助线程,参见4.5 节关于物理内存管理的描述)。在介绍阶段1 初始化的逻辑以前,我们先来看看在多处理器或多核系统上的初始化过程。如图2.16所示,当第一个处理器(也称为引导处理器,或0 号处理器,或P0)执行到阶段1 初始化时,在Phase1InitializationDiscard 的一个特定点上,它调用KeStartAllProcessors 函数,以启动其他的处理器。这些处理器从KiSystemStartup 函数开始执行。

KeStartAllProcessors 函数的代码位于base\ntos\ke\i386\allproc.c 文件中,它设置好前文提到的位于KiSystemStartup 函数结束处的屏障KiBarrierWait , 然后依次调用KiInitProcessor 函数来启动每个处理器。KiInitProcessor 函数为每个处理器构造一份状态信息(KPROCESSOR_STATE 结构),然后调用HalStartNextProcessor 函数启动该处理器。处理器状态信息是通过调用KiInitProcessorState 函数来构造的,在该函数的代码中,我们可以看到,新处理器的起始指令地址(即KPROCESSOR_STATE 结构的ContextFrame.Eip成员)为KiSystemStartup 例程。KeStartAllProcessors 函数在启动了其他所有处理器以后,设置好每个处理器的控制块(PRCB)中的相关信息,并同步所有处理器的性能计数器,最后打开屏障KiBarrierWait,允许其他处理器进入空闲线程循环。这意味着,此后其他的处理器可以按照线程调度器的选择来运行比空闲线程优先级更高的线程了。所以,屏障KiBarrierWait 可以看成是引导处理器对于非引导处理器的一个约束,在放任它们参与线程调度以前执行必要的初始处理。

VOID

KeStartAllProcessors (

    VOID

    )

//函数功能:

    在初始化1阶段即将开始时候被0阶段所调用,该函数为每一个处理器实现了与hal联系的x86指定代码

//返回值:

    所有可用的处理器都被作为参数返回到KiSystemStartup

    

{

    KDESCRIPTOR         Descriptor;

    ULONG               NewProcessorNumber;

    SIZE_T              ProcessorDataSize;

    UCHAR               NodeNumber = 0;

    ULONG               IdtOffset;

    ULONG               GdtOffset;

#if defined(KE_MULTINODE)

    USHORT              ProcessorId;

    PKNODE              Node;

    NTSTATUS            Status;

#endif

    //如果RELOCATEPHYSICAL标志被启用,那么就不要启用额外的处理器

    if (KeLoaderBlock->LoadOptions != NULL) {

        if (strstr(KeLoaderBlock->LoadOptions"RELOCATEPHYSICAL") != NULL) {

            return;

        }

    }

    // If the boot processor has PII spec A27 errata (also present in

    // early Pentium Pro chips), then use only one processor to avoid

    // unpredictable eflags corruption.

    //

    // Note this only affects some (but not all) chips @ 333Mhz and below.

    if (!(KeFeatureBits & KF_WORKING_PTE)) {

        return;

    }

   

// Calculate the size of the per processor data.  This includes

    //   PCR (+PRCB)

    //   TSS

    //   Idle Thread Object

    //   NMI TSS

    //   Double Fault TSS

    //   Double Fault Stack

    //   GDT

    //   IDT

// If this is a multinode system, the KNODE structure is allocated as well.  

// It isn't very big so we waste a few bytes for processors that aren't the first in a node.

    // A DPC and Idle stack are also allocated but these are done separately.

    ProcessorDataSize = ROUNDUP16(sizeof(KPCR))                 +

                        ROUNDUP16(sizeof(KTSS))                 +

                        ROUNDUP16(sizeof(ETHREAD))              +

                        ROUNDUP16(FIELD_OFFSET(KTSSIoMaps))   +

                        ROUNDUP16(FIELD_OFFSET(KTSSIoMaps))   +

                        ROUNDUP16(DOUBLE_FAULT_STACK_SIZE);

#if defined(KE_MULTINODE)

    ProcessorDataSize += ROUNDUP16(sizeof(KNODE));

#endif

    // 增加GDT的大小

    GdtOffset = ProcessorDataSize;

    _asm {

        sgdt    Descriptor.Limit

    }

    ProcessorDataSize += Descriptor.Limit + 1;

    // 增加IDT的大小

    IdtOffset = ProcessorDataSize;

    _asm {

        sidt    Descriptor.Limit

    }

    ProcessorDataSize += Descriptor.Limit + 1;

// 设置障碍值防止其他处理器进入空闲线程循环

    KiBarrierWait = 1;

    //不停地向HAL请求下一个处理器,直到反馈无任何处理器之后才停止

    for (NewProcessorNumber = 1;NewProcessorNumber < MAXIMUM_PROCESSORS;NewProcessorNumber++) {

        if (! KiInitProcessor(NewProcessorNumber, &NodeNumberIdtOffsetGdtOffsetProcessorDataSize) ) {

            break;

        }

//依次调用KiInitProcessor函数来启动每个处理器,该函数为每个处理器构造一份状态信息(KPROCESSOR_STATE结构)

        KiProcessorStartControl = KcStartContinue;

#if defined(KE_MULTINODE)

        Node->ProcessorMask |= 1 << NewProcessorNumber;

#endif

// 等待一个处理器在内核中完成初始化.然后再等待其他的

        while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0) {

            KeYieldProcessor();

        }

    }

KiAllProcessorsStarted();  // 启动所有处理器

KeAdjustInterruptTime (0);  // 同步所有处理器的性能计数器

// 到此处开始允许其他处理器进入空闲线程循环

    KiBarrierWait = 0;

//屏障KiBarrierWait可以看成是引导处理器对于非引导处理器的一个约束,在放任它们参与线程调度以前执行必要的初始处理

//此后所有的处理器可以按照线程调度器的选择来运行比空闲线程优先级更高的线程了 

}

PKPRCB

KiInitProcessorState(

    PKPROCESSOR_STATE  pProcessorState,

    PVOID               PerProcessorAllocation,

    ULONG               NewProcessorNumber,

    UCHAR               NodeNumber,

    ULONG               IdtOffset,

    ULONG               GdtOffset,

    PVOID             *ppStack,

    PVOID             *ppDpcStack

    )

/*++

函数功能:

在阶段0初始化时初始化处理器的各种状态信息

返回值:

    新的处理器的Prcb

--*/

{

    KDESCRIPTOR         Descriptor;

    KDESCRIPTOR         TSSDescDFTSSDescNMITSSDescPCRDesc;

    PKGDTENTRY          pGDT;

    PUCHAR              pThreadObject;

    PULONG              pTopOfStack;

    PKTSS               pTSS;

    PUCHAR              Base;

    PKPRCB              NewPrcb;

    ULONG               xCr0xCr3xEFlags;

    Base = (PUCHAR)PerProcessorAllocation;

// 设置处理器的GDT.

    _asm {

        sgdt    Descriptor.Limit

    }

    KiCloneDescriptor (&Descriptor,

                       &pProcessorState->SpecialRegisters.Gdtr,

                       Base + GdtOffset);

    pGDT = (PKGDTENTRYpProcessorState->SpecialRegisters.Gdtr.Base;

// 设置处理器的IDT.

    _asm {

        sidt    Descriptor.Limit

    }

    KiCloneDescriptor (&Descriptor,

                       &pProcessorState->SpecialRegisters.Idtr,

                       Base + IdtOffset);

//设置处理器的TSS和PCR.

    KiCloneSelector (KGDT_R0_PCRpGDT, &PCRDescBase);

    RtlZeroMemory (BaseROUNDUP16(sizeof(KPCR)));

    Base += ROUNDUP16(sizeof(KPCR));

    KiCloneSelector (KGDT_TSSpGDT, &TSSDescBase);

    Base += ROUNDUP16(sizeof(KTSS));

    //空闲线程对象

    pThreadObject = Base;

    RtlZeroMemory(Basesizeof(ETHREAD));

    Base += ROUNDUP16(sizeof(ETHREAD));

// 不可屏蔽中断的任务状态段和双重故障任务状态段以及相关的堆栈

    KiCloneSelector (KGDT_DF_TSSpGDT, &DFTSSDescBase);

    Base += ROUNDUP16(FIELD_OFFSET(KTSSIoMaps));

    KiCloneSelector (KGDT_NMI_TSSpGDT, &NMITSSDescBase);

    Base += ROUNDUP16(FIELD_OFFSET(KTSSIoMaps));

    Base += DOUBLE_FAULT_STACK_SIZE;

    pTSS = (PKTSS)DFTSSDesc.Base;

    pTSS->Esp0 = (ULONG)Base;

    pTSS->Esp  = (ULONG)Base;

    pTSS = (PKTSS)NMITSSDesc.Base;

    pTSS->Esp0 = (ULONG)Base;

    pTSS->Esp  = (ULONG)Base;

// 设置其他特殊的寄存器状态

    _asm {

        mov     eaxcr0

        and     eaxNOT (CR0_AM or CR0_WP)

        mov     xCr0eax

        mov     eaxcr3

        mov     xCr3eax

        pushfd

        pop     eax

        mov     xEFlagseax

        and     xEFlagsNOT EFLAGS_INTERRUPT_MASK

    }

    pProcessorState->SpecialRegisters.Cr0 = xCr0;

    pProcessorState->SpecialRegisters.Cr3 = xCr3;

    pProcessorState->ContextFrame.EFlags = xEFlags;

    pProcessorState->SpecialRegisters.Tr  = KGDT_TSS;

    pGDT[KGDT_TSS>>3].HighWord.Bytes.Flags1 = 0x89;

    // Encode the processor number into the segment limit of the TEB 6 bits in total. 4 in the high and 2 in the low limit.

    pGDT[KGDT_R3_TEB>>3].LimitLow = (USHORT)((NewProcessorNumber&0x3)<<(16-2));

    pGDT[KGDT_R3_TEB>>3].HighWord.Bits.LimitHi = (NewProcessorNumber>>2);

#if defined(_X86PAE_)

    pProcessorState->SpecialRegisters.Cr4 = CR4_PAE;

#endif

    // Allocate a DPC stack, idle thread stack and ThreadObject for the new processor.

    *ppStack = MmCreateKernelStack (FALSENodeNumber);

    *ppDpcStack = MmCreateKernelStack (FALSENodeNumber);

    // Setup context - push variables onto new stack.

    pTopOfStack = (PULONG) *ppStack;

    pTopOfStack[-1] = (ULONGKeLoaderBlock;

    pProcessorState->ContextFrame.Esp = (ULONG) (pTopOfStack-2);

    pProcessorState->ContextFrame.Eip = (ULONGKiSystemStartup;

    pProcessorState->ContextFrame.SegCs = KGDT_R0_CODE;

    pProcessorState->ContextFrame.SegDs = KGDT_R3_DATA;

    pProcessorState->ContextFrame.SegEs = KGDT_R3_DATA;

    pProcessorState->ContextFrame.SegFs = KGDT_R0_PCR;

    pProcessorState->ContextFrame.SegSs = KGDT_R0_DATA;

// 初始化新的处理器PCR和PRCB

    KiInitializePcr (

        (ULONG)       NewProcessorNumber,

        (PKPCR)       PCRDesc.Base,

        (PKIDTENTRY)  pProcessorState->SpecialRegisters.Idtr.Base,

        (PKGDTENTRY)  pProcessorState->SpecialRegisters.Gdtr.Base,

        (PKTSS)       TSSDesc.Base,

        (PKTHREAD)    pThreadObject,

        (PVOID)       *ppDpcStack

    );

    NewPrcb = ((PKPCR)(PCRDesc.Base))->Prcb;

// Assume new processor will be the first processor in its SMT set.   

//(Right choice for non SMT processors, adjusted later if not correct).

    NewPrcb->MultiThreadSetMaster = NewPrcb;

#if defined(KE_MULTINODE)

    // If this is the first processor on this node, use the

    // space allocated for KNODE as the KNODE.

    if (KeNodeBlock[NodeNumber] == &KiNodeInit[NodeNumber]) {

        Node = (PKNODE)Base;

        *Node = KiNodeInit[NodeNumber];

        KeNodeBlock[NodeNumber] = Node;

    }

    Base += ROUNDUP16(sizeof(KNODE));

    NewPrcb->ParentNode = Node;

#else

    NewPrcb->ParentNode = KeNodeBlock[0];

#endif

    ASSERT(((PUCHAR)PerProcessorAllocation + GdtOffset) == Base);

    //  Adjust LoaderBlock so it has the next processors state

    KeLoaderBlock->KernelStack = (ULONGpTopOfStack;

    KeLoaderBlock->Thread = (ULONGpThreadObject;

    KeLoaderBlock->Prcb = (ULONGNewPrcb;

    // Get CPUID(1) info from the starting processor.

    KiProcessorStartData[0] = 1;

    KiProcessorStartControl = KcStartGetId;

    return NewPrcb;

}

非引导处理器的初始化过程虽然也执行KiSystemStartup 函数,但其执行逻辑相对要简单很多。同样地,KiSystemStartup 也调用HalInitializeProcessor 和KiInitializeKernel 函数来执行HAL和内核部分的初始化,并且,KiInitializeKernel函数也调用ExpInitializeExecutive,但是,ExpInitializeExecutive 函数仅仅简单地调用HalInitSystem 而已。在这些函数中,Number 参数代表了当前处理器的序号,如果为0,则表明这是引导处理器,否则为非引导处理器。这样的逻辑不难理解:有些初始化过程,特别是内核执行体组件(比如内存管理器和对象管理器)的初始化例程,它们的处理逻辑针对整个系统,而并非某个处理器,所以只需在引导处理器上运行一次即可;而另外有些初始化过程,则是针对单个处理器的初始化(包括设置处理器的中断描述符表、PRCB 的状态等),因而需要针对每个处理器都执行一次。在阅读这些函数的代码时,我们可以清楚地看到这一基本差别.

前面我们提到了阶段1 初始化是在System 进程的一个系统线程中进行的,它在一个恰当的点上调用KeStartAllProcessors 函数以启动其他的处理器。阶段1 初始化是内核的完全初始化,它占据了系统初始化过程中相当一部分时间。在Windows 2000 系统的引导过程中,可以在屏幕上看到一个进度条,它指示了阶段1 初始化的进度;而在WindowsXP/Server 2003 及以后的Windows 系统中,则是一个闪烁的Windows 标志图案出现在屏幕上,而并非进度条。如前所述,阶段1 初始化的主函数为Phase1InitializationDiscard,其代码位于base\ntos\init\initos.c 文件的933~1 865 行。从该函数的代码可以看出,阶段1的初始化进度仍然有一个百分比估计。该函数按以下步骤完成其主要功能:

(1) 设置全局变量InitializationPhase 为1,标志着当前这次系统引导过程进入内核的阶段1 初始化。

(2) 调用HalInitSystem 函数,执行HAL 的阶段1 初始化。

(3) 初始化图形引导驱动程序,显示Windows 启动屏幕,设置引导进度条范围(0,100)。虽然在Windows Server 2003 中已不再显示进度条,但进度指示逻辑仍然保留。

(4) 调用PoInitSystem,完成电源管理器的阶段0 初始化。

(5) 调用HalQueryRealTimeClock,初始化系统时间,这一步必须在HAL 的阶段1 初始化以后才能进行。再调用KeSetSystemTime 以设置内核的系统时间。

(6) 调用KeStartAllProcessors,启动并初始化其他的处理器。启动了这些辅助处理器以后,重新调用HalQueryRealTimeClock 以初始化系统时间,并调用KeSetSystemTime 设置内核的系统时间。

(7) 接下来,调用ObInitSystem,完成对象管理器的阶段1 初始化;调用ExInitSystem,完成执行体组件的阶段1 初始化;调用KeInitSystem,完成微内核部分的初始化;调用KdInitSystem,完成内核调试器的阶段1 初始化;调用SeInitSystem,完成安全子系统的阶段1 初始化。

(8) 调用InbvUpdateProgressBar 函数,更新进度条至10%。

(9) 创建符号链接“\SystemRoot”。

(10) 调用MmInitSystem,完成内存管理器的阶段1 初始化。

(11) 将国家语言支持(NLS)表映射到系统空间中,然后重置翻译表。

(12) 调用CcInitializeCacheManager,初始化缓存管理器。

(13) 调用CmInitSystem1,初始化配置管理器,建立起基本的注册表对象操作,把引导时获得的数据转变成注册表格式。现在,HKLM\SYSTEM 和HKLM\HARDWARE 已可以使用,这一步必须在I/O 子系统初始化之前完成。

(14) 调用CcPfInitializePrefetcher,初始化内核中的预取器(prefetcher)。

(15) 进度条前进到15%。

(16) 调用FsRtlInitSystem,初始化文件系统支持库(FsRtl)。

(17) 调用KdDebuggerInitialize1,即kdcom.dll 中的调试器初始化。

(18) 调用PpInitSystem,初始化即插即用管理器。

(19) 进度条前进到20%。

(20) 调用LpcInitSystem,初始化LPC 子系统。

(21) 现在系统时间已经正常运行,调用ExInitSystemPhase2,再次初始化执行体组件。

(22) 进度条更新范围设置为25~75%。

(23) 调用IoInitSystem,初始化I/O 系统。这是系统引导过程中较为复杂的一部分,将占据进度条50%的范围。IoInitSystem 函数所做的工作包括:I/O 子系统中的状态变量初始化、驱动程序对象类型和设备对象类型的创建、加载“引导-启动”类型的驱动程序、加载“系统-启动”类型的驱动程序,以及WMI 初始化等。详细的执行过程,请参IoInitSystem 函数的代码,位于base\ntos\io\iomgr\ioinit.c 文件中。

(24) 进度条更新范围恢复到0~100%。

(25) 再次调用MmInitSystem,将当前已加载内核模块中的PAGE 段标记为“可换页”。

(26) 进度条前进到80%。

(27) 调用PoInitSystem,完成电源管理器的阶段1 初始化。

(28) 调用PsInitSystem,完成进程管理器的阶段1 初始化。

(29) 进度条前进到85%。

(30) 调用SeRmInitPhase1,执行安全引用监视器(SRM)的阶段1 初始化,包括建安全引用监视器的命令服务线程。该线程创建一个名为“引用监视器命令端口”的LP端口,以接收来自lsass.exe 进程的命令。参见SeRmInitPhase1 函数的代码。

(31) 进度条前进到90%。

(32) 创建会话管理器子系统进程(smss.exe)。首先准备各种参数信息(RTL_USERPROCESS_PARAMETERS 结构),包括环境参数字符串,然后调用RtlCreateUserProces函数以创建smss 进程。

(33) 进度条前进到100%。

(34) 最后,调用ZwWaitForSingleObject 函数,在smss 进程的句柄上等待,超时值设置为5 s 。如果等待成功, 则意味着在5 s 内smss 进程退出了, 于是调用“KeBugCheck(SESSION5_INITIALIZATION_FAILED)”,系统崩溃;若等待超时,则认为会话管理器已经正常运行,于是阶段1 初始化完成,当前线程蜕变成零页面线程。

VOID

Phase1Initialization (

    IN PVOID Context

    )

{

    Phase1InitializationDiscard (Context);

    MmZeroPageThread();   //内存管理器在后台将空闲页面清零的辅助线程

    return;

}

VOID

Phase1InitializationDiscard (

    IN PVOID Context

    )

{

    PLOADER_PARAMETER_BLOCK LoaderBlock;

    PETHREAD Thread;

    PKPRCB Prcb;

    KPRIORITY Priority;

    NTSTATUS Status;

    UNICODE_STRING SessionManager;

    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;

    PVOID Address;

    SIZE_T Size;

    LARGE_INTEGER UniversalTime;

    LARGE_INTEGER CmosTime;

    LARGE_INTEGER OldTime;

    TIME_FIELDS TimeFields;

    UNICODE_STRING EnvStringNullStringUnicodeSystemDriveString;

    PWSTR SrcDst;

    BOOLEAN ResetActiveTimeBias;

    HANDLE NlsSection;

    LARGE_INTEGER SectionSize;

    LARGE_INTEGER SectionOffset;

    PVOID SectionBase;

    PVOID ViewBase;

    ULONG CacheViewSize;

    SIZE_T CapturedViewSize;

    ULONG SavedViewSize;

    LONG BootTimeZoneBias;

    PKLDR_DATA_TABLE_ENTRY DataTableEntry;

#ifndef NT_UP

    PMESSAGE_RESOURCE_ENTRY MessageEntry1;

#endif

    PCHAR MPKernelString;

    PCHAR Options;

    PCHAR YearOverrideOption;

    LONG  CurrentYear = 0;

    BOOLEAN NOGUIBOOT;

    BOOLEAN SOS;

    PVOID Environment;

    PRTL_USER_PROCESS_INFORMATION ProcessInformation;

    ProcessInformation = ExAllocatePoolWithTag(NonPagedPool,

                                        sizeof(*ProcessInformation),

                                        'tinI');   //分配内存给进程信息

    if (ProcessInformation == NULL) {

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,

                     STATUS_NO_MEMORY,

                     8,

                     0,

                     0); //如果进程信息初始化失败,就抛出内存异常,系统BSOD

    }

// The following is a dummy reference section to inline functions that need to have a reference forced. 

// This code is never executed, but the compiler can never assume it isn't.

    // N.B. The control variable is always false.

    if (InitForceInline == TRUE) {

        KGUARDED_MUTEX Mutex;

        extern ULONG volatile *VpPoolHitTag;

        KeTryToAcquireGuardedMutex(&Mutex);

        KeEnterGuardedRegion();

        KeLeaveGuardedRegion();

        KeAreApcsDisabled();

        KeRaiseIrqlToDpcLevel();

        VpPoolHitTag = &PoolHitTag;

    }  //此处的代码永远不会被执行

    ResetActiveTimeBias = FALSE;

    InitializationPhase = 1;     //设置该全局变量为,标志着当前系统引导过程进入内核的阶段初始化

    Thread = PsGetCurrentThread();

    Priority = KeSetPriorityThread( &Thread->Tcb,MAXIMUM_PRIORITY - 1 ); //此处提高当前线程到最高优先级,避免在初始化过程中被预先清空

    LoaderBlock = (PLOADER_PARAMETER_BLOCK)Context;

    if (HalInitSystem(InitializationPhaseLoaderBlock) == FALSE) { //执行HAL的阶段初始化

        KeBugCheck(HAL1_INITIALIZATION_FAILED);  //失败即BSOD

    }

   

    // 允许图形引导驱动根据OsLoadOptions的值给出不同的显示

    Options = LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL;

    if (Options) {

        NOGUIBOOT = (BOOLEAN)(strstr(Options"NOGUIBOOT") != NULL);

    } else {

        NOGUIBOOT = FALSE;

    }

    InbvEnableBootDriver((BOOLEAN)!NOGUIBOOT);

    // 执行到此处时候已经有足够的功能启动系统的图形引导驱动程序

    InbvDriverInitialize(LoaderBlock, 18);    //初始化图形引导驱动程序

    if (NOGUIBOOT) {

        InbvNotifyDisplayOwnershipLost(NULL); //如果用户指定了进行的是无界面的引导那么无需图形引导驱动,直接释放显示控制权

    }

    if (Options) {

        SOS = (BOOLEAN)(strstr(Options"SOS") != NULL);

    } else {

        SOS = FALSE;

    }

    if (NOGUIBOOT) {

        InbvEnableDisplayString(FALSE);

    } else {

        InbvEnableDisplayString(SOS);

        DisplayBootBitmap(SOS);    //显示字符串和位图"SOS"

    }

   

    // 检测是否启动进入WinPE

    if (Options) {

        if (strstr(Options"MININT") != NULL) {

            InitIsWinPEMode = TRUE;

            if (strstr(Options"INRAM") != NULL) {

                InitWinPEModeType |= INIT_WINPEMODE_INRAM;

            } else {

                InitWinPEModeType |= INIT_WINPEMODE_REGULAR;

            }

        }

    }

    CmpInitSystemVersion(2, NULL);

    // 初始化电源子系统

    if (!PoInitSystem(0)) {

        KeBugCheck(INTERNAL_POWER_ERROR);  //完成电源管理器的阶段初始化

    }

    

    // OSLOADOPTIONS可以设置/YEAR=2000.说明这个选项允许用户在拥有中断时钟的硬件层设置一个特定的年份

    if (Options) {

        YearOverrideOption = strstr(Options"YEAR");

        if (YearOverrideOption != NULL) {

            YearOverrideOption = strstr(YearOverrideOption,"=");

        }

        if (YearOverrideOption != NULL) {

            CurrentYear = atol(YearOverrideOption + 1);

        }

    }

    if (ExCmosClockIsSane

        && HalQueryRealTimeClock(&TimeFields)) {  //初始化系统时间,必须在HAL的阶段初始化以后才能进行

        

        if (YearOverrideOption) {

            TimeFields.Year = (SHORT)CurrentYear;  //如果函数初始化成功的话,就赋值当前年份

        }

        RtlTimeFieldsToTime(&TimeFields, &CmosTime);

        UniversalTime = CmosTime;

        if ( !ExpRealTimeIsUniversal ) {

// 此处会将设置的本地时间自动转换成通用时间,如果已经设置过时间那么ExpLastTimeZoneBias会包含一个有效的时区数值

            if ( ExpLastTimeZoneBias == -1 ) {

                ResetActiveTimeBias = TRUE;

                ExpLastTimeZoneBias = ExpAltTimeZoneBias;

                }

 ExpTimeZoneBias.QuadPart = Int32x32To64(

                                ExpLastTimeZoneBias*60,   //秒数的偏差

                                10000000

                                );

            SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.HighPart;

            SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.LowPart;

            SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.HighPart;

            UniversalTime.QuadPart = CmosTime.QuadPart + ExpTimeZoneBias.QuadPart;

        }

        KeSetSystemTime(&UniversalTime, &OldTimeFALSENULL);  //设置内核的系统时间

        PoNotifySystemTimeSet();  //通知其他组件系统时间已经被设置

        KeBootTime = UniversalTime;

        KeBootTimeBias = 0;

    }

    MPKernelString = "";

    CmpInitSystemVersion(8, Options);

#ifndef NT_UP

    CmpInitSystemVersion(3, NULL);

    KeStartAllProcessors();  //启动并初始化其他处理器

    // 在其他辅助处理器之后,重新调用HalQueryRealTimeClock函数初始化系统时间

    if (ExCmosClockIsSane

        && HalQueryRealTimeClock(&TimeFields)) {

        if (YearOverrideOption) {

            TimeFields.Year = (SHORT)CurrentYear;

        }

        RtlTimeFieldsToTime(&TimeFields, &CmosTime);

        if ( !ExpRealTimeIsUniversal ) {

            UniversalTime.QuadPart = CmosTime.QuadPart + ExpTimeZoneBias.QuadPart;

        }

        KeSetSystemTime(&UniversalTime, &OldTimeTRUENULL);  //再次设置内核的系统时间

    }

    

    // 设置系统进程的优先级以及所有的线程

    KeSetAffinityProcess(KeGetCurrentThread()->ApcState.Process,

                         KeActiveProcessors);

    DataTableEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,

                                   KLDR_DATA_TABLE_ENTRY,

                                   InLoadOrderLinks);

    Status = RtlFindMessage (DataTableEntry->DllBase, 11, 0,

                        WINDOWS_NT_MP_STRING, &MessageEntry1);

    if (NT_SUCCESSStatus )) {

        MPKernelString = MessageEntry1->Text;

    }

    else {

        MPKernelString = "MultiProcessor Kernel\r\n";  //判断为多处理器内核

    }

    CmpInitSystemVersion(9, MPKernelString);

#endif

    if (!HalAllProcessorsStarted()) {

        KeBugCheck(HAL1_INITIALIZATION_FAILED); //此处代码意味着所有的处理器都已经开始运行,而且所有之前的初始化工作都已准备就绪

    }

    CmpInitSystemVersion(4, DataTableEntry);

    if (!ObInitSystem()) {

        KeBugCheck(OBJECT1_INITIALIZATION_FAILED);  //完成对象管理器的阶段初始化

    }

    if (!ExInitSystem()) {

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,1,0);  //完成执行体组件的阶段初始化

    }

    if (!KeInitSystem()) {

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,2,0);  //完成微内核部分的初始化

    }

    if (!KdInitSystem(InitializationPhaseNULL)) {

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,STATUS_UNSUCCESSFUL,0,3,0);  //完成内核调试器的阶段的初始化

    }

    if (!SeInitSystem()) {

        KeBugCheck(SECURITY1_INITIALIZATION_FAILED);  //完成安全子系统的阶段的初始化,目录和执行体对象已经可用

// 这个过程必须在设备驱动初始化之前完成

    }

    InbvUpdateProgressBar(10);  //更新进度条到%

    // 创建符号链接\SystemRoot

    Status = CreateSystemRootLink(LoaderBlock);

    if ( !NT_SUCCESS(Status) ) {

        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED,Status,0,0,0);

    }

    if (MmInitSystem(1, LoaderBlock) == FALSE) {

        KeBugCheck(MEMORY1_INITIALIZATION_FAILED);  //完成内存管理器的阶段的初始化

    }

    // 将国家语言支持表,即NLS表映射到系统空间中,然后重置翻译表

    SectionSize.HighPart = 0;

    SectionSize.LowPart = InitNlsTableSize;

    

Status = ZwCreateSection(

                &NlsSection,

                SECTION_ALL_ACCESS,

                NULL,

                &SectionSize,

                PAGE_READWRITE,

                SEC_COMMIT,

                NULL

                );

    if (!NT_SUCCESS(Status)) {

        KdPrint(("INIT: Nls Section Creation Failed %x\n",Status));

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,1,0,0);

    }

    Status = ObReferenceObjectByHandle(

                NlsSection,

                SECTION_ALL_ACCESS,

                MmSectionObjectType,

                KernelMode,

                &InitNlsSectionPointer,

                NULL

                );

    ZwClose(NlsSection);

    if ( !NT_SUCCESS(Status) ) {

        KdPrint(("INIT: Nls Section Reference Failed %x\n",Status));

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,2,0,0); //判断对Nls段的引用是否正常

    }

    SectionBase = NULL;

    CacheViewSize = SectionSize.LowPart;

    SavedViewSize = CacheViewSize;

    SectionSize.LowPart = 0;

    Status = MmMapViewInSystemCache (InitNlsSectionPointer,

                                     &SectionBase,

                                     &SectionSize,

                                     &CacheViewSize);  //映射得到的Nls指针到系统缓存

    if (!NT_SUCCESS(Status)) {

        KdPrint(("INIT: Map In System Cache Failed %x\n",Status));

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,3,0,0);

    }

    // 将NLS的数据复制到动态缓冲区,这样可以释放通过loader分配的缓冲区

    RtlCopyMemory (SectionBaseInitNlsTableBaseInitNlsTableSize);

    //解除视图的映射以移除内存中的所有页面

    MmUnmapViewInSystemCache (SectionBaseInitNlsSectionPointerFALSE);

    SectionBase = NULL;

    

    //重新映射如系统缓存,但是需要注意的是此时页面不在有效

    Status = MmMapViewInSystemCache(

                InitNlsSectionPointer,

                &SectionBase,

                &SectionSize,

                &SavedViewSize

                );

    if ( !NT_SUCCESS(Status) ) {

        KdPrint(("INIT: Map In System Cache Failed %x\n",Status));

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,4,0,0);

    }

    ExFreePool(InitNlsTableBase); //分配完要释放空间

    InitNlsTableBase = SectionBase;

    RtlInitNlsTables(

        (PVOID)((PUCHAR)InitNlsTableBase+InitAnsiCodePageDataOffset),

        (PVOID)((PUCHAR)InitNlsTableBase+InitOemCodePageDataOffset),

        (PVOID)((PUCHAR)InitNlsTableBase+InitUnicodeCaseTableDataOffset),

        &InitTableInfo

        );

    RtlResetRtlTranslations(&InitTableInfo);

    ViewBase = NULL;

    SectionOffset.LowPart = 0;

    SectionOffset.HighPart = 0;

CapturedViewSize = 0;

    //映射系统dll到用户地址空间

    Status = MmMapViewOfSection (InitNlsSectionPointer,

                                 PsGetCurrentProcess(),

                                 &ViewBase,

                                 0L,

                                 0L,

                                 &SectionOffset,

                                 &CapturedViewSize,

                                 ViewShare,

                                 0L,

                                 PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {

        KdPrint(("INIT: Map In User Portion Failed %x\n",Status));

        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED,Status,5,0,0);

    }

    RtlCopyMemory (ViewBaseInitNlsTableBaseInitNlsTableSize);

    InitNlsTableBase = ViewBase;

    if (!CcInitializeCacheManager()) {

        KeBugCheck(CACHE_INITIALIZATION_FAILED);  //初始化缓存管理器

    }

    if (!CmInitSystem1(LoaderBlock)) {

        KeBugCheck(CONFIG_INITIALIZATION_FAILED);

    // 初始化配置管理器,建立基本的注册表对象操作,把引导时获得的数据转换成注册表格式

    // 到此为止,HKLM\SYSTEM和HKLM\HARDWARE已经可以使用,这一个步骤必须在I/O子系统初始化之前完成

    }

    CcPfInitializePrefetcher(); //初始化内核中的预取器(prefetcher)

    InbvUpdateProgressBar(15);  //进度条前进到%

    // 计算时区偏差以及下一个转换日期

    BootTimeZoneBias = ExpLastTimeZoneBias;

    ExpRefreshTimeZoneInformation(&CmosTime);

    if (ResetActiveTimeBias) {

        ExLocalTimeToSystemTime(&CmosTime,&UniversalTime);

        KeBootTime = UniversalTime;

        KeBootTimeBias = 0;

        KeSetSystemTime(&UniversalTime, &OldTimeFALSENULL);

    }

    else {

        // 检查时区转换是否在启动之前执行

        if (BootTimeZoneBias != ExpLastTimeZoneBias) {

            ZwSetSystemTime(NULL,NULL);

        }

    }

    if (!FsRtlInitSystem()) {

        KeBugCheck(FILE_INITIALIZATION_FAILED); //初始化文件系统支持库(FsRtl)

    }

   

//range list跟PNP管理器有关主要是用于识别、分配硬件资源

//PNP管理器在初始化之前必须初始化range list package

    RtlInitializeRangeListPackage();

    HalReportResourceUsage();

    KdDebuggerInitialize1(LoaderBlock); //kdcom.dll中的调试器初始化

    if (!PpInitSystem()) {

        KeBugCheck(PP1_INITIALIZATION_FAILED); //初始化即插即用管理器,必须在I/O系统初始化之前完成

    }

    InbvUpdateProgressBar(20);  //进度条更新到%

    if (!LpcInitSystem()) {

        KeBugCheck(LPC_INITIALIZATION_FAILED); 

    //初始化LPC子系统,也必须在I/O初始化之前完成

        //因为某些驱动会创建系统线程,而系统线程可能会终止从而引发LPC被调用

    }

    ExInitSystemPhase2();//现在系统时间已经正常运行,再次初始化执行体组件

  

    // Allow time slip notification changes.

    KdpTimeSlipPending = 0;

    InbvSetProgressBarSubset(25, 75);  //进度条更新范围设置为-75%

    if (!IoInitSystem(LoaderBlock)) {

        KeBugCheck(IO1_INITIALIZATION_FAILED);  //初始化I/O系统,这部分非常复杂,将占据进度条%的范围

    }

    InbvSetProgressBarSubset(0, 100);  //进度条更新范围恢复到-100%

    CmpInitSystemVersion(6, NULL);

    MmInitSystem(2, LoaderBlock);  //将当前已加载内核模块中的PAGE段标记为可换页

    InbvUpdateProgressBar(80);     //进度条前进到%

    if (!PoInitSystem(1)) {

        KeBugCheck(INTERNAL_POWER_ERROR);  //完成电源管理器的阶段的初始化

    }

    

    if (PsInitSystem(1, LoaderBlock) == FALSE) {

        KeBugCheck(PROCESS1_INITIALIZATION_FAILED);  //完成进程管理器的阶段的初始化

    }

//运行到此处,\SystemRoot已经被初始化定义,可以定位ntdll.dll和smss.exe了

    InbvUpdateProgressBar(85);  //进度条前进到%

    // Force KeBugCheck to look at PsLoadedModuleList now that it is setup.

    if (LoaderBlock == KeLoaderBlock) {

        KeLoaderBlock = NULL;

    }

    // 初始化loaderblock

    MmFreeLoaderBlock (LoaderBlock);

    LoaderBlock = NULL;

    Context = NULL;

    if (!SeRmInitPhase1()) {

        KeBugCheck(REFMON_INITIALIZATION_FAILED);

//执行安全引用监视器(SRM)的阶段的初始化,包括创建安全引用监视器的命令服务线程

//该线程创建一个名为引用监视器命令端口的LPC端口,以接收来自lsass.exe进程的命令

    }

    InbvUpdateProgressBar(90); //进度条前进到%

    // 创建会话管理器子系统进程smss.exe

    Size = sizeof( *ProcessParameters ) +

           ((DOS_MAX_PATH_LENGTH * 6) * sizeofWCHAR ));  //多分配一些命令行参数的空间

    ProcessParameters = NULL;

    Status = ZwAllocateVirtualMemoryNtCurrentProcess(),

                                      (PVOID *)&ProcessParameters,

                                      0,

                                      &Size,

                                      MEM_COMMIT,

                                      PAGE_READWRITE

                                    );

    if (!NT_SUCCESSStatus )) {

        KeBugCheckEx(SESSION1_INITIALIZATION_FAILED,Status,0,0,0);

    }

//初始化PRTL_USER_PROCESS_PARAMETERS结构中的各种参数信息,包括环境参数字符串

    ProcessParameters->Length = (ULONG)Size;

    ProcessParameters->MaximumLength = (ULONG)Size;

    // Reserve the low 1 MB of address space in the session manager.

    // Setup gets started using a replacement for the session manager and that process needs to be able to use the vga driver on x86,

// which uses int10 and thus requires the low 1 meg to be reserved in the process. 

// The cost is so low that we just do this all the time, even when setup isn't running.

    ProcessParameters->Flags = RTL_USER_PROC_PARAMS_NORMALIZED | RTL_USER_PROC_RESERVE_1MB;

    Size = PAGE_SIZE;

    Environment = NULL;

    Status = ZwAllocateVirtualMemoryNtCurrentProcess(),

                                      &Environment,

                                      0,

                                      &Size,

                                      MEM_COMMIT,

                                      PAGE_READWRITE

                                    );  //分配虚拟内存

    if (!NT_SUCCESSStatus )) {

        KeBugCheckEx(SESSION2_INITIALIZATION_FAILED,Status,0,0,0);

    }

    ProcessParameters->Environment = Environment;

    Dst = (PWSTR)(ProcessParameters + 1);

    ProcessParameters->CurrentDirectory.DosPath.Buffer = Dst;

    ProcessParameters->CurrentDirectory.DosPath.MaximumLength = DOS_MAX_PATH_LENGTH * sizeofWCHAR );

//DOS_MAX_PATH_LENGTH = 255+5= 260

    RtlCopyUnicodeString( &ProcessParameters->CurrentDirectory.DosPath,

                          &NtSystemRoot

                        );

    Dst = (PWSTR)((PCHAR)ProcessParameters->CurrentDirectory.DosPath.Buffer +

                  ProcessParameters->CurrentDirectory.DosPath.MaximumLength

                 );

    ProcessParameters->DllPath.Buffer = Dst;

    ProcessParameters->DllPath.MaximumLength = DOS_MAX_PATH_LENGTH * sizeofWCHAR );

    RtlCopyUnicodeString( &ProcessParameters->DllPath,

                          &ProcessParameters->CurrentDirectory.DosPath

                        );

    RtlAppendUnicodeToString( &ProcessParameters->DllPathL"\\System32" );

    Dst = (PWSTR)((PCHAR)ProcessParameters->DllPath.Buffer +

                  ProcessParameters->DllPath.MaximumLength

                 );

    ProcessParameters->ImagePathName.Buffer = Dst;

    ProcessParameters->ImagePathName.MaximumLength = DOS_MAX_PATH_LENGTH * sizeofWCHAR );

    if (NtInitialUserProcessBufferType != REG_SZ ||

        (NtInitialUserProcessBufferLength != (ULONG)-1 &&

         (NtInitialUserProcessBufferLength < sizeof(WCHAR) ||

          NtInitialUserProcessBufferLength >

          sizeof(NtInitialUserProcessBuffer) - sizeof(WCHAR)))) {

        KeBugCheckEx(SESSION2_INITIALIZATION_FAILED,

                     STATUS_INVALID_PARAMETER,

                     NtInitialUserProcessBufferType,

                     NtInitialUserProcessBufferLength,

                     sizeof(NtInitialUserProcessBuffer));

    }

    // Executable names with spaces don't need to be supported so just find the first space and assume it terminates the process image name.

    Src = NtInitialUserProcessBuffer;

    while (*Src && *Src != L' ') {

        Src++;

    }

    ProcessParameters->ImagePathName.Length =

        (USHORT)((PUCHAR)Src - (PUCHAR)NtInitialUserProcessBuffer);

    RtlCopyMemory(ProcessParameters->ImagePathName.Buffer,

                  NtInitialUserProcessBuffer,

                  ProcessParameters->ImagePathName.Length);

    ProcessParameters->ImagePathName.Buffer[ProcessParameters->ImagePathName.Length / sizeof(WCHAR)] = UNICODE_NULL;

    Dst = (PWSTR)((PCHAR)ProcessParameters->ImagePathName.Buffer +

                  ProcessParameters->ImagePathName.MaximumLength

                 );

    ProcessParameters->CommandLine.Buffer = Dst;

    ProcessParameters->CommandLine.MaximumLength = DOS_MAX_PATH_LENGTH * sizeofWCHAR );

    RtlAppendUnicodeToString(&ProcessParameters->CommandLine,

                             NtInitialUserProcessBuffer);

    CmpInitSystemVersion(7, NULL);

    NullString.Buffer = L"";

    NullString.Length = sizeof(WCHAR);

    NullString.MaximumLength = sizeof(WCHAR);

    EnvString.Buffer = ProcessParameters->Environment;

    EnvString.Length = 0;

    EnvString.MaximumLength = (USHORT)Size;

    RtlAppendUnicodeToString( &EnvStringL"Path=" );

    RtlAppendUnicodeStringToString( &EnvString, &ProcessParameters->DllPath );

    RtlAppendUnicodeStringToString( &EnvString, &NullString );

    UnicodeSystemDriveString = NtSystemRoot;

    UnicodeSystemDriveString.Length = 2 * sizeofWCHAR );

    RtlAppendUnicodeToString( &EnvStringL"SystemDrive=" );

    RtlAppendUnicodeStringToString( &EnvString, &UnicodeSystemDriveString );

    RtlAppendUnicodeStringToString( &EnvString, &NullString );

    RtlAppendUnicodeToString( &EnvStringL"SystemRoot=" );

    RtlAppendUnicodeStringToString( &EnvString, &NtSystemRoot );

    RtlAppendUnicodeStringToString( &EnvString, &NullString );

    SessionManager = ProcessParameters->ImagePathName;

Status = RtlCreateUserProcess(

                &SessionManager,

                OBJ_CASE_INSENSITIVE,

                RtlDeNormalizeProcessParamsProcessParameters ),

                NULL,

                NULL,

                NULL,

                FALSE,

                NULL,

                NULL,

                ProcessInformation); //此处完成smss进程的创建

    if (InbvBootDriverInstalled)

    {

        FinalizeBootLogo();     //一切就绪后初始化启动logo

    }

    if (!NT_SUCCESS(Status)) {

        KeBugCheckEx(SESSION3_INITIALIZATION_FAILED,Status,0,0,0);

    }

    Status = ZwResumeThread(ProcessInformation->ThreadNULL);

    if ( !NT_SUCCESS(Status) ) {

        KeBugCheckEx(SESSION4_INITIALIZATION_FAILED,Status,0,0,0);

    }

    InbvUpdateProgressBar(100); //进度条前进到100%

// 打开调试输出

    InbvEnableDisplayString(TRUE);

    OldTime.QuadPart = Int32x32To64(5, -(10 * 1000 * 1000));

    Status = ZwWaitForSingleObject(ProcessInformation->Process,FALSE,&OldTime); //在smss进程句柄上执行等待指令,超时值设置为s

    if (Status == STATUS_SUCCESS) {

        KeBugCheck(SESSION5_INITIALIZATION_FAILED);

    }

//若等待成功,则意味着s内smss进程退出了,于是系统BSOD;若超时,说明会话管理器已经正常运行,阶段初始化完成

    ZwCloseProcessInformation->Thread );

    ZwCloseProcessInformation->Process );   //关闭线程和进程句柄

//释放传递给会话管理器参数的那部分内存

    Size = 0;

    Address = Environment;

    ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&Address,&Size,MEM_RELEASE);

    Size = 0;

    Address = ProcessParameters;

    ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID *)&Address,&Size,MEM_RELEASE);

InitializationPhase += 1;

#if defined(_X86_)

    KiInitializeInterruptTimers();

#endif

    ExFreePool(ProcessInformation);

}

阶段1 初始化完成以后,内核已经完全初始化,执行体的各个组件进入正常运行状态。但作为一个操作系统,Windows 的引导过程尚未完成,仅仅内核正常工作还不够,系统必须让应用程序也能够运行起来。接下来的引导工作由刚刚启动起来的smss 进程继续进行。


1.3 建立用户登录会话

Windows 内核在阶段1 初始化的最后,启动了一个用户模式进程——会话管理器子系统(smss)。Smss 进程是Windows 操作系统的关键组成部分,它尽管是一个用户模式进程,但有其特殊性:首先,它是可信的,这意味着它可以做一些其他用户进程无法做的事情,比如创建安全令牌;其次,它直接建立在Windows 内核的基础上,只使用Windows内核提供的系统服务,而不依赖于任何一个环境子系统。这不难理解,因为smss 启动时,Windows 子系统尚未启动,而且,启动Windows 子系统本身正是smss 的任务之一。

Smss 进程启动以后,继续完成系统引导过程。Smss 做的工作有相当一部分依赖于注册表中的设置。在内核的阶段1 初始化过程中,配置管理器已经初始化,但是直到smss进程启动起来,只有SYSTEM 储巢已被加载到内存中。加载其他的储巢也是smss 的职责,它通过调用系统服务NtInitializeRegistry 来初始化注册表。在NtInitializeRegistry 系统服务中,除了用户轮廓储巢以外的所有其他储巢,均会被加载到系统中,并且注册表树也将建立起来。实际上,加载这些储巢的工作由CmpInitializeHiveList 函数来完成,它仅仅加载6 个储巢(包括SYSTEM 储巢),正好是表2.6 中的前6 个储巢。

Smss 在注册表中的主键是HKLM\SYSTEM\CurrentControlSet\Control\Session Manager,用于指示smss 在系统初始化阶段做一些必要的工作。图2.17 显示了在Windows Server2003 SP1 系统中该键的一个典型设置。

按照Session Manager 键中的指示,smss 主线程完成以下事项:

运行在启动时执行的程序,这些程序由BootExecute 值指定。

执行启动时的文件删除或重命名任务,这由FileRenameOperations 子键来指定。

打开已知的DLL,并且在对象管理器名字空间的\KnownDlls 目录下创建相应的内存区对象,这些已知DLL 的列表位于KnownDLLs 子键中。已知DLL 是系统全局共享DLL,包括子系统DLL 等,各种应用程序通常都会用到这些DLL。

创建页面文件,页面文件列表由Memory Management 子键中的PagingFiles 值指定。

建立系统的全局环境变量,这些环境变量由Environment 键下的值指定。

加载Windows 子系统的内核模式模块( win32k.sys ) , 这是通过系统服务NtSetSystemInformation 来完成的,该函数也会调用win32k.sys 中的初始化例程(即入口函数)。子系统内核模块的文件路径由SubSytstems 子键的Kmode 值指定。关于Windows 子系统的初始化过程,请参考9.2 节。

启动Windows 子系统进程(csrss.exe)。子系统进程的命令行字符串由SubSystems子键的Windows 值指定。

代码摘自\private\sm\server\sminit.c

NTSTATUS

SmpLoadDataFromRegistry(

    OUT PUNICODE_STRING InitialCommand

    )

/*++

函数功能:

该函数为NT会话管理器从注册表中加载所有的可配置的数据

--*/

{

    NTSTATUS Status;

    PLIST_ENTRY HeadNext;

    PSMP_REGISTRY_VALUE p;

    PVOID OriginalEnvironment;

    RtlInitUnicodeString( &SmpDebugKeywordL"debug" );

    RtlInitUnicodeString( &SmpASyncKeywordL"async" );

    RtlInitUnicodeString( &SmpAutoChkKeywordL"autocheck" );

    InitializeListHead( &SmpBootExecuteList );

    InitializeListHead( &SmpPagingFileList );

    InitializeListHead( &SmpDosDevicesList );

    InitializeListHead( &SmpFileRenameList );

    InitializeListHead( &SmpKnownDllsList );

    InitializeListHead( &SmpExcludeKnownDllsList );

    InitializeListHead( &SmpSubSystemList );

    InitializeListHead( &SmpSubSystemsToLoad );

    InitializeListHead( &SmpSubSystemsToDefer );

    InitializeListHead( &SmpExecuteList );      //初始化相关数据列表

    Status = RtlCreateEnvironmentTRUE, &SmpDefaultEnvironment );   //创建默认的环境块

    if (!NT_SUCCESSStatus )) {

        KdPrint(("SMSS: Unable to allocate default environment - Status == %X\n"Status ));

        returnStatus );

        }

    // In order to track growth in smpdefaultenvironment, make it sm's environment while doing the registry groveling and then restore it

    OriginalEnvironment = NtCurrentPeb()->ProcessParameters->Environment;

    NtCurrentPeb()->ProcessParameters->Environment = SmpDefaultEnvironment;

    Status = RtlQueryRegistryValuesRTL_REGISTRY_CONTROL,

                                     L"Session Manager",

                                     SmpRegistryConfigurationTable,

                                     NULL,

                                     NULL

                                   );

    SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment;

    NtCurrentPeb()->ProcessParameters->Environment = OriginalEnvironment;

    if (!NT_SUCCESSStatus )) {

        KdPrint(( "SMSS: RtlQueryRegistryValues failed - Status == %lx\n"Status ));

        returnStatus );

        }

    Status = SmpInitializeDosDevices();  //初始化DOS设备

    if (!NT_SUCCESSStatus )) {

        KdPrint(( "SMSS: Unable to initialize DosDevices configuration - Status == %lx\n"Status ));

        returnStatus );

        }

    Head = &SmpBootExecuteList;

    while (!IsListEmptyHead )) {

        Next = RemoveHeadListHead );

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

#ifdef SMP_SHOW_REGISTRY_DATA

        DbgPrint"SMSS: BootExecute( %wZ )\n", &p->Name );

#endif

        SmpExecuteCommand( &p->Name, 0 );

        RtlFreeHeapRtlProcessHeap(), 0, p );

        }     //运行在启动时执行的程序,这些程序由BootExecute值来指定

    SmpProcessFileRenames();   //执行启动时的文件删除或重命名任务,这由FileRenameOperations子键来指定

    Status = SmpInitializeKnownDlls();  //初始化已知的dlls

    if (!NT_SUCCESSStatus )) {

        KdPrint(( "SMSS: Unable to initialize KnownDll configuration - Status == %lx\n"Status ));

        returnStatus );

        }

    // 处理页面文件列表.

    Head = &SmpPagingFileList;

    while (!IsListEmptyHead )) {

        Next = RemoveHeadListHead );

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

#ifdef SMP_SHOW_REGISTRY_DATA

        DbgPrint"SMSS: PagingFile( %wZ )\n", &p->Name );

#endif

        SmpAddPagingFile( &p->Name );

        RtlFreeHeapRtlProcessHeap(), 0, p );

        }

    // 创建页面文件

    SmpCreatePagingFiles();

    // 完成注册表初始化

    NtInitializeRegistry(FALSE);

    Status = SmpCreateDynamicEnvironmentVariables( );

    if (!NT_SUCCESSStatus )) {

        return Status;

        }

    // Translate the system partition information stored during IoInitSystem into a DOS path and store in Win32-standard location.

    SmpTranslateSystemPartitionInformation();

    Head = &SmpSubSystemList;

    while (!IsListEmptyHead )) {

        Next = RemoveHeadListHead );

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

        if ( !_wcsicmpp->Name.BufferL"Kmode" )) {

            BOOLEAN TranslationStatus;

            UNICODE_STRING FileName;   //"Kmode"指定子系统内核模块的文件路径

            TranslationStatus = RtlDosPathNameToNtPathName_U(

                                    p->Value.Buffer,

                                    &FileName,

                                    NULL,

                                    NULL

                                    );

            if ( TranslationStatus ) {

                PVOID State;

                Status = SmpAcquirePrivilegeSE_LOAD_DRIVER_PRIVILEGE, &State );

                if (NT_SUCCESSStatus )) {

                    Status = NtSetSystemInformation(

                                SystemExtendServiceTableInformation,

                                (PVOID)&FileName,

                                sizeof(FileName)

                                );

                    RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer);

                    SmpReleasePrivilegeState );

                    if ( !NT_SUCCESS(Status) ) {

                        Status = STATUS_SUCCESS;

                        }

                    }

                }

            else {

                Status = STATUS_OBJECT_PATH_SYNTAX_BAD;

                }

            }

#ifdef SMP_SHOW_REGISTRY_DATA

        DbgPrint"SMSS: Unused SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value );

#endif

        RtlFreeHeapRtlProcessHeap(), 0, p );

        }

    Head = &SmpSubSystemsToLoad;

    while (!IsListEmptyHead )) {

        Next = RemoveHeadListHead );

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

#ifdef SMP_SHOW_REGISTRY_DATA

        DbgPrint"SMSS: Loaded SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value );

#endif

        if (!_wcsicmpp->Name.BufferL"debug" )) {

            SmpExecuteCommand( &p->ValueSMP_SUBSYSTEM_FLAG | SMP_DEBUG_FLAG );

            }

        else {

            SmpExecuteCommand( &p->ValueSMP_SUBSYSTEM_FLAG );

            }

        RtlFreeHeapRtlProcessHeap(), 0, p );

        }

    Head = &SmpExecuteList;

    if (!IsListEmptyHead )) {

        Next = Head->Blink;

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

        RemoveEntryList( &p->Entry );

        *InitialCommand = p->Name;

        // This path is only taken when people want to run ntsd -p -1 winlogon

        // This is nearly impossible to do in a race free manner.

// In some cases, we can get in a state where we can not properly fail a debug API. 

// This is due to the subsystem switch that occurs when ntsd is invoked on csr. If csr is relatively idle, this does not occur. 

// If it is active when you attach, then we can get into a potential race. The slimy fix is to do a 5 second delay

        // if the command line is anything other that the default.

            {

                LARGE_INTEGER DelayTime;

                DelayTime.QuadPart = Int32x32To64( 5000, -10000 );

                NtDelayExecution(

                    FALSE,

                    &DelayTime

                    );

            }

        }

    else {

        RtlInitUnicodeStringInitialCommandL"winlogon.exe" );

        InitialCommandBuffer[ 0 ] = UNICODE_NULL;

        LdrQueryImageFileExecutionOptionsInitialCommand,

                                           L"Debugger",

                                           REG_SZ,

                                           InitialCommandBuffer,

                                           sizeofInitialCommandBuffer ),

                                           NULL

                                         );

        if (InitialCommandBuffer[ 0 ] != UNICODE_NULL) {

            wcscatInitialCommandBufferL" " );

            wcscatInitialCommandBufferInitialCommand->Buffer );

            RtlInitUnicodeStringInitialCommandInitialCommandBuffer );

            KdPrint(( "SMSS: InitialCommand == '%wZ'\n"InitialCommand ));

            }

        }

    while (!IsListEmptyHead )) {

        Next = RemoveHeadListHead );

        p = CONTAINING_RECORDNext,

                               SMP_REGISTRY_VALUE,

                               Entry

                             );

#ifdef SMP_SHOW_REGISTRY_DATA

        DbgPrint"SMSS: Execute( %wZ )\n", &p->Name );

#endif

        SmpExecuteCommand( &p->Name, 0 );

        RtlFreeHeapRtlProcessHeap(), 0, p );

        }

#ifdef SMP_SHOW_REGISTRY_DATA

    DbgPrint"SMSS: InitialCommand( %wZ )\n"InitialCommand );

#endif

    returnStatus );

}

如果利用Microsoft 提供的符号信息来调试WRK 系统,则可以观察到以上这些行为均在smss 主模块(smss.exe)的SmpLoadDataFromRegistry 函数中完成。由此也可以看出,Windows 子系统作为会话(session)的一部分,它的实例由smss 来启动。Smss 除了依据注册表中的设置来完成必要的系统引导工作以外,还执行以下的步骤,以进一步提供多会话和本地登录服务:

创建LPC 端口对象(\SmApiPort),以接收“加载子系统”或“创建会话”的请求。

启动登录进程(winlogon.exe),登录进程将会承担起与用户登录有关的事项。

NTSTATUS

SmpInit(

    OUT PUNICODE_STRING InitialCommand,

    OUT PHANDLE WindowsSubSystem

    )

{

    NTSTATUS st;

    OBJECT_ATTRIBUTES ObjA;

    HANDLE SmpApiConnectionPort;

    UNICODE_STRING Unicode;

    NTSTATUS Status;

    ULONG HardErrorMode;

    SmBaseTag = RtlCreateTagHeapRtlProcessHeap(),

                                  0,

                                  L"SMSS!",

                                  L"INIT\0"

                                  L"DBG\0"

                                  L"SM\0"

                                );

    // Make sure we specify hard error popups

    HardErrorMode = 1;

    NtSetInformationProcessNtCurrentProcess(),

                             ProcessDefaultHardErrorMode,

                             (PVOID) &HardErrorMode,

                             sizeofHardErrorMode )

                           );

    RtlInitUnicodeString( &SmpSubsystemNameL"NT-Session Manager" );

    RtlInitializeCriticalSection(&SmpKnownSubSysLock);

    InitializeListHead(&SmpKnownSubSysHead);

    RtlInitializeCriticalSection(&SmpSessionListLock);

    InitializeListHead(&SmpSessionListHead);

    SmpNextSessionId = 1;

    SmpNextSessionIdScanMode = FALSE;

    SmpDbgSsLoaded = FALSE;

    // 初始化安全描述符以获取更多的特权

    // (保护模式尚未从注册表中读取).

    st = SmpCreateSecurityDescriptorsTRUE );

    if ( !NT_SUCCESS(st) ) {

        return(st);

        }

    InitializeListHead(&NativeProcessList);

    SmpHeap = RtlProcessHeap();

    RtlInitUnicodeString( &UnicodeL"\\SmApiPort" );

    InitializeObjectAttributes( &ObjA, &Unicode, 0, NULLSmpApiPortSecurityDescriptor);

    st = NtCreatePort(

            &SmpApiConnectionPort,

            &ObjA,

            sizeof(SBCONNECTINFO),

            sizeof(SMMESSAGE_SIZE),

            sizeof(SBAPIMSG) * 32

            );    //此处创建LPC端口对象

    ASSERTNT_SUCCESS(st) );

    SmpDebugPort = SmpApiConnectionPort;

    st = RtlCreateUserThread(

            NtCurrentProcess(),

            NULL,

            FALSE,

            0L,

            0L,

            0L,

            SmpApiLoop,

            (PVOIDSmpApiConnectionPort,

            NULL,

            NULL

            );

    ASSERTNT_SUCCESS(st) );

    st = RtlCreateUserThread(

            NtCurrentProcess(),

            NULL,

            FALSE,

            0L,

            0L,

            0L,

            SmpApiLoop,

            (PVOIDSmpApiConnectionPort,

            NULL,

            NULL

            );

    ASSERTNT_SUCCESS(st) );

    // Configure the system

    Status = SmpLoadDataFromRegistryInitialCommand );

    if (NT_SUCCESSStatus )) {

        *WindowsSubSystem = SmpWindowsSubSysProcess;

        }

    returnStatus );

}

加载win32k.sys的代码位于WRK中base\ntos\ex\sysinfo.c

//省略部分代码

case SystemExtendServiceTableInformation:

            {

                ULONG_PTR EntryPoint;

                UNICODE_STRING Image;

                PVOID ImageBaseAddress;

                PDRIVER_INITIALIZE InitRoutine;

                PIMAGE_NT_HEADERS NtHeaders;

                PVOID SectionPointer;

                DRIVER_OBJECT Win32KDevice;

  // 判断系统信息缓冲区长度是否正确

                if (SystemInformationLength != sizeof(UNICODE_STRING)) {

                    return STATUS_INFO_LENGTH_MISMATCH;

                }

// 如果先前的模式不是内核模式,就验证是否能够加载win32k子系统

                if (PreviousMode != KernelMode) {

// 检测是否为会话首进程

                    if (MmIsSessionLeaderProcess(PsGetCurrentProcess()) == FALSE) {

                        return STATUS_PRIVILEGE_NOT_HELD;

                    }

                    

// 检测是否有权限加载驱动

                    if (SeSinglePrivilegeCheck(SeLoadDriverPrivilegeUserMode) == FALSE) {

                        return STATUS_PRIVILEGE_NOT_HELD;

                    }

                   

// 检测win32k子系统是否被加载

                    try {

                       

     // 测试并读取unicode字符串描述符

                        ProbeAndReadUnicodeStringEx(&Image,(PUNICODE_STRING)SystemInformation);

// 检测unicode字符串的长度是否正确

                        if (Image.Length != WIN32K_PATH_SIZE) {

                            return STATUS_PRIVILEGE_NOT_HELD;

                        }

                        

// 测试unicode字符串的缓冲区并检测路径名称

                        ProbeForReadSmallStructure(Image.Buffer,

                                                   WIN32K_PATH_SIZE,

                                                   sizeof(UCHAR));

                        if (memcmp(Image.Buffer, &Win32kFullPath[0], WIN32K_PATH_SIZE) != 0) {

                            return STATUS_PRIVILEGE_NOT_HELD;

                        }

    

                        

// 初始化win32全路径名称的字符串描述符

                        Image.Buffer = &Win32kFullPath[0];

                        Image.MaximumLength = WIN32K_PATH_SIZE;

                    } except(EXCEPTION_EXECUTE_HANDLER) {

                        return GetExceptionCode();

                    }

// 递归调用这个内核服务,迫使先前的模式进入内核

                    Status = ZwSetSystemInformation(SystemExtendServiceTableInformation,

                                                    (PVOID)&Image,

                                                    sizeof(Image));

                    return Status;

                }

                

// 先前的模式即为内核-加载指定的驱动

                Image = *(PUNICODE_STRING)SystemInformation;

                Status = MmLoadSystemImage(&Image,

                                           NULL,

                                           NULL,

                                           MM_LOAD_IMAGE_IN_SESSION,

                                           &SectionPointer,

                                           (PVOID *)&ImageBaseAddress);

                if (NT_SUCCESS(Status) == FALSE) {

                    return Status;

                }

               

// 获取映像基地址

                NtHeaders = RtlImageNtHeader(ImageBaseAddress);

                if (NtHeaders == NULL) {

                    MmUnloadSystemImage(SectionPointer);

                    return STATUS_INVALID_IMAGE_FORMAT;

                }

                

// 调用win32k.sys的PE头中指定的入口函数EntryPoint

                EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;

                EntryPoint += (ULONG_PTR)ImageBaseAddress;

                InitRoutine = (PDRIVER_INITIALIZE)EntryPoint;

                RtlZeroMemory(&Win32KDevicesizeof(Win32KDevice));

                ASSERT(KeGetCurrentIrql() == 0);

                Win32KDevice.DriverStart = (PVOID)ImageBaseAddress;

                Status = (InitRoutine)(&Win32KDeviceNULL);

                ASSERT(KeGetCurrentIrql() == 0);

                

// 不成功的话就卸载

                if (NT_SUCCESS(Status) == FALSE) {

                    MmUnloadSystemImage(SectionPointer);

                } else {

                    MmSessionSetUnloadAddress(&Win32KDevice);

                }

            }

            break;

Smss 的主线程在完成了以上描述的初始化工作以后,将在csrss 进程和winlogon 进程的句柄上等待。一旦等待成功,则意味着这两个进程中至少有一个退出了,于是系统崩溃。Windows 操作系统依赖于这两个进程,所以,它们也是保持Windows 操作系统正常运行不可缺少的组成部分。

接下来引导过程转到了winlogon 进程。它的职责包括:

创建初始的窗口站(WinSta0),并且为该窗口站创建一个桌面线程和RIT(Raw InputThread)以便接收标准输入。

创建登录桌面和默认桌面。登录桌面只有winlogon 进程才可以访问,因而也称为winlogon 桌面;而默认桌面允许其他进程访问。因此,当登录桌面活动时,其他进程无法访问与该桌面关联的代码或数据。Windows 用这种方式来保护与口令相关的操作,以及锁定桌面或解除桌面锁定这样的安全操作。

启动服务控制管理器(SCM,Service Control Manager)进程(services.exe)。在启动Windows 服务的过程中,会有更多的窗口站被创建。SCM 进程加载所有“自动-启动”类型的服务和设备驱动程序。

启动本地安全权威子系统( lsass ) 进程。然后与它建立一个LPC 连接(LsaAuthenticationPort 端口),以便在登录、注销和口令操作过程中交换信息。

关于这里提到的窗口站、桌面、桌面线程和RIT ,它们是Windows 窗口管理中的重要组成部分,由Windows 子系统内核模块win32k.sys 实现。Winlogon 的登录是通过一种称为GINA(图形化识别和认证,Graphical Identification andAuthentication)的可扩展机制来完成的,例如,默认的GINA 为msgina,可显示标准的Windows 登录对话框,指示用户输入用户名和口令。Winlogon 对于Ctrl+Alt+Del 按键序列做了特殊的保护,以防止其他程序截取此按键序列。第三方厂商可以利用像指纹或虹膜这样的生物特征来识别用户,然后从一个加密的数据库中提取出他们的口令,这样就不需要用户再输入口令了。

当winlogon 通过GINA 获得了用户名和口令以后,它首先调用lsass 函数LsaLookupAuthenticationPackage 以获得一个认证包的句柄,然后调用LsaLogonUser 将登录信息传递给认证包。一旦认证包认证了当前用户,则winlogon 继续该用户的登录过程;否则认证失败。因此,认证过程是由lsass 来完成的。

在登录过程的最后,winlogon 检查注册表HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Userinit 的值,并创建一个进程来运行该值字符串。该值串的默认值为userinit.exe 程序的路径。Userinit 进程加载当前登录用户的轮廓,然后检查HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,并创建一个进程来运行该值字符串;如果该值不存在,则运行HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Shell 的值,其默认值为explorer.exe。然后,userinit 进程退出。由于当前登录会话的Shell 程序(explorer.exe)已经启动,因此用户可以在桌面上操作了。

至此,引导过程结束,用户登录到系统中,并可通过explorer.exe 程序的用户界面操作系统中的资源,例如文件系统中的目录和文件;也可以启动各种应用程序。下图显示了从会话管理器启动一直到Shell 程序启动之间发生的事项。

在系统引导过程中,有多个进程被创建,包括smss 、csrss 、winlogon 、SCM(services.exe)、lsass、userinit(登录完成后自动退出)、explorer 等。这些进程都是操作系统的一部分,而且大多数还是可信的。实际上,在系统启动以后,当用户开始在Shell程序中操作时,他们通常可以看到更多的进程,这其中有些进程是由SCM 启动并初始化的。在Windows 中,SCM 提供了在系统启动时启动进程的机制,这些进程被称为服务(service)进程,它们类似于UNIX 中的守护进程。例如,Web 服务器或者数据库服务器显然应该以这样的方式来启动,因为无论是否有人登录到系统中,它们都必须被启动。在每个服务进程中,它可以宿纳一个或多个Windows 服务(Windows service)。

Smss和Winlogon进程的初始化过程代码不完整,这里给出两个文件的idb,借助IDA的graph view可以很清晰地看出流程.

Smss:主要的函数有SmConnectToSm,SmpLoadDataFromRegistry以及SmpLoadSubSystemForMuSession

发布了172 篇原创文章 · 获赞 132 · 访问量 189万+

猜你喜欢

转载自blog.csdn.net/cosmoslife/article/details/7855425
今日推荐