--------------------------------------------------------------------------------------
操作系统的启动
启动的英文是boot,但又被称为引导,boot原本意思是靴子的意思,有一句话是这样说的

"pull oneself up by one's bootstraps"

让计算机运行程序是一件非常矛盾的事情,因为计算机只有运行程序才能启动计算机但是不启动计算机怎么能运行程序?,所以这是一个很矛盾的问题,所以工程师费劲心思制造出了一块固有的程序,叫boot程序,也就是引导程序,boot程序被放进内存中,用以做一些初始化的工作。

首先有一块固有的ROM芯片,里面是写好的一组程序这个程序叫做bios,当计算机加点启动时,首先执行的就是bios里的程序
BIOS里固化的程序有
自诊断程序(会读取CMOS RAM(可读写)的参数即BIOS设定系统参数的存放场所),也称作POST操作(Power On Self-Test)
CMOS设置程序(你可以通过敲击相应的热键进入BIOS设置界面,在那里你可以更改BIOS的设置,最终写入的地方就是CMOS RAM,所以BIOS设置也就是对CMOS RAM设置),
系统自装载程序(也就是根据磁盘活动分区的主引导记录找到引导程序,用以将操作系统内核装载进内存),
I/O驱动程序(在这区间,需要必要的通信),
中断服务(用于驱动硬件做一些事情)

boot启动分四个阶段:
1.开机会最开始访问0xFFFF0地址,然后跳转到BIOS的初始化程序,把BIOS ROM中的初始化程序复制到内存中执行。BIOS会进行自检,其具体操作是读取CMOS RAM里的一些信息,若出现错误,机器会报不同声响的蜂鸣声。这个操作也称为POST(POST很短,只是检测非常重要的设备->640k的常规内存),Power On Self-Test,因为作为最底层的硬件设备(例如内存,显卡等最基础的硬件设备),初始化需要保证他们必须没有错误,所以它的优先级是最高的。细节:先对内存进行一个检索,看它是否存在,或者内存有问题,当然,此时只会检索640kb的内存,因为此时在实模式下。接下来BIOIS会查找显卡的BIOS,存放显卡的ROM芯片的起始地址通常在0xC0000,系统BIOS如果找到它,会调用它的BIOS的初始化代码,多数显卡会显示一些基础信息,都是一闪而过的。系统BIOS会接着查找其他设备的BIOS程序,找到之后同样调用它们的BIOS初始化代码。查找完其它设备的BIOS后,系统BIOS才会显示它自己的画面,接着会测试所有的RAM。内存测试通过后,接着系统BIOS会初始化一些标准硬件,包括硬盘,CD-ROM,串口,并口等设备,标准设备检测完毕后。系统BIOS支持即插即用Pnp的代码会开始检测和配置这些设备,没找到一个就会显示该设备的基础信息,同时为该设备分配中断(注意这里的中断时指最底层的硬件设备的中断),DMA通道(Direct Memory Access 直接内存访问,指不经过cpu,直接通过通道传输数据),接下来就是BIOS与CMOS的信息交互。如果有问题,BIOS会直接控制喇叭发声来报告错误,喇叭声音的长短大小代表了错误的类型。

常规内存在内存分配表中占用最前面的位置,从0KB到640KB(地址00000H~9FFFFH),共占640KB的容量。

640k内存图如下,注意,启动的时候用到的全是真实内存

启动postgre 启动的英文_BIOS


2.硬件没有问题,那么就进入下一阶段。BIOS 开始搜寻可引导的存储设备(即根据用户指定的引导顺序来寻找引导程序),这个称为Boot Sequence,一般就是从硬盘引导内核,如果磁盘内的操作系统数据受到破坏,那么这个时候就需要从usb或其它可移动设备进行引导,需要更改引导顺序。一般来说这时候会读取磁盘中的MBR信息,也就是主引导记录,它前面的440字节是引导程序,会将它读入内存,如果引导程序没有问题会把控制权交给其活动分区的PBR(Partition Boot Record)。

其主要实现代码如下,注释已经完整给出,最好动态调试下,了解流程,具体动态调试。用IDA就可以很方便的调试,选择Remote GDB Debugger 然后填上相应的代码即可,具体操作在吾爱上有教程。注意,用IDA调试的时候,在0x7C00处下断点要注意不要勾选硬件断点,因为此时我们是处于实模式下,而硬件断点需要DR寄存器,我之前就不小心下了个硬件断点,然后就彻底崩掉了…

MEMORY:7C00 xor     ax, ax							;可以看出引导程序是被复制到0x7c00处执行的
MEMORY:7C02 mov     ss, ax
MEMORY:7C04 mov     sp, 7C00h                       ; 0:7c00
MEMORY:7C07 sti
MEMORY:7C08 push    ax
MEMORY:7C09 pop     es
MEMORY:7C0A push    ax
MEMORY:7C0B pop     ds                              ; es ds = 0
MEMORY:7C0C cld
MEMORY:7C0D mov     si, 7C1Bh
MEMORY:7C10 mov     di, 61Bh
MEMORY:7C13 push    ax
MEMORY:7C14 push    di                              ; di = 61bh,jmp to 61b
MEMORY:7C15 mov     cx, 1E5h
MEMORY:7C18 rep movsb
MEMORY:7C1A retf
MEMORY:7C1B ; ---------------------------------------------------------------------------
MEMORY:7C1B mov     bp, 7BEh
MEMORY:7C1E mov     cl, 4						;cx记录着分区表的项数
MEMORY:7C20
MEMORY:7C20 loc_7C20:                               
MEMORY:7C20 cmp     [bp+0], ch  					;[bp+0]的一个字节跟ch进行比较,ch为0 0x80换算成1字节为-128 
MEMORY:7C23 jl      short loc_7C2E				;这里判断是否为活动分区,是的话跳转
MEMORY:7C25 jnz     short loc_7C3A				;分区若出现了其他值,跳到loc_7c3a执行错误处理
MEMORY:7C27 add     bp, 10h
MEMORY:7C2A loop    loc_7C20
MEMORY:7C2C int     18h                             ; TRANSFER TO ROM BASIC 错误处理
MEMORY:7C2C                                         
MEMORY:7C2E
MEMORY:7C2E loc_7C2E:        					;这个是判断有无其他分区的                       
MEMORY:7C2E mov     si, bp						;这时候说明找到了第一个活动分区
MEMORY:7C30
MEMORY:7C30 loc_7C30:                              
MEMORY:7C30 add     si, 10h						;si = bp + 0x10 si是一个循环器 每次加0x10
MEMORY:7C33 dec     cx							; cx--
MEMORY:7C34 jz      short loc_7C4F
MEMORY:7C36 cmp     [si], ch						;ch恒为0 若其它均为0,直到cx == 0 会跳到正常步骤里,否则继续输出那串字符串
MEMORY:7C38 jz      short loc_7C30				
MEMORY:7C3A
MEMORY:7C3A loc_7C3A:                               ; CODE XREF: MEMORY:7C25↑j
MEMORY:7C3A mov     al, byte_7B5					;byte_7B5是 0x2c
MEMORY:7C3D
MEMORY:7C3D loc_7C3D:                               
MEMORY:7C3D                                         
MEMORY:7C3D mov     ah, 7						;ah = 7
MEMORY:7C3F mov     si, ax
MEMORY:7C41
MEMORY:7C41 loc_7C41:                               
MEMORY:7C41 lodsb								;ds:si -> es:di si是0x072c 即invalid partition table di为0x800一片空白的区
MEMORY:7C42										;字节会装入ax里
MEMORY:7C42 loc_7C42:                               
MEMORY:7C42 cmp     al, 0						;会执行死循环
MEMORY:7C44 jz      short loc_7C42				;0x072c这里是一个ascii字符串
MEMORY:7C46 mov     bx, 7						
MEMORY:7C49 mov     ah, 0Eh						;ah = 0xE bl = 0x7 
MEMORY:7C4B int     10h                             ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE)
MEMORY:7C4B                                         ; AL = character, BH = display page (alpha modes)
MEMORY:7C4B                                         ; BL = foreground color (graphics modes)
MEMORY:7C4D jmp     short loc_7C41				;每次显示一个字符
MEMORY:7C4F ; ---------------------------------------------------------------------------
MEMORY:7C4F
MEMORY:7C4F loc_7C4F:                               ;假如分区没问题
MEMORY:7C4F mov     [bp+10h], cl
MEMORY:7C52 call    near ptr unk_7C9B			;检测是否有扩展样式
MEMORY:7C55 jnb     short loc_7C81				;
MEMORY:7C57
MEMORY:7C57 loc_7C57:                               
MEMORY:7C57 inc     byte ptr [bp+10h]			;LBA高两字节
MEMORY:7C5A cmp     byte ptr [bp+4], 0Bh			;offset为4 是文件类型 若是FAT32文件类型,则跳到loc_7C6B
MEMORY:7C5E jz      short loc_7C6B
MEMORY:7C60 cmp     byte ptr [bp+4], 0Ch			;同样也是FAT32分区
MEMORY:7C64 jz      short loc_7C6B
MEMORY:7C66 mov     al, byte_7B6					;Error loading operating system
MEMORY:7C69 jnz     short loc_7C3D				;如果不是FAT32,那么就说明文件类型有问题
MEMORY:7C6B
MEMORY:7C6B loc_7C6B:                               
MEMORY:7C6B                                         ; MEMORY:7C64↑j
MEMORY:7C6B add     byte ptr [bp+2], 6			;扇区号加6,可能这是FAT32的特点?
MEMORY:7C6F add     word ptr [bp+8], 6			;LBA起始值加6
MEMORY:7C73 adc     word ptr [bp+0Ah], 0				
MEMORY:7C77 call    near ptr unk_7C9B
MEMORY:7C7A jnb     short loc_7C81
MEMORY:7C7C mov     al, byte_7B6					;Error Loading operating system
MEMORY:7C7F jmp     short loc_7C3D
MEMORY:7C81 ; ---------------------------------------------------------------------------
MEMORY:7C81
MEMORY:7C81 loc_7C81:                               ;检测最后两字节
MEMORY:7C81                                         ; MEMORY:7C7A↑j
MEMORY:7C81 cmp     word_7DFE, 0AA55h			;检测最后两字节
MEMORY:7C87 jz      short loc_7C94				;如果有结束标志,则跳到loc_7C94
MEMORY:7C89 cmp     byte ptr [bp+10h], 0			;如果LBA高两位为0,则跳到Loc_7c57检测文件类型
MEMORY:7C8D jz      short loc_7C57
MEMORY:7C8F mov     al, byte_7B7					;Missing operating system
MEMORY:7C92 jmp     short loc_7C3D
MEMORY:7C94 ; ---------------------------------------------------------------------------
MEMORY:7C94
MEMORY:7C94 loc_7C94:                               ;这里已经将PBR装入0:7c00处了,现在要跳到那
MEMORY:7C94 mov     di, sp						;di = 0x7c00
MEMORY:7C96 push    ds
MEMORY:7C97 push    di							;retAddr = 0x7c00
MEMORY:7C98 mov     si, bp
MEMORY:7C9A retf
MEMORY:7C9A ; ---------------------------------------------------------------------------
MEMORY:7C9B
MEMORY:7C9B loc_7C9B:                              ;正常来说 检查分区表没问题会跳到这里
MEMORY:7C9B                                         
MEMORY:7C9B mov     di, 5						;di = 5
MEMORY:7C9E mov     dl, [bp+0]					;dl = 0x80
MEMORY:7CA1 mov     ah, 8
MEMORY:7CA3 int     13h                             ; 读取驱动器参数,AH = 0x8 DL是驱动器这里为0x80 0x80是硬盘的第一个
MEMORY:7CA3                                         	; cf = 0表示无错
MEMORY:7CA5 jb      short loc_7CCA				;若有错 则 cf = 1,会跳转到loc_7cca处,尝试将磁盘读入,进行磁盘复位
MEMORY:7CA7 mov     al, cl						
MEMORY:7CA9 and     al, 3Fh						;取出扇区数
MEMORY:7CAB cbw									;转换为字指令,AL的内容扩展到AH,形成AX中的字
MEMORY:7CAC mov     bl, dh						;bl存磁头数
MEMORY:7CAE mov     bh, ah						;bh高位清0
MEMORY:7CB0 inc     bx							;bx++
MEMORY:7CB1 mul     bx							;扇区*磁头数
MEMORY:7CB3 mov     dx, cx						;dx存放柱面和扇区相应地址
MEMORY:7CB5 xchg    dl, dh						;交换一下高低位,方便取出柱面
MEMORY:7CB7 mov     cl, 6						
MEMORY:7CB9 shr     dh, cl						
MEMORY:7CBB inc     dx							;dx里存放着柱面
MEMORY:7CBC mul     dx							;获得LBA地址
MEMORY:7CBE cmp     [bp+0Ah], dx					;[bp+0x0a]是LBA的高位地址,根据little-endian而言,offset为0xa是LBA的高位判断高位
MEMORY:7CC1 ja      short loc_7CE6				;若磁盘填入的大于实际的,则判断是否有扩展样式
MEMORY:7CC3 jb      short loc_7CCA				;若磁盘的LBA地址小于实际的LBA,将扇区读入内存
MEMORY:7CC5 cmp     [bp+8], ax					;[bp+8]是低位
MEMORY:7CC8 jnb     short loc_7CE6				;判断是否有扩展样式
MEMORY:7CCA	
MEMORY:7CCA loc_7CCA:                               
MEMORY:7CCA                                         
MEMORY:7CCA mov     ax, 201h
MEMORY:7CCD mov     bx, 7C00h
MEMORY:7CD0 mov     cx, [bp+2]
MEMORY:7CD3 mov     dx, [bp+0]					;al = 01,ah = 02 读取相应扇区到7c00处
MEMORY:7CD6 int     13h                             ; DISK - READ SECTORS INTO MEMORY
MEMORY:7CD6                                         ; AL = number of sectors to read, CH = track, CL = sector
MEMORY:7CD6                                         ; DH = head, DL = drive, ES:BX -> buffer to fill
MEMORY:7CD6                                         ; Return: CF set on error, AH = status, AL = number of sectors read
MEMORY:7CD8 jnb     short locret_7D2B			;若不出现错误,则返回
MEMORY:7CDA dec     di							;di 为尝试复位次数
MEMORY:7CDB jz      short locret_7D2B
MEMORY:7CDD xor     ah, ah
MEMORY:7CDF mov     dl, [bp+0]
MEMORY:7CE2 int     13h                             ; DISK - RESET DISK SYSTEM 磁盘系统复位 继续读取扇区
MEMORY:7CE2                                         ; DL = drive (if bit 7 is set both hard disks and floppy disks reset)
MEMORY:7CE4 jmp     short loc_7CCA
MEMORY:7CE6 ; ---------------------------------------------------------------------------
MEMORY:7CE6
MEMORY:7CE6 loc_7CE6:                               ; 说明现在可能有扩展样式
MEMORY:7CE6                                         ; MEMORY:7CC8↑j
MEMORY:7CE6 mov     dl, [bp+0]					;磁盘号
MEMORY:7CE9 pusha
MEMORY:7CEA mov     bx, 55AAh					
MEMORY:7CED mov     ah, 41h ; 'A'				
MEMORY:7CEF int     13h                             ; DISK - Check for INT 13h Extensions
MEMORY:7CEF                                         ; BX = 55AAh, DL = drive number
MEMORY:7CEF                                         ; Return: CF set if not supported
MEMORY:7CEF                                         ; AH = extensions version
MEMORY:7CEF                                         ; BX = AA55h
MEMORY:7CEF                                         ; CX = Interface support bit map
MEMORY:7CF1 jb      short loc_7D29				;表明现在样式存在问题。
MEMORY:7CF3 cmp     bx, 0AA55h
MEMORY:7CF7 jnz     short loc_7D29
MEMORY:7CF9 test    cl, 1
MEMORY:7CFC jz      short loc_7D29
MEMORY:7CFE popa
MEMORY:7CFF
MEMORY:7CFF loc_7CFF:                               ; 表明支持扩展样式,则扩展读
MEMORY:7CFF pusha
MEMORY:7D00 push    0
MEMORY:7D02 push    0
MEMORY:7D04 push    word ptr [bp+0Ah]			;push进去了 LBA地址
MEMORY:7D07 push    word ptr [bp+8]
MEMORY:7D0A push    0
MEMORY:7D0C push    7C00h
MEMORY:7D0F push    1
MEMORY:7D11 push    10h
MEMORY:7D13 mov     ah, 42h ; 'B'
MEMORY:7D15 mov     si, sp
MEMORY:7D17 int     13h                             ; DISK - IBM/MS Extension - EXTENDED READ (DL - drive, DS:SI - disk address packet)
MEMORY:7D19 popa
MEMORY:7D1A popa
MEMORY:7D1B jnb     short locret_7D2B
MEMORY:7D1D dec     di
MEMORY:7D1E jz      short locret_7D2B
MEMORY:7D20 xor     ah, ah
MEMORY:7D22 mov     dl, [bp+0]
MEMORY:7D25 int     13h                             ; DISK - RESET DISK SYSTEM
MEMORY:7D25                                         ; DL = drive (if bit 7 is set both hard disks and floppy disks reset)
MEMORY:7D27 jmp     short loc_7CFF
MEMORY:7D29 ; ---------------------------------------------------------------------------
MEMORY:7D29
MEMORY:7D29 loc_7D29:                               
MEMORY:7D29                                         
MEMORY:7D29 popa
MEMORY:7D2A stc									;令cf为置1,表示出现问题
MEMORY:7D2B
MEMORY:7D2B locret_7D2B:                            
MEMORY:7D2B                                         
MEMORY:7D2B retn

3.引导程序执行完后会交给活动分区的引导程序,是否为活动分区在分区表里会有体现,然后读取这个活动分区的第一扇区,也叫做卷引导记录,卷引导记录的作用j就是指出操作系统在磁盘的位置,让计算机可以加载操作系统到内存。当然如果操作系统在扩展分区的话,情况会复杂点,扩展分区会类似于一个结构体的模型,里面含有一个逻辑分区的地址和一个指向下一个扩展分区的指针,操作系统会不断的寻找,直到找到活动分区,不过这种情况很少见,一般操作系统安装到扩展分区,会进行下面的一种操作。这种情况下读取完MBR的440字节后,不把控制权交给某个分区,而是启动一个 启动管理器(boot manager),由用户决定启动哪一个操作系统。

4.Windows 2000/XP/Windows 2003系统下,内核版本为NT 5.X,会将控制权交给NTLDR这个启动管理器而Windows Vista、Windows 7、windows 8/8.1和windows 10内核版本为NT 6.X 中使用的新的启动管理器,称为bootmgr启动管理器。当然,在读取活动分区的第一扇区的时候,PBR会搜寻到启动管理器,对于bootmgr而言,它会会初始化一个最小的文件系统,为了方便读取c盘目录下的文件。然后读取\boot\bcd(bios config data启动配置参数)文件,类似于xp系统的boot.ini文件,然后假如存在多个操作系统的话,且等待时间不为0,那么会在显示器界面上显示操作系统的选项,供用户选择要启动的操作系统。bootmgr会去启动盘寻找C:\Windows\system32\winload.exe,之后就把控制权交给操作系统了。

Windowsn Vista等等启动过程类似于。注意的是bootmgr必须由版本最高的启动管理器负责管理所有的操作系统,启动管理器只具备向下兼容性、不具备向上兼容性。因此,启动管理器的版本必须严格按照高低先后的顺序正确排列,而不能像 Windows 的正式名称一样胡乱设置,否则便有可能无法正确配置多重操作系统共存。

默认情况下bcd配置文件在c:\Windows\System32目录中,程序名叫BCDEdit.exe

启动postgre 启动的英文_启动postgre_02


运行后会出现一些配置信息

启动postgre 启动的英文_初始化_03


指出了bootmgr的位置,和winload的位置

BIOS->MBR->DPT->PBR->bootmgr->\boot\bcd->选择相应操作系统->Winload.exe->内核加载->操作系统启动

对于xp系统而言,它的启动管理器是NTLDR,即NT LoaDeR。NTLDR被安装后,处理器会从实模式转为32 flat memory model(32位平坦模式,即段处理器无用,其偏移可以寻址到任意地址空间),接着NTLDR会初始化一个基础的文件系统(因为要从磁盘分区寻找某些中重要文件,如后文中提到的Ntoskrnl.exe文件,还有相当重要的注册表,注册表目录在C:\Windows\System32\Config,分多个文件存放),用以读取带有启动参数的文件boot.ini,然后假如有多个操作系统供选择,则等待操作系统的选择,接着就会将操作系统装载入内存(boot.ini会有参数给出操作系统在哪个分区,并且选择菜单由boot.ini给出,但是boot.ini并不是必需的,但却非常重要,如果丢失,那么默认从c盘开始启动操作系统了),然后,假若windows NT/windows 2000/windows XP/windows server 2003被选择(若不是NT操作系统,那么会将控制权交给bootsect.dos,例如非常古老的Windows9x系列),那么NTLDR会启动Ntdetect.com,来检测相应的硬件,存在于c盘目录下,主要是收集一些硬件信息,将硬件列表传入NTLDR,然后将硬件信息填入HKLM\HARDWARE。然后NTLDR会按一下顺序装载Ntoskrnl.exe(NT OS Kernel,仅仅装载,还没有初始化内核),Hal.dll(用于解决硬件的复杂性,硬件抽象层,同样也是为了硬件的可移植性质考虑的),kdcom.dll(内核调试器硬件扩展DLL),bootvid.dll(用于windows徽标和侧滚动栏和系统信息的集合(可以通过msinfo32.exe查看系统信息,也即是加载了HKEY_LOCAL_MACHINE/System注册表键)。Ntoskrnl(非常重要的内核初始化程序)存储了启动 logo 画面。也就是非常令人熟悉的那个过场动画。在进程中以System进程名来表示。如果在Windows注册表中定义了多个硬件配置,则此时将提示用户选择一个)。默认情况下,ControlSet001是系统真实的配置信息。可是为了避免序号混乱,windows启动时会从ControlSet001复制一份副本,作为操作系统当前的配置信息。也就是CurrentControlSet。我们对于计算机配置进行的改动都会写入到CurrentControlSet中,重新启动的过程中,CurrentControlSet的内容会覆盖掉ControlSet001中的内容,以保证两个键的内容相同。这样,CurrentControlSet002中的内容就是“最近一次成功配置的信息”。并且引人注意的是System/select子项,有几个整数键,Current表示选中的ControlSet控件组,Default下次采用的控件组,Failed表示失败后保存的控件组(此控件组在用户第一次调用“近期一次的正确配置”选项之前并不实际存在,因为我从来没用过,所以这里就是0.你失败的话会为你保留),LastKnownGood,最近一次正确的配置。Ntldr会根据载入的Select键的Default内容判断接下来需要载入哪个ControlSet注册表键(这些键会决定随后系统将载入哪些设备驱动或者启动哪些服务)。服务见下表。对于驱动而言,也是有必要的,主要的是在注册表中找start键的值为0和1的设备驱动 ,start键的值为0.1.2.3.4,数值越小,启动越早。1,比0稍晚一些,2是从登录界面出现的时候,3表示需要手动加载,4表示禁止加载。

HKLM\SYSTEM\CurrentControlSet\Services									//启动的服务
HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder	//启动服务的顺序

启动postgre 启动的英文_初始化_04


启动postgre 启动的英文_BIOS_05


启动postgre 启动的英文_操作系统_06

可以看下xp系统的boot.ini的具体的东西

启动postgre 启动的英文_初始化_07


选择操作系统的时间是30秒,操作系统所处于的位置是磁盘0的1扇区处的WINDOWS目录下

操作系统有两种选择,第二个是正常的我们的Windows操作系统,第一项是我为了用windbg调试而增加了/debug选项,并且用的串口是com2,波特率是115200bit。

启动postgre 启动的英文_底层_08


注册表中的System项:

启动postgre 启动的英文_初始化_09


之后便交给操作系统来处理了,具体的细节我还不太懂,待完全知晓后再补坑。