分类:
2010-09-07 22:26:26
从这篇文章开始,准备用2----3篇的篇幅写一下主引导扇区的分析,先完成DOS下主引导扇区的分析,再分析一下grub下的主引导扇区,同时会简单介绍一下主引导扇区的利用。
一直以来一直想写一篇关于PC机启动过程的文章,就是从机器上电到BIOS读取主引导扇区并把控制权交给主引导扇区中的引导程序开始,但每次想到这个问 题,都感到过程比较复杂,一是自己也不能彻彻底底地搞清楚,二是想不出能写出什么新意,所以就一直没有动笔。但是写这篇文章,是不得不涉及到PC机的启动 过程了,好在不是这篇文章的重点,而且只需泛泛说说就可以,所以估计不会出什么大问题。
1、PC机的启动过程
开机自检(POST Power-On Self Test)和显卡初始化。
POST主要是检查一些重要的设备,比如内存和显卡,由于此时显卡还没有初始化,所以无法显示,当发现问题时,会发出长短不一的“滴滴”声,以区别不同的问题,不同厂家的BIOS,对叫声的定义也有区别。
POST之后BIOS首先要做的就是进行显卡初始化,然后显示开机画面及相关信息。
内存测试及设备安装。
我
们开机的时候都会看到一个Memory Test的提示,这就是内存测试喽;内存测试后,开始安装一些标准设备,比如IDE、软驱、串口、并口等等,这些
标准设备安装后,会检测那些即插即用(Plug & Play)设备,每检测出一个,都会显示一行该设备的相关信息,BIOS会为这些设备分配
I/O通道、DMA以及中断。
更新ESCD(Extended System Configuration Da
设备初始化完成后,系统会显示系统中的所有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 |
分区类型符: 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=1 读取一个扇区
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调用做一些说明:
l 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中
l int 13h ah=00 复位磁盘
入口: ah=00
dl=需复位的驱动器代码,0、1、...用于识别软盘,80h、81h、...用于识别硬盘
出口:无
l int 10h ah=0eh 在屏幕上显示字符
入口: ah=0eh
al=要显示的字符
bl=显示字符的前景色。在通常字符方式下,0--表示黑色,1--表示白色
出口: 字符显示在屏幕上
下面我就程序中一些可能引起疑问的地方做一些说明。
l 为什么要把自身程序搬移到其他地方?
主引导扇区被读到0:7C00的位置是BIOS完成的,不可改变,把自身搬移到其它地方运行是为了空出0:7C00这块内存,后面好把分区引导扇区读到这里。
l 为什么还要把分区引导程序读到0:7C00的地方?
引 导扇区的引导记录是如何建立的呢?在DOS下是通过FORMAT命令建立的,除了硬盘可以启动以外,还有软盘也可以启动,软盘是不需要分区的,其启动扇区 非常类似于硬盘上的分区引导程序,但BIOS在读取软盘的启动扇区时,同样是读到0:7C00处执行,为了使软盘、硬盘的分区引导过程保持一致性,所以要 把分区引导程序读到0:7C00处执行,这样,软盘、硬盘的引导程序就可以做成一样的了。
l 主引导程序不允许分区中有一个以上的活动分区。
DOS的主引导程序在发现了第一个活动分区以后(标志为80h),要检查后面的所有分区标志,如果又发现有第二个活动分区,DOS将不知所措,并显示错误信息Invalid Partition Table,并进入死循环。
l 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。
l 关于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的主引导扇区。