Windows NT引导过程源代码分析

Windows NT的引导过程始于MBR中的引导代码,它读取分区表找到可引导分区,将引导扇区加载到内存并执行。引导扇区代码根据文件系统(如NTFS或FAT)加载ntldr,并跳转到ntldr执行。ntldr负责加载系统文件,初始化NTFS相关信息,启动smss.exe,创建LPC端口,加载winlogon.exe并进行用户登录处理。引导过程涉及多个关键进程,如smss、csrss、winlogon和服务控制管理器等,确保系统正常启动和安全登录。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值