福州做企业网站的公司,一个网站域名多少钱,人才招聘网站开发背景,气象网站建设需求方案转载自#xff1a;https://blog.csdn.net/cosmoslife/article/details/7855425
Windows 引导过程
Windows 内核中的各个组件和各种机制在起作用以前#xff0c;必须首先被初始化。此初始化工作是在系统引导时完成的。当用户打开计算机的电源开关时#xff0c;计算机便开始…转载自https://blog.csdn.net/cosmoslife/article/details/7855425
Windows 引导过程
Windows 内核中的各个组件和各种机制在起作用以前必须首先被初始化。此初始化工作是在系统引导时完成的。当用户打开计算机的电源开关时计算机便开始运行但操作系统并不立即获得控制权而是BIOS 代码首先获得控制它执行必要的硬件检测工作并允许用户通过一些功能键来配置当前系统中的硬件设置甚至诊断硬件问题然后才将控制权交给操作系统。
1.1 内核加载
在Intel x86 系统上Windows 操作系统获得控制首先从硬盘的主引导记录MBRMaster 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 $4org_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 ;不是,只有 x00 或者x80 是有效的 add si,16 ;执行到下一个入口点 dec bl jnz next int 18h ;未检测到可引导的入口,返回
boot: mov dx,[si] ;引导开始处 mov cx,[si2] 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; theyre 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,[bx2] ; 检测是否是第一次开启保护模式以及分页机制 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 [si2],VideoSelector mov si,offset _vp mov word ptr [si2],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 dont know where the page directory is, since it was allocated in the osloader.
; So we dont 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 [si2],0b800h mov si,offset _vp mov word ptr [si2],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 [esp2] ; 获取入口点参数 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(Couldnt initialize memory\n); while (1) { } }
// 初始化OS loader和I/O系统 Status BlIoInitialize(); if (Status ! ESUCCESS) { BlPrint(Couldnt 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.com在i386目录下 Status BlOpen( DriveId, \\i386\\ntdetect.com, ArcOpenReadOnly, DetectFileId ); } else {
#endif Status BlOpen( DriveId, \\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(Buffer, LoadOptions); 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) { } } ExternalServicesTableBootContextRecord-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 i1; do { sprintf(NameBuffer, multi(0)disk(0)rdisk(0)partition(%u),i); Status ArcOpen(NameBuffer,ArcOpenReadOnly,FileId); if (Status ! ESUCCESS) { // 遍历所有分区未找到合适的对象,故设置默认值. i1; break; } else { // 读取前512个字节 Status ArcRead(FileId, SectorBuffer, 512, Count); ArcClose(FileId); if (StatusESUCCESS) { // 只需要比较前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 UseTimeOutTRUE; BOOLEAN AlreadyInitialized FALSE; extern BOOLEAN FwDescriptorsValid; // 打开引导分区以便加载引导驱动. Status ArcOpen(PartitionName, ArcOpenReadOnly, DriveId); if (Status ! ESUCCESS) { BlPrint(Couldnt open drive %s\n,PartitionName); BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName); goto BootFailed; } // 初始化双字节内码系统以及显示支持. TextGrInitialize(DriveId); do { Status BlOpen( DriveId, \\boot.ini, ArcOpenReadOnly, BootFileId ); //此处开始打开boot.ini BootFile MyBuffer; RtlZeroMemory(MyBuffer, SECTOR_SIZE32); if (Status ! ESUCCESS) { BootFile[0]\0; } else { // 通过从头到尾读取boot.ini文件获取大小 FileSize 0; do { Status BlRead(BootFileId, BootFile, SECTOR_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文件大于一个扇区的大小,那么就需要重新分配更大的缓冲区 BootFileFwAllocateHeap(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 BlRead( BootFileId, 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 pBootFile; while ((*p!\0) (*p!26)) { p; } if (*p ! \0) { *p\0; } BlClose(BootFileId); } if (!AlreadyInitialized) { AbiosInitDataStructures(); } MdShutoffFloppy(); //关闭软驱 TextClearDisplay(); //清除显示文本 pBlSelectKernel(DriveId,BootFile, LoadOptions, UseTimeOut); if (!AlreadyInitialized) { BlPrint(BlFindMessage(BL_NTDETECT_MSG)); if (!BlDetectHardware(DriveId, LoadOptions)) { 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; //设置引导菜单无等待操作的时间,即用户如不操作则一直停留 UseTimeOutFALSE; i0; 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,consoleinmulti(0)key(0)keyboard(0)); strcpy(ConsoleOutputName,consoleoutmulti(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(BootPartitionName, ArcOpenReadOnly, 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 BlOutputDotsFALSE;
#else
BOOLEAN BlOutputDotsTRUE;
#endif
CHAR KernelFileName[8131]ntoskrnl.exe;
CHAR HalFileName[8131]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(Argc, Argv, consoleout); if (ConsoleOutDevice NULL) { return ENODEV; } Status ArcOpen(ConsoleOutDevice, ArcOpenWriteOnly, BlConsoleOutDeviceId); if (Status ! ESUCCESS) { return Status; }
// 获取控制台输入设备的名字并获取读取权限 ConsoleInDevice BlGetArgumentValue(Argc, Argv, consolein); if (ConsoleInDevice NULL) { return ENODEV; } Status ArcOpen(ConsoleInDevice, ArcOpenReadOnly, 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(NULL, NULL); if (Status ! ESUCCESS) { BlFatalError(LOAD_HW_FW_CFG_CLASS, DIAG_BL_CONFIG_INIT, LOAD_HW_FW_CFG_ACT); goto LoadFailed; } // 复制osloadoptions参数给LoaderBlock LoadOptions BlGetArgumentValue(Argc, Argv, osloadoptions); if (LoadOptions ! NULL) { FileSize strlen(LoadOptions)1; FileName (PCHAR)BlAllocateHeap(FileSize); strcpy(FileName, LoadOptions); BlLoaderBlock-LoadOptions FileName; //检测标志值判断是否应该输出文件名而不是单独的原点 if ((strstr(FileName,SOS)!NULL) || (strstr(FileName,sos)!NULL)) { BlOutputDotsFALSE; } FileNamestrstr(BlLoaderBlock-LoadOptions,HAL); if (FileName) { for (i0; isizeof(HalFileName); i) { if (FileName[4i] ) { HalFileName[i]\0; break; } HalFileName[i]FileName[4i]; } } HalFileName[sizeof(HalFileName)-1]\0; FileNamestrstr(BlLoaderBlock-LoadOptions,KERNEL); if (FileName) { for (i0; isizeof(KernelFileName); i) { if (FileName[7i] ) { KernelFileName[i]\0; break; } KernelFileName[i]FileName[7i]; } } KernelFileName[sizeof(KernelFileName)-1]\0; } else { BlLoaderBlock-LoadOptions NULL; } // 获取加载设备的名称以及读取的权限. LoadDevice BlGetArgumentValue(Argc, Argv, 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(LoadDevice, ArcOpenReadOnly, LoadDeviceId); if (Status ! ESUCCESS) { BlFatalError(LOAD_HW_DISK_CLASS, DIAG_BL_OPEN_BOOT_DEVICE, LOAD_HW_DISK_ACT); goto LoadFailed; } // 获取系统设备的名称以及读取的权限. SystemDevice BlGetArgumentValue(Argc, Argv, 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(SystemDevice, ArcOpenReadOnly, 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(Argc, Argv, 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(BootDirectoryPath, LoadFileName); strcat(BootDirectoryPath, \\System32\\); // 生成ntoskrnl.exe的全路径 // \winnt\system32\ntoskrnl.exe strcpy(KernelDirectoryPath, BootDirectoryPath); strcat(KernelDirectoryPath, KernelFileName); // 加载内核模块映像文件. BlOutputLoadMessage(LoadDevice, KernelDirectoryPath); 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(Argc, Argv, 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 Limit; Index 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)BlAllocateHeap( FileSize); strcpy(FileName, LoadFileName); strcat(FileName, \\);
BlLoaderBlock-NtBootPathName FileName;
// 生成符合ARC命名规范的HAL设备名称以及NT路径名.
#ifdef i386 strcpy(DeviceName[0], BlGetArgumentValue(Argc, Argv, 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_BLOCKWindows 内核在初始化过程中将会用到这些参数信息。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 名称是指符合ARCAdvanced 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[PcPrcbDataPbNumber], 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.AsApcListHead4, 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 [ecx5],089h ; 32bit, dpl0, 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 [ecx5],085h ; 描述符特权级别dpl0, present, taskgate mov word ptr [ecx2],KGDT_DF_TSS lea ecx,[edi]KGDT_DF_TSS mov byte ptr [ecx5],089h ; 32位, 描述符特权级别dpl0, present, TSS32, not busy mov edx,offset FLAT:_KiDoubleFaultTSS mov eax,edx mov [ecxKgdtBaseLow],ax shr eax,16 mov [ecxKgdtBaseHi],ah mov [ecxKgdtBaseMid],al mov eax, MINIMUM_TSS_SIZE mov [ecxKgdtLimitLow],ax
; KiInitializeTSS(
; 双重故障任务状态段
; ); push edx stdCall _KiInitializeTSS, edx pop edx mov eax,cr3 mov [edxTssCr3],eax mov eax, offset FLAT:_KiDoubleFaultStack mov dword ptr [edxTssEsp],eax mov dword ptr [edxTssEsp0],eax mov dword ptr [edx020h],offset FLAT:_KiTrap08 mov dword ptr [edx024h],0 ; eflags mov word ptr [edx04ch],KGDT_R0_CODE ; 设置CS的值 mov word ptr [edx058h],KGDT_R0_PCR ; 设置FS的值 mov [edx050h],ss mov word ptr [edx048h],KGDT_R3_DATA OR RPL_MASK ; Es mov word ptr [edx054h],KGDT_R3_DATA OR RPL_MASK ; Ds
; 设置32位不可屏蔽中断任务门去获取不可屏蔽中断异常 mov eax,KissIdt lea ecx,[eax]10h ; Descriptor 2 mov byte ptr [ecx5],085h ; dpl0, present, taskgate mov word ptr [ecx2],KGDT_NMI_TSS lea ecx,[edi]KGDT_NMI_TSS mov byte ptr [ecx5],089h ; 32bit, dpl0, present, TSS32, not busy mov edx,offset FLAT:_KiNMITSS mov eax,edx mov [ecxKgdtBaseLow],ax shr eax,16 mov [ecxKgdtBaseHi],ah mov [ecxKgdtBaseMid],al mov eax, MINIMUM_TSS_SIZE mov [ecxKgdtLimitLow],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 [edxTssCr3],eax mov eax, offset FLAT:_KiDoubleFaultTSS mov eax, dword ptr [eax038h] ; get DF stack mov dword ptr [edxTssEsp0],eax ; use it for NMI stack mov dword ptr [edx038h],eax mov dword ptr [edx020h],offset FLAT:_KiTrap02 mov dword ptr [edx024h],0 ; eflags mov word ptr [edx04ch],KGDT_R0_CODE ; set value for CS mov word ptr [edx058h],KGDT_R0_PCR ; set value for FS mov [edx050h],ss mov word ptr [edx048h],KGDT_R3_DATA OR RPL_MASK ; Es mov word ptr [edx054h],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]ThApcStateAsProcess, ecx ; set addr of threads process
; 设置 PCR: Teb, Prcb 指针.
; Prcb 相关参数将在函数 _KiInitializeKernel中设置 mov dword ptr PCR[PcTeb], 0 ; PCR-Teb 0
; 初始化KernelDr7和KernelDr6为0. 该操作必须在调试器调用前完成.
mov dword ptr PCR[PcPrcbDataPbProcessorStatePsSpecialRegistersSrKernelDr6],0
mov dword ptr PCR[PcPrcbDataPbProcessorStatePsSpecialRegistersSrKernelDr7],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 [eax40h] ; 保存双重故障描述符 push dword ptr [eax44h] push dword ptr [eax10h] ; 保存不可屏蔽中断故障描述符 push dword ptr [eax14h] mov edi,KissIdt mov esi,offset FLAT:_IDT mov ecx,offset FLAT:_IDTLEN ; shr ecx,2 rep movsd pop dword ptr [eax14h] ; 恢复双重故障描述符
pop dword ptr [eax10h] pop dword ptr [eax44h] ; 恢复不可屏蔽中断故障描述符 pop dword ptr [eax40h]
ifdef QLOCK_STAT_GATHER EXTRNP KiCaptureQueuedSpinlockRoutines,0,,FASTCALL fstCall KiCaptureQueuedSpinlockRoutines
endif
kiss_notp0:
ifndef NT_UP
; 告知引导处理器该处理器已经开始运行. stdCall _KiProcessorStart
endif
; A new processor cant come online while execution is frozen
; Take freezelock while adding a processor to the system
; NOTE: dont 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_LENGTHKTRAP_FRAME_LENGTHKTRAP_FRAME_ALIGN push CR0_EMCR0_TSCR0_MP ; 为Cr0NpxState预留空间
; arg6 - LoaderBlock
; arg5 - processor number
; arg4 - addr of prcb
; arg3 - idle threads 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[PcPrcbDataPbCurrentThread] ; 设置空闲线程的地址 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 KiIdleLoop0 ; 进入空闲循环
stdENDP _KiSystemStartup