#ifdefi386
//此处硬编码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); gotoLoadFailed; }
// 关闭调试系统 BlLogTerminate();
// 将控制权转换给已加载的映像 (SystemEntry)(BlLoaderBlock); Status = EBADF;
BlFatalError(LOAD_SW_BAD_FILE_CLASS,DIAG_BL_KERNEL_INIT_XFER,LOAD_SW_FILE_REINST_ACT); LoadFailed:
returnStatus; }
至此,引导系统所需的模块(包括内核映像、HAL,以及必要的设备驱动程序)都已经被加载到内存中。而且,在此过程中os loader 也已经构造了一个参数块,记录下了这次引导过程中加载器所获得的各种参数信息。参数块的类型为LOADER_PARAMETER_BLOCK,Windows 内核在初始化过程中将会用到这些参数信息。WRK 中包含有它的定义,如下(见public\\internal\\base\\inc\\arc.h 文件):
typedefstruct_LOADER_PARAMETER_BLOCK {
LIST_ENTRYLoadOrderListHead; //加载的模块链表,每个元素都为KLDR_DATA_TABLE_ENTRY
LIST_ENTRYMemoryDescriptorListHead;
//内存描述符链表,每个元素都为MEMORY_ALLOCATION_DESCRIPTOR
LIST_ENTRYBootDriverListHead;//引导驱动程序链表,每个元素都为BOOT_DRIVER_LIST_ENTRY ULONG_PTRKernelStack; ULONG_PTRPrcb; ULONG_PTRProcess; ULONG_PTRThread; PVOIDRegistryBase;
//内核栈顶
//进程环境,指向一个进程控制块 //初始进程,EPROCESS //初始线程,ETHREAD //系统储巢的长度 //系统储巢的基地址
ULONGRegistryLength;
PCONFIGURATION_COMPONENT_DATAConfigurationRoot; //配置树,包含ISA,磁盘和ACPI的配置数据 PCHARArcBootDeviceName;
//引导分区的ARC名称
PCHARArcHalDeviceName; //系统分区的ARC名称
PCHARNtBootPathName; //OS目录的路径名称,比如\PCHARNtHalPathName; PCHARLoadOptions;
//OD加载器的路径名称,比如\//引导选项,来自boot.ini
//包含ANSI代码页,OEM代码页和Unicode码表 //OEM字体文件
PNLS_DATA_BLOCKNlsData; PVOIDOemFontFile;
PARC_DISK_INFORMATIONArcDiskInformation; //所有磁盘的签名结构
struct_SETUP_LOADER_BLOCK *SetupLoaderBlock; //网络引导或文字模式安装引导 PLOADER_PARAMETER_EXTENSIONExtension; //扩展部分
union {
I386_LOADER_BLOCKI386; // 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,
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,
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