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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章