硬盘的 0 磁道的第一个扇区称为 MBR,它的大小是 512 字节,而这个区域可以分为三个部分。第一部分为 pre-boot 区(预启动区),占 446 字节;第 二部分是分区表(Partition Table),占 64 个字节,硬盘中分区有多少以及每一分区的大小都记在其中。第三部分是 magic
number,占 2 个字节,固定为 55AA。
MBR 通常由 fdisk 等类似命令创建,系统上电自检之后,会调用 INT 19 中断 (CTRL+ALT+Delete 同样引发这个中断) 。INT 19 会查找活动分区(用第一个字符为 80 标记) 载入并跳转到 0000:7C00 开始执行程序。若未找到活动分区,则自动执行启动硬盘的 MBR 0000:7C00 地址程序。因为每个操作系统的启动区格式都不相同,所以这里的这段程序会查找操作系统的启动代码或者启动管理器例如 Grub 等。
以下是一个 MBR 的十六进制和 ASCII 码,后面会分段介绍具体内容。
OFFSET 0 1 2 3 4 5 6 7 8 9 A B C D E F *0123456789ABCDEF*
000000 fa33c08e d0bc007c 8bf45007 501ffbfc *.3.....|..P.P...*
000010 bf0006b9 0001f2a5 ea1d0600 00bebe07 *................*
000020 b304803c 80740e80 3c00751c 83c610fe *...<.t..<.u.....*
000030 cb75efcd 188b148b 4c028bee 83c610fe *.u......L.......*
000040 cb741a80 3c0074f4 be8b06ac 3c00740b *.t..<.t.....<.t.*
000050 56bb0700 b40ecd10 5eebf0eb febf0500 *V.......^.......*
000060 bb007cb8 010257cd 135f730c 33c0cd13 *..|...W.._s.3...*
000070 4f75edbe a306ebd3 bec206bf fe7d813d *Ou...........}.=*
000080 55aa75c7 8bf5ea00 7c000049 6e76616c *U.u.....|..Inval*
000090 69642070 61727469 74696f6e 20746162 *id partition tab*
0000a0 6c650045 72726f72 206c6f61 64696e67 *le.Error loading*
0000b0 206f7065 72617469 6e672073 79737465 * operating syste*
0000c0 6d004d69 7373696e 67206f70 65726174 *m.Missing operat*
0000d0 696e6720 73797374 656d0000 00000000 *ing system......*
0000e0 00000000 00000000 00000000 00000000 *................*
0000f0 TO 0001af SAME AS ABOVE
0001b0 00000000 00000000 00000000 00008001 *................*
0001c0 0100060d fef83e00 00000678 0d000000 *......>....x....*
0001d0 00000000 00000000 00000000 00000000 *................*
0001e0 00000000 00000000 00000000 00000000 *................*
0001f0 00000000 00000000 00000000 000055aa *..............U.*
上面代码反汇编的结果如下:1. 首先,代码会把自己复制到 0000:0600BEGIN: NOW AT 0000:7C00, RELOCATE
0000:7C00 FA CLI disable int's
0000:7C01 33C0 XOR AX,AX set stack seg to 0000
0000:7C03 8ED0 MOV SS,AX
0000:7C05 BC007C MOV SP,7C00 set stack ptr to 7c00
0000:7C08 8BF4 MOV SI,SP SI now 7c00
0000:7C0A 50 PUSH AX
0000:7C0B 07 POP ES ES now 0000:7c00
0000:7C0C 50 PUSH AX
0000:7C0D 1F POP DS DS now 0000:7c00
0000:7C0E FB STI allow int's
0000:7C0F FC CLD clear direction
0000:7C10 BF0006 MOV DI,0600 DI now 0600
0000:7C13 B90001 MOV CX,0100 move 256 words (512 bytes)
0000:7C16 F2 REPNZ move MBR from 0000:7c00
0000:7C17 A5 MOVSW to 0000:0600
0000:7C18 EA1D060000 JMP 0000:061D jmp to NEW_LOCATION
- 从分区表中查找活动分区
设置表指针
NEW_LOCATION: NOW AT 0000:0600
0000:061D BEBE07 MOV SI,07BE point to first table entry
0000:0620 B304 MOV BL,04 there are 4 table entries
第一遍查找
SEARCH_LOOP1: SEARCH FOR AN ACTIVE ENTRY
0000:0622 803C80 CMP BYTE PTR [SI],80 is this the active entry?
0000:0625 740E JZ FOUND_ACTIVE yes
0000:0627 803C00 CMP BYTE PTR [SI],00 is this an inactive entry?
0000:062A 751C JNZ NOT_ACTIVE no
0000:062C 83C610 ADD SI,+10 incr table ptr by 16
0000:062F FECB DEC BL decr count
0000:0631 75EF JNZ SEARCH_LOOP1 jmp if not end of table
0000:0633 CD18 INT 18 GO TO ROM BASIC
找到活动分区
FOUND_ACTIVE: FOUND THE ACTIVE ENTRY
0000:0635 8B14 MOV DX,[SI] set DH/DL for INT 13 call
0000:0637 8B4C02 MOV CX,[SI+02] set CH/CL for INT 13 call
0000:063A 8BEE MOV BP,SI save table ptr
第二遍查找以保证只有一个活动分区
SEARCH_LOOP2: MAKE SURE ONLY ONE ACTIVE ENTRY
0000:063C 83C610 ADD SI,+10 incr table ptr by 16
0000:063F FECB DEC BL decr count
0000:0641 741A JZ READ_BOOT jmp if end of table
0000:0643 803C00 CMP BYTE PTR [SI],00 is this an inactive entry?
0000:0646 74F4 JZ SEARCH_LOOP2 yes
无活动分区,则打印信息,挂起系统
NOT_ACTIVE: MORE THAN ONE ACTIVE ENTRY FOUND
0000:0648 BE8B06 MOV SI,068B display "Invld prttn tbl"
DISPLAY_MSG: DISPLAY MESSAGE LOOP
0000:064B AC LODSB get char of message
0000:064C 3C00 CMP AL,00 end of message
0000:064E 740B JZ HANG yes
0000:0650 56 PUSH SI save SI
0000:0651 BB0700 MOV BX,0007 screen attributes
0000:0654 B40E MOV AH,0E output 1 char of message
0000:0656 CD10 INT 10 to the display
0000:0658 5E POP SI restore SI
0000:0659 EBF0 JMP DISPLAY_MSG do it again
HANG: HANG THE SYSTEM LOOP
0000:065B EBFE JMP HANG sit and stay!
找到活动分区之后,会读取启动信息
READ_BOOT: READ ACTIVE PARITION BOOT RECORD 0000:065D BF0500 MOV DI,0005 INT 13 retry count INT13RTRY: INT 13 RETRY LOOP 0000:0660 BB007C MOV BX,7C00 0000:0663 B80102 MOV AX,0201 read 1 sector 0000:0666 57 PUSH DI save DI 0000:0667 CD13 INT 13 read sector into 0000:7c00 0000:0669 5F POP DI restore DI 0000:066A 730C JNB INT13OK jmp if no INT 13 0000:066C 33C0 XOR AX,AX call INT 13 and 0000:066E CD13 INT 13 do disk reset 0000:0670 4F DEC DI decr DI 0000:0671 75ED JNZ INT13RTRY if not zero, try again 0000:0673 BEA306 MOV SI,06A3 display "Errr ldng systm" 0000:0676 EBD3 JMP DISPLAY_MSG jmp to display loop INT13OK: INT 13 ERROR 0000:0678 BEC206 MOV SI,06C2 "missing op sys" 0000:067B BFFE7D MOV DI,7DFE point to signature 0000:067E 813D55AA CMP WORD PTR [DI],AA55 is signature correct? 0000:0682 75C7 JNZ DISPLAY_MSG no 0000:0684 8BF5 MOV SI,BP set SI 0000:0686 EA007C0000 JMP 0000:7C00 JUMP TO THE BOOT SECTOR WITH SI POINTING TO PART TABLE ENTRY
后面是一些显示字符
0000:0680 ........ ........ ......49 6e76616c * Inval* 0000:0690 69642070 61727469 74696f6e 20746162 *id partition tab* 0000:06a0 6c650045 72726f72 206c6f61 64696e67 *le.Error loading* 0000:06b0 206f7065 72617469 6e672073 79737465 * operating syste* 0000:06c0 6d004d69 7373696e 67206f70 65726174 *m.Missing operat* 0000:06d0 696e6720 73797374 656d00.. ........ *ing system. *
接着是一段没有用的空区域
0000:06d0 ........ ........ ......00 00000000 * .....* 0000:06e0 00000000 00000000 00000000 00000000 *................* 0000:06f0 00000000 00000000 00000000 00000000 *................* 0000:0700 00000000 00000000 00000000 00000000 *................* 0000:0710 00000000 00000000 00000000 00000000 *................* 0000:0720 00000000 00000000 00000000 00000000 *................* 0000:0730 00000000 00000000 00000000 00000000 *................* 0000:0740 00000000 00000000 00000000 00000000 *................* 0000:0750 00000000 00000000 00000000 00000000 *................* 0000:0760 00000000 00000000 00000000 00000000 *................* 0000:0770 00000000 00000000 00000000 00000000 *................* 0000:0780 00000000 00000000 00000000 00000000 *................* 0000:0790 00000000 00000000 00000000 00000000 *................* 0000:07a0 00000000 00000000 00000000 00000000 *................* 0000:07b0 00000000 00000000 00000000 0000.... *............ *
后面是分区表信息,启示位置为 0000:07be,每个分区表占 16 字节,这里只有一个主分区,开头的 80 表明这是一个启动分区。
0000:07b0 ........ ........ ........ ....8001 * ....* 0000:07c0 0100060d fef83e00 00000678 0d000000 *......>....x....* 0000:07d0 00000000 00000000 00000000 00000000 *................* 0000:07e0 00000000 00000000 00000000 00000000 *................* 0000:07f0 00000000 00000000 00000000 0000.... *............ *
最后是 55AA 标记
0000:07f0 ........ ........ ........ ....55aa *..............U.*
如果奇怪为什么用 55AA 这个值,写成二进制就清楚了:
0x55aa=(0101010110101010)2