Chinaunix首页 | 论坛 | 博客
  • 博客访问: 544598
  • 博文数量: 252
  • 博客积分: 6057
  • 博客等级: 准将
  • 技术积分: 1635
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-21 10:17
文章分类

全部博文(252)

文章存档

2013年(1)

2012年(1)

2011年(32)

2010年(212)

2009年(6)

分类:

2010-09-07 22:26:26

    基本上这是一篇比较入门级的文章。

    从这篇文章开始,准备用2----3篇的篇幅写一下主引导扇区的分析,先完成DOS下主引导扇区的分析,再分析一下grub下的主引导扇区,同时会简单介绍一下主引导扇区的利用。

    一直以来一直想写一篇关于PC机启动过程的文章,就是从机器上电到BIOS读取主引导扇区并把控制权交给主引导扇区中的引导程序开始,但每次想到这个问 题,都感到过程比较复杂,一是自己也不能彻彻底底地搞清楚,二是想不出能写出什么新意,所以就一直没有动笔。但是写这篇文章,是不得不涉及到PC机的启动 过程了,好在不是这篇文章的重点,而且只需泛泛说说就可以,所以估计不会出什么大问题。

1、PC机的启动过程

  • 系统上电时,CPU被复位,此时CPU工作在实模式下,CPU执行的第一条指令位于存储器地址0xffff0位置上,用段:偏移的方式表示为:ffff:0。
    我们在DOS下使用debug可以看到位于存储器中该位置上的指令。
     

    我们看到这条指令是:jmp F000:E05B。
    以 前我有一篇文章里详细介绍了PC机中的存储器地址结构(《关于A20 Gate》),从这篇文章中可以了解到,F000:E05B这个地址和前面的 F000:0都在BIOS中。为什么CPU执行的第一条指令是一条跳转指令呢?因为在实模式下,x86 CPU的最大寻址空间只有:0xFFFFF,而 FFFF:0这个位置离CPU可以寻址的最后一个字节只有16个字节的长度,16个字节当然什么都干不了,只好设置一条跳转指令到别的地方去执行指令,可 以肯定的是,在FFFF:0这个位置一定是一条跳转指令,但跳转到什么地址去,不同的BIOS是肯定不一样的。
  • 开机自检(POST Power-On Self Test)和显卡初始化。
    POST主要是检查一些重要的设备,比如内存和显卡,由于此时显卡还没有初始化,所以无法显示,当发现问题时,会发出长短不一的“滴滴”声,以区别不同的问题,不同厂家的BIOS,对叫声的定义也有区别。
    POST之后BIOS首先要做的就是进行显卡初始化,然后显示开机画面及相关信息。

  • 内存测试及设备安装。
    我 们开机的时候都会看到一个Memory Test的提示,这就是内存测试喽;内存测试后,开始安装一些标准设备,比如IDE、软驱、串口、并口等等,这些 标准设备安装后,会检测那些即插即用(Plug & Play)设备,每检测出一个,都会显示一行该设备的相关信息,BIOS会为这些设备分配 I/O通道、DMA以及中断。

  • 更新ESCD(Extended System Configuration Data
    设备初始化完成后,系统会显示系统中的所有PCI设备清单,然后我们经常会看到一个提示“Update ESCD… Success”,这就是更新ESCD喽,ESCD是BIOS用来与操作系统交换硬件配置信息的一种方法,这些信息存放在CMOS中,通常ESCD只在系统硬配置发生变化后才会更新,所以不是每次启动机器时都能看到提示。

  • 启动操作系统
    这就是我们要说的重点了,以上都完成后,BIOS会根据CMOS中设置的启动顺序启动相应的设备,我们这里不讨论其他设备,只是假定按顺序系统要启动硬盘了。

    但是这个时候,文件系统并没有建立,BIOS也不知道你的硬盘里存放的是什么东西,要启动的是什么系统,所以BIOS是无法直接启动操作系统的,加之一个 硬盘可以有多个分区,每个分区都有可能是一个不同的操作系统,BIOS也无从判断应该从哪个分区启动,所以对待硬盘,所有的BIOS都是读取硬盘的0磁 头、0柱面、1扇区的内容,然后把控制权交给这里面的MBR(Main Boot Record)。

2、主引导扇区的结构

要 注意,硬盘的磁头和柱面都是从0开始数的,但是扇区是从1开始数的,通常我们硬盘上的这个0磁头、0柱面、1扇区称作第一个物理扇区,一般我们把这个主引 导扇区分成三部分,第一部分叫MBR(Master Boot Rocord),主引导记录,这部分有446个字节,从 0--445(0x00--0x1BD);另一部分叫做DPT(Disk Partition Table),磁盘分区表,占这个扇区中的其余64个字 节,从446--509(0x1BE--0x1FD);第三部分是一个结束标志,占两个字节,从510--511(0x1FE--0x1FF),其正常内 容应该是0xAA55(0x55在低地址字节)。

主引导记录中一般有启动代码和数据,使用不同的boot loader,启动代码可能是不同的,这一系列文章将分析DOS下的启动代码和GRUB的启动代码,大家可以比较其中的差异。

不管用什么boot loader,其分区表的结构都是一样的,为了后面文章叙述的方便,我们在这里做一个简单的介绍,分区表可以容纳4个分区的信息,每个分区信息占16个字节,其结构如下:

 字节偏移

说明 

 0

引导标志。若值为80H表示活动分区,若值为00H表示非活动分区。

 1-3

本分区的起始磁头号、扇区号、柱面号。其中:磁头号--第1字节;扇区号--第2字节的低6位;柱面号—为第2字节高2位+第3字节8位

 4

分区类型符:
    00H——表示该分区未用(即没有指定);
    01h--FAT12基本分区
    04H--FAT16基本分区
    06H——big FAT16基本分区;

    0BH——FAT32基本分区;

    05H——扩展分区;

    07H——NTFS分区;

    0FH——(LBA模式)扩展分区(83H为Linux分区等)

 5-7

本分区的结束磁头号、扇区号、柱面号。其中:

    磁头号——第1字节;

    扇区号——第2字节的低6位;

    柱面号——第2字节的高2位+第3字节

 8-11

分区起始扇区数,指分区相对于记录该分区的分区表的扇区位置之差 (该分区表:LBA=0x0)

 12-15

本分区的总扇区数

 

3、如何获得主引导扇区的内容

一般我在debug下使用下面方法获得主引导扇区的内容:

实际上,我在debug下变了一段小程序,这段程序如下:

mov ax,0201 ;ah=2 读取扇区;al=读取一个扇区

mov bx,7c00 ;读取内容放到这个偏移地址上

mov cx,0001 ;0柱面(cl高2位+ch),1扇区(cl低6位)

mov dx,0080 ;0磁头(dh),硬盘c(dl:0-A盘,1-B盘,80h-硬盘c,81h-硬盘D)

int 13 ;BIOS调用

int 3 ;单步中断

执 行这段小程序后,主引导扇区的内容将被读出并放到偏移地址为0x7C00的地方,我们为什么要将主引导扇区读到0x7c00这个地方呢?因为BIOS在读 主引导扇区时,会将其固定读到0000:7C00这个地址上,我们也把主引导扇区读到偏移为7C00的位置,这样看起程序来和实际会更加接近。

如果你看上面的操作感觉莫名其妙,建议你去学习一下DOS下debug的使用,然后再回来看这篇文章。

 

4、DOS的主引导扇区中引导程序流程图

下 面这个流程图与后面我们公布的主引导扇区的代码相对应,使用DOS6.22下的分区软件FDISK进行的分区,这个流程图的主要用途是为了帮助理解后面的 代码,要明确的是,主引导扇区的职责是从硬盘上找到一个可以启动的分区,然后从这个分区上读出分区引导程序,最终把控制权交给分区引导程序,更详细的解释 和理解请看下一节。

 

(点击图片可看到大图)

5、DOS的主引导扇区中引导程序分析

在前面第3节,我们已经介绍了如何获得主引导扇区的内容,一下介绍的代码,完全是在DEBUG中反汇编出来的,实际反汇编出来的代码类似于下面的样子:

-u7c00 7c8a

20E8:7C00 FA            CLI

20E8:7C01 33C0          XOR   AX,AX

20E8:7C03 8ED0          MOV   SS,AX

20E8:7C05 BC007C        MOV   SP,7C00

20E8:7C08 8BF4          MOV   SI,SP

20E8:7C0A 50            PUSH  AX

20E8:7C0B 07            POP   ES

20E8:7C0C 50            PUSH  AX

20E8:7C0D 1F            POP   DS

20E8:7C0E FB            STI

20E8:7C0F FC            CLD

20E8:7C10 BF0006        MOV   DI,0600

20E8:7C13 B90001        MOV   CX,0100

20E8:7C16 F2            REPNZ

20E8:7C17 A5            MOVSW

20E8:7C18 EA1D060000    JMP   0000:061D

20E8:7C1D BEBE07        MOV   SI,07BE

...... ...... ......

第一列为地址,第二列为汇编码(opcode),第三列为反编译出来的汇编助记符,

为了阅读方便,本文公布的代码去掉了地址中的段地址部分,偏移地址也按照运行时的实际地址做了修改,汇编码(opcode)去掉,每行的最后加上了注释,全部代码如下:

7C00    CLI                      ;关中断

7C01    XOR AX,AX                ;AX=0

7C03    MOV SS,AX                ;SS=0

7C05    MOV SP,7C00              ;SP=7C00

7C08    MOV SI,SP                ;SI=7C00

7C0A    PUSH AX

7C0B    POP ES                   ;ES=0

7C0C    PUSH AX

7C0D    POP DS                   ;DS=0

7C0E    STI                      ;开中断

7C0F    CLD

7C10    MOV DI,0600

7C13    MOV CX,0100

7C16    REPNZ                    ;将主引导记录从0:7C00搬移到0:600

7C17    MOVSW

7C18    JMP 0000:061D            ;在新位置执行下一行语句

;主引导扇区已经被搬移到0:600的地方,并继续执行

061D    MOV SI,07BE              ;指向分区信息表

0620    MOV BL,04                ;最多4个分区信息表

0622    CMP BYTE PTR [SI],80     ;该分区是否为活动分区?

0625    JZ 0635                 ;该分区为活动分区,跳转

0627    CMP BYTE PTR [SI],00

062A    JNZ 0648

062C    ADD SI,+10               ;指向分区信息表的下一项

062F    DEC BL                   ;是否已经没有分区信息了?

0631    JNZ 0622                 ;还有分区信息,检查下一项

0633    INT 18                  ;没有分区信息了,没有可以启动的分区,启动ROM BASIC

;找到了活动分区,开始读取活动分区的分区引导程序

0635    MOV DX,[SI]              ;DH中为该分区磁头号,DL=80h

0637    MOV CX,[SI+02]           ;CL的低6位为扇区号,CH和CL的高2位为柱面号

063A    MOV BP,SI                ;将分区信息头指针暂存在BP中

063C    ADD SI,+10               ;指向分区信息表中下一项

063F    DEC BL                   ;已经没有分区信息了?

0641    JZ 065D                 ;没有分区信息了。

0643    CMP BYTE PTR [SI],00     ;该分区是非活动分区?

0646    JZ 063C                 ;该分区为非活动分区,检查下一分区

0648    MOV SI,068B              ;该分区不是非活动分区,

                                 ;则显示错误信息Invalid Partition Table

                                 ;此时,让SI指向字符串的开始,

                                 ;字符串的结束以00h标志;由于此时没有装载

                                 ;操作系统,所以只能使用BIOS调用

064B    LODSB                    ;将一个字符调入AL

064C    CMP AL,00                ;是否为字符串结束标志?

064E    JZ 065B                 ;字符串结束标志,字符串显示完毕

0650    PUSH SI                  ;下一个要显示的字符的指针

0651    MOV BX,0007              ;以黑底白字显示

0654    MOV AH,0E                ;显示一个字符

0656    INT 10                   ;BIOS调用

0658    POP SI                   ;下一个要显示的字符的指针

0659    JMP 064B                 ;显示下一个字符

065B    JMP 065B                 ;死循环

065D    MOV DI,0005              ;读扇区的操作最多重试5次

0660    MOV BX,7C00              ;将扇区读到偏移为7C00的地方

0663    MOV AX,0201              ;AH=02 读出扇区,AL=01 读1个扇区

0666    PUSH DI                  ;重试次数

0667    INT 13                   ;读扇区的BIOS调用

0669    POP DI                   ;重试次数

066A    JNB 0678                 ;读扇区成功

066C    XOR AX,AX                ;AX=0,AH=0表示复位磁盘

066E    INT 13                   ;复位磁盘

0670    DEC DI                   ;是否已达到最大重试次数?

0671    JNZ 0660                 ;还允许重试

0673    MOV SI,06A3              ;显示错误信息:Error loading operation system

0676    JMP 064B                 ;调用显示字符串程序并进入死循环

;读扇区成功,将控制权交给分区引导程序

0678    MOV SI,06C2              ;如果结束标志不是AA55h,

                                 ;则显示Missing operation system

067B    MOV DI,7DFE              ;指向结束标志

067E    CMP WORD PTR [DI],AA55   ;结束标志是否正常

0682    JNZ 064B                 ;结束标志不对,显示错误信息并进入死循环

0684    MOV SI,BP                ;SI指向分区引导扇区的开始

0686    JMP 0000:7C00            ;将控制权交给分区引导程序

-

0680                                   49 6E 76 61 6C              Inval

0690  69 64 20 70 61 72 74 69-74 69 6F 6E 20 74 61 62   id partition tab

06A0  6C 65 00 45 72 72 6F 72-20 6C 6F 61 64 69 6E 67   le.Error loading

06B0  20 6F 70 65 72 61 74 69-6E 67 20 73 79 73 74 65    operating syste

06C0  6D 00 4D 69 73 73 69 6E-67 20 6F 70 65 72 61 74   m.Missing operat

06D0  69 6E 67 20 73 79 73 74-65 6D 00 00 48 4B 9C 39   ing system..HK.9

06E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

06F0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0700  00 00 00 00 00 00 00 00-00 00 00                  ...........

-

0700  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0710  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0720  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0730  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0740  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0750  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0760  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0770  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

-d

0780  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

0790  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

07A0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

07B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 80 01   ................

07C0  01 00 06 7F BF 07 3F 00-00 00 C1 FB 3F 00 00 00   ......?.....?...

07D0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

07E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

07F0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 55 AA   ..............U.

-q

结合第4节的流程图,搞懂这个源程序应该不是问题,由于在运行主引导程序的时候,操作系统还没有装载,所以只能使用BIOS调用,下面就程序中用到的一些BIOS调用做一些说明:

int 13h ah=02     读扇区

入口: ah=02

al=要读的扇区数目

dl=驱动器代码,0、1、...用于识别软盘,80h、81h、...用于识别硬盘

dh=所读磁盘的磁头号

ch=柱面号的低8位。柱面号一共10位,其中高2位放在cl的bit6:7

cl=所读扇区的第一个扇区的扇区号。低6位为扇区号,高2位为柱面号的一部分。

es:bx=读出内容的起始存放地址

出口: es:bx指向读出内容

出错: CF=1表示有错误产生,错误码在AH中

int 13h ah=00     复位磁盘

入口: ah=00

dl=需复位的驱动器代码,0、1、...用于识别软盘,80h、81h、...用于识别硬盘

出口:无

int 10h ah=0eh    在屏幕上显示字符

入口: ah=0eh

al=要显示的字符

bl=显示字符的前景色。在通常字符方式下,0--表示黑色,1--表示白色

出口: 字符显示在屏幕上

下面我就程序中一些可能引起疑问的地方做一些说明。

为什么要把自身程序搬移到其他地方?

主引导扇区被读到0:7C00的位置是BIOS完成的,不可改变,把自身搬移到其它地方运行是为了空出0:7C00这块内存,后面好把分区引导扇区读到这里。

为什么还要把分区引导程序读到0:7C00的地方?

引 导扇区的引导记录是如何建立的呢?在DOS下是通过FORMAT命令建立的,除了硬盘可以启动以外,还有软盘也可以启动,软盘是不需要分区的,其启动扇区 非常类似于硬盘上的分区引导程序,但BIOS在读取软盘的启动扇区时,同样是读到0:7C00处执行,为了使软盘、硬盘的分区引导过程保持一致性,所以要 把分区引导程序读到0:7C00处执行,这样,软盘、硬盘的引导程序就可以做成一样的了。

主引导程序不允许分区中有一个以上的活动分区。

DOS的主引导程序在发现了第一个活动分区以后(标志为80h),要检查后面的所有分区标志,如果又发现有第二个活动分区,DOS将不知所措,并显示错误信息Invalid Partition Table,并进入死循环。

BIOS永远把启动硬盘称为80h

在使用BIOS调用int 13h操作硬盘时,需要知道硬盘的标识,以80h、81h...表示。

假 如你的机器上有两块硬盘,一块连接在IDE1的master上,另一块连接在IDE2的master上,然后我们在BIOS设置里设置为第二块硬盘启动, 不要以为IDE1上的硬盘为80h,而IDE2上的硬盘为81h,BIOS永远把启动盘识别为80h,把其他盘按顺序成为81h、82h...

说 这个问题是因为在地址为0635h处有一条指令,mov dx,[si],此时,si指向分区表中找到的活动分区信息的起始处,第一个字节一定是80h, 而第二个字节按照分区信息的结构说明(本文第2节有介绍)应该是磁头号,根据int 13h ah=02的功能调用,dh应该是磁头号,dl应该是磁盘表 示,mov dx,[si]会把dh装入磁头号,没有问题,但dl会永远装入80h,这就是上面说的,BIOS永远把启动盘成为80h。

关于BIOS的int 18h

程序中显示,如果主引导程序找不到活动分区,将调用int 18h,这个int 18h是什么呢?

在 最早的IBM PC中,在ROM中都安装有一款ROM BASIC的ROM芯片,可以做BAISIC的解释器用,就是说,即便没有操作系统,IBM PC 也会启动ROM BASIC,让你可以编一个BASIC的程序运行,或者逐条运行BASIC语句,当然那个BASIC不是现在说的VB,不过基本语法十分 相近,这个int 18h,就是用来启动ROM BASIC的,18h的中断向量实际指向了ROM BASIC的入口。

但实际上,好像是由于版权等因素,绝大多数的PC兼容机都没有安装ROM BASIC,所以实际上这个int 18h不会启动ROM BASIC,只会让机器死机。

现在的PC机大多对这个int 18h做了适当的处理,比如我的一台机器上,BIOS里专门做了int 18h的程序,当调用int 18h时,屏幕会显示“PRESS A KEY TO REBOOT”,然后按任意键会重新启动,不过分析这个代码不是本文的内容。

顺便说一句,在Vitual Box下安装的DOS 6.22中,如果调用int 18h,会出现致命错误然后死机,如果有读者曾经按照我介绍的方法在虚拟机上使用DOS,可以试一下,不会出现什么严重后果,如下图:

 

6、结束语

我 在baidu上搜了一下,其实网上关于DOS主引导程序的分析还是不少的,如果大家觉得我这篇和其他版本还是略有不同的话,而且也确实让你了解了一些以前 不知道的事情,我觉得目的也就达到了,还有一点想说明一下,不是每个硬盘的主引导扇区中都有主引导记录的,如果这块硬盘不需要启动,则不需要有主引导记 录,有个分区表就足够了。下一篇文章我们会分析一下GRUB的主引导扇区。

 


阅读(579) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~