2007年1月26日星期五

The GRUB MBR

http://mirror.href.com/thestarman/asm/mbr/GRUB.htm


The GRUB MBR
(being the GRand Unified Boot
Loader's
"stage1" Sector
)

A Disk Editor View and Comments on the Code
(as seen in Memory during Execution)
Plus some Lessons for Linux users
!



NOTE: Although the code used in this MBR is no mystery to anyone who has a search engine and a bit of Assembly knowledge (since it is all Open-Source code), I decided that many would still like to see a listing and comments on the Assembly instructions from GRUB's MBR (or "stage1" as it's called) in an effort to help computer users understand what happens when their Master Boot Record is replaced by the boot code from GRUB. Also note that this code can be used as either MBR code in a hard drive's MBR sector, or as a Linux install's Boot Sector! GRUB as a whole (all of its software working together) is actually a Boot Manager, capable of launching a variety of OS-types from its menu display; to which some distros such as Red Hat® have added their own splash screens. SuSE® (another Linux distribution company) has gone a step further, not only adding a splash screen patch, but some executable code which adds an animated count-down bar to their splash screen!

GRUB often replaces the original MBR code when someone 'tries out' Linux for the first time. In the past, this was only the case for Red Hat and a few lesser known distros. But now many others, such as SuSE 9.2 (SuSe has been defaulting to GRUB for some time) and the new Ubuntu (since 2004; Debian-based) distro, are also defaulting to GRUB*. This page will show you where "stage1" gets loaded into Memory and how the rest of GRUB gets executed from there. You can find documented sources by the authors of GRUB at most Linux archive sites. If you are still using Windows more often than Linux, make sure you grab GRUB's Manual in PDF or HTML form (rather than 'man' or 'info' pages); begin your search here: http://www.gnu.org/software/grub/ . In the source code, the source file for stage1 (GRUB's MBR or Boot Sector) is called: stage1.S. You may find the file stage1.h quite helpful as well; it contains locations/uses of bytes in the BPB area.

_______________________
* I must note here that Ubuntu's first distro known as Warty (October 2004) uses a version of GRUB which was never patched for splash screens; more importantly, their version of GRUB seems to have no way of installing into the MBR without also adding all of its "stage2" code immediately after it (overwriting many sectors of the first track)! Note that neither the Red Hat (since at least 8.0) nor SuSE (probably long before 9.1) installation programs will overwrite any sectors in the first track, except for the MBR sector. So, be aware that just because a distro uses GRUB, they certainly do not all follow the same installation methods! To most people, this wouldn't make any difference at all, but we consider it disturbing that a popular new Linux distro's install program (for Ubuntu) couldn't point out what it was really going to do (write at least 16 sectors to the first track) rather than simply stating it was writing GRUB to the MBR. [ So: Back-up the whole first track, if you are trying out any new Linux distros and you have some other boot manager or save data there as we do.]



A Disk Editor View of
GRUB's MBR/Boot Sector

Note: There are many different versions of GRUB being used in the real world. The one we're examining here contains the stage1 code for both versions 0.92 and 0.93 (12/08/2002); still found in most Linux installations. Also versions 0.94 (some: 1/25/2004 others: 5/13/2004; for example, as used in SuSE Linux 9.1 and seen in the Khexedit pics below) and 0.95 (6/13/2004). The MBR code for both of these versions is identical and basically the same as before (only one new instruction was added), but the offsets for the "GRUB " and error message strings have changed because of that! One should also note that Linux companies such as SuSE®, RedHat®, etc. may make changes of their own (SuSE® made a slight change in its 0.94 GRUB MBR code; see code comments below).

Like the Boot Records of an OS, the first three bytes could be called the Jump Instruction. But only the first two bytes are being used to form the actual JMP (Jump) instruction to the rest of the executable code; the third byte (90h) is just a NOP instruction ('No Op' do nothing). So the execution jumps over the 71 bytes highlighted in pink which can be thought of as a BIOS Parameter Block (or BPB); the GRUB documentation even uses that term.

Absolute sector 0 (cylinder 0, head 0, sector 1)
0 1 2 3 4 5 6 7 8 9 A B C D E F
0000 EB 48 90 D0 BC 00 7C FB 50 07 50 1F FC BE 1B 7C .H.....P.P....
0010 BF 1B 06 50 57 B9 E5 01 F3 A4 CB BE BE 07 B1 04 ...PW...........
0020 38 2C 7C 09 75 15 83 C6 10 E2 F5 CD 18 8B 14 8B 8,.u...........
0030 EE 83 C6 10 49 74 16 38 2C 74 F6 BE 10 07 03 02 ....It.8,t......
0040 80 00 00 80 DF 0A 93 01 00 08 FA EA 50 7C 00 00 ............P..
0050 31 C0 8E D8 8E D0 BC 00 20 FB A0 40 7C 3C FF 74 1....... ..@<.t
0060 02 88 C2 52 BE 76 7D E8 34 01 F6 C2 80 74 54 B4 ...R.v}.4....tT.
0070 41 BB AA 55 CD 13 5A 52 72 49 81 FB 55 AA 75 43 A..U..ZRrI..U.uC
0080 A0 41 7C 84 C0 75 05 83 E1 01 74 37 66 8B 4C 10 .A..u....t7f.L.
0090 BE 05 7C C6 44 FF 01 66 8B 1E 44 7C C7 04 10 00 ...D..f..D....
00A0 C7 44 02 01 00 66 89 5C 08 C7 44 06 00 70 66 31 .D...f.\..D..pf1
00B0 C0 89 44 04 66 89 44 0C B4 42 CD 13 72 05 BB 00 ..D.f.D..B..r...
00C0 70 EB 7D B4 08 CD 13 73 0A F6 C2 80 0F 84 F3 00 p.}....s........
00D0 E9 8D 00 BE 05 7C C6 44 FF 00 66 31 C0 88 F0 40 ......D..f1...@
00E0 66 89 44 04 31 D2 88 CA C1 E2 02 88 E8 88 F4 40 f.D.1..........@
00F0 89 44 08 31 C0 88 D0 C0 E8 02 66 89 04 66 A1 44 .D.1......f..f.D
0100 7C 66 31 D2 66 F7 34 88 54 0A 66 31 D2 66 F7 74 f1.f.4.T.f1.f.t
0110 04 88 54 0B 89 44 0C 3B 44 08 7D 3C 8A 54 0D C0 ..T..D.;D.}<.T..
0120 E2 06 8A 4C 0A FE C1 08 D1 8A 6C 0C 5A 8A 74 0B ...L......l.Z.t.
0130 BB 00 70 8E C3 31 DB B8 01 02 CD 13 72 2A 8C C3 ..p..1......r*..
0140 8E 06 48 7C 60 1E B9 00 01 8E DB 31 F6 31 FF FC ..H`......1.1..
0150 F3 A5 1F 61 FF 26 42 7C BE 7C 7D E8 40 00 EB 0E ...a.&B.}.@...
0160 BE 81 7D E8 38 00 EB 06 BE 8B 7D E8 30 00 BE 90 ..}.8.....}.0...
0170 7D E8 2A 00 EB FE 47 52 55 42 20 00 47 65 6F 6D }.*...GRUB .Geom
0180 00 48 61 72 64 20 44 69 73 6B 00 52 65 61 64 00 .Hard Disk.Read.
0190 20 45 72 72 6F 72 00 BB 01 00 B4 0E CD 10 AC 3C Error.........<
01A0 00 75 F4 C3 00 00 00 00 00 00 00 00 00 00 00 00 .u..............
01B0 00 00 00 00 00 00 00 00 A8 E1 A8 E1 00 00 80 01 ................
01C0 01 00 07 FE FF 6D 3F 00 00 00 AF 39 D7 00 00 00 .....m?....9....
01D0 C1 6E 0C FE FF FF EE 39 D7 00 BD 86 BB 00 00 FE .n.....9........
01E0 FF FF 83 FE FF FF AB C0 92 01 CD 2F 03 00 00 FE .........../....
01F0 FF FF 0F FE FF FF 78 F0 95 01 83 AF CC 00 55 AA ......x.......U.
0 1 2 3 4 5 6 7 8 9 A B C D E F

GRUB MBR example using version 0.92/0.93 code.

At offset 176h (for 0.92/0.93) we find the Zero-terminated string "GRUB ". Under versions 0.94/0.95, it should begin at offset 179h instead, but in the SuSE 9.1 MBR (with ver. 0.94) they added an extra instruction which shifted the "GRUB " string offset to 17Bh. [Note: If you have an earlier version of GRUB (before 0.92), you're likely to find this string in yet another location!]
The "GRUB " string is followed by a group of very brief Error Messages; each phrase in the Error Message section is zero-terminated as well. The bolded code inserted after the Error Message section is a subroutine used to display both the word "GRUB " and any Error Messages on the screen; it's called by the main body of code as needed.

Although GRUB is a Boot Manager, its stage1 code follows the structure of all MBRs by placing the standard four-entry Partition Table in its agreed upon location (offsets 01BEh through 01FDh) which is followed by the standard Word-sized signature ID of AA55h (remember hex Words for Intel x86 CPUs are stored in memory with the Lowest-byte first and the Highest-byte last; that's why you see: "55 AA" on the disk).

Finally, GRUB makes sure not to use any of the bytes between offsets 1B8h and 1BBh because they're used by Microsoft® Windows NT/2000/XP/2003 as the NT Drive Serial Number; which in our example above is the four-byte WORD E1A8E1A8h.



[For more comments on the code below, read the stage1.S source file for your version of the GRUB distribution.]


GRUB stage1 (MBR or Boot Sector) Code:


7C00 EB48 JMP 7C4A ; Jump over BPB data area
7C02 90 NOP ; to main body of code.

This BPB Data Area (BIOS Parameter Block) is filled with useful data for any program examining it as a normal Volume Boot Record and also important to the GRUB code which follows it:

        0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
7C00 D0 BC 00 7C FB 50 07 50 1F FC BE 1B 7C ....P.P....
7C10 BF 1B 06 50 57 B9 E5 01 F3 A4 CB BE BE 07 B1 04 ...PW...........
7C20 38 2C 7C 09 75 15 83 C6 10 E2 F5 CD 18 8B 14 8B 8,.u...........
7C30 EE 83 C6 10 49 74 16 38 2C 74 F6 BE 10 07 ....It.8,t....

7C3E 03 02 ..
7C40 80 00 00 80 DF 0A 93 01 00 08 ..........
  The BYTES in the BPB which are referenced in the code below are:

[7C05] -> BC 00 7C FB 50 07 50 1F FC BE 1B 7C BF 1B
("Disk Address Packet" for LBA mode.)

[7C40] -> 80 ("Boot Drive") NOTE: For those of you with multi-OS
booting
systems, if your Linux installation with GRUB's
remaining software (stage2, menu file, etc.) is located
somewhere other than on the Primary Master drive, this
value will be 81, 82, etc. depending upon which drive
that Linux OS's /boot/grub directory is located.

[7C41] -> 00 ("Force LBA mode byte")
[7C42] -> 00 80 (8000h) Memory location where GRUB stores the
next stage of the code to execute. See
7D54 below.
[7C44] -> Note: A very important location for anyone using GRUB!
This (4-byte) Quad-Word contains the location of GRUB's
stage2 file in sectors! You will always see the bytes
01 00 00 00 in this location whenever GRUB has been
installed in the first track (Sectors 1 ff.) of an HDD;
immediately following the GRUB MBR in Absolute Sector 0.
Example:
DF 0A 93 01 (1930ADFh) [ "stage2 Sector" -> 26,413,791 ]
[So, for this GRUB install, its stage2 file is located at
Absolute Sector 26413791. This value will of course vary
depending upon the physical location of the stage2 file!]

Another example: A /boot partition had so many errors that it had
to be re-formatted; after saving the files. When re-writing the
saved files back to /boot, as expected, stage2 got relocated. We
used a disk editor with a search function to find "Loading stage2"
string, then had to convert the Absolute Sector value 26,472,763
to: 193F13Bh and finally edit bytes in GRUB MBR to: 3B F1 93 01.

NOTE: For anyone who may need to do this some day, we ran into
yet another problem: The stage2 file itself, also contains a hard-
coded
value (two actually) pointing to other parts of GRUB code!
See: First sector of stage2 (values in red) starting at offset 1f0.
Though I was successful in editing them (I noted the difference
between the two values and our old stage2 "base location" then made
changes in the new stage2 values accordingly), but we still do not
know for sure what they're actually used for. It was enough to keep
GRUB from operating correctly until we made the changes though! So,
our new values in stage2 became: 55 F1 93 01 (offsets 1F0-1F3) and:
3C F1 93 01 (at offsets 1F8-1FB).


[7C48] -> 00 08 (800h) [Don't confuse this with the 8000 at 7C42.]

7C4A FA            CLI

; In the 0.94 and 0.95 code, an OR DL,80 instruction is inserted here
; when GRUB is installed in an MBR as a "workaround for buggy BIOSes.."
; which don't pass the boot drive byte correctly. If GRUB is installed
; as the Linux Boot Sector, a value of 00 is used instead of 80, which
; effectively makes it a NOP. This code (80 CA 80) causes all offsets
; after it to shift by 3 bytes, so all relative jumps were changed too!

7C4B EA507C0000 JMP 0000:7C50 ; Long Jump to the next instruction
; because some bogus BIOSes jump to
; 07C0:0000 instead of 0000:7C00.

7C50 31C0 XOR AX,AX
7C52 8ED8 MOV DS,AX
7C54 8ED0 MOV SS,AX
7C56 BC0020 MOV SP,2000
7C59 FB STI
7C5A A0407C MOV AL,[7C40] ; <<<<<<<< Boot Drive
7C5D 3CFF CMP AL,FF
7C5F 7402 JZ 7C63
7C61 88C2 MOV DL,AL
7C63 52 PUSH DX
7C64 BE767D MOV SI,7D76 ; --> "GRUB "
7C67 E83401 CALL 7D9E ; Display GRUB ID on screen.

7C6A F6C280 TEST DL,80
7C6D 7454 JZ 7CC3
7C6F B441 MOV AH,41 ; Function 41h of INT13
7C71 BBAA55 MOV BX,55AA
7C74 CD13 INT 13 ; Test for INT13 Extensions

7C76 5A POP DX
7C77 52 PUSH DX
7C78 7249 JB 7CC3
7C7A 81FB55AA CMP BX,AA55
7C7E 7543 JNZ 7CC3
7C80 A0417C MOV AL,[7C41] <<<< name="94suse">
; At this point, SuSE Linux 9.1 added a JS instruction to jump to
; the code at 7CC3. Why? Neither 0.94 nor 0.95 have this! There are
; already 3 jumps above (7C6D, 7C78, 7C7E) and 2 below (7C8A, 7CBC)
; to this same location. Is there a problem with "TEST AL,AL" here?

7C85 7505 JNZ 7C8C
7C87 83E101 AND CX,+01
7C8A 7437 JZ 7CC3


7C8C 668B4C10 * MOV ECX,[SI+10]

7C90 BE057C MOV SI,7C05 <<<<<<<<>7C44] <<<<<>Stage2 code
from the beginning of
the partition (the offset
is in number of sectors).

7C9C C7041000 MOV WORD PTR [SI],0010
7CA0 C744020100 MOV WORD PTR [SI+02],0001

7CA5 66895C08 * MOV [SI+08],EBX

7CA9 C744060070 MOV WORD PTR [SI+06],7000

7CAE 6631C0 * XOR EAX,EAX

7CB1 894404 MOV [SI+04],AX

7CB4 6689440C * MOV [SI+0C],EAX

7CB8 B442 MOV AH,42 ; Function 42h of INT13
7CBA CD13 INT 13 ; Extended Read (using
; Disk Address Packet).
7CBC 7205 JB 7CC3
7CBE BB0070 MOV BX,7000
7CC1 EB7D JMP 7D40
7CC3 B408 MOV AH,08 ; Function 08 of INT13
7CC5 CD13 INT 13 ; Get Drive Parameters

7CC7 730A JNB 7CD3
7CC9 F6C280 TEST DL,80

7CCC 0F84F300 * JZ 7DC3
7CD0 E98D00 JMP 7D60 ; There was an HDD Error!

7CD3 BE057C MOV SI,7C05 <<<<<< "Disk Packet?" 7CD6 C644FF00 MOV BYTE PTR [SI-01],00 7CDA 6631C0 * XOR EAX,EAX 7CDD 88F0 MOV AL,DH 7CDF 40 INC AX 7CE0 66894404 * MOV [SI+04],EAX 7CE4 31D2 XOR DX,DX 7CE6 88CA MOV DL,CL 7CE8 C1E202 * SHL DX,02 7CEB 88E8 MOV AL,CH 7CED 88F4 MOV AH,DH 7CEF 40 INC AX 7CF0 894408 MOV [SI+08],AX 7CF3 31C0 XOR AX,AX 7CF5 88D0 MOV AL,DL 7CF7 C0E802 * SHR AL,02 7CFA 668904 * MOV [SI],EAX 7CFD 66A1447C * MOV EAX,[7C44] <<<<<<>Stage2 code
from the beginning of
the partition (the offset
is in number of sectors).
7D01 6631D2 * XOR EDX,EDX
7D04 66F734 * DIV WORD PTR [SI]

7D07 88540A MOV [SI+0A],DL

7D0A 6631D2 * XOR EDX,EDX
7D0D 66F77404 * DIV WORD PTR [SI+04]

7D11 88540B MOV [SI+0B],DL
7D14 89440C MOV [SI+0C],AX
7D17 3B4408 CMP AX,[SI+08]
7D1A 7D3C JGE 7D58 ; There was a Geometry Error!
7D1C 8A540D MOV DL,[SI+0D]

7D1F C0E206 * SHL DL,06

7D22 8A4C0A MOV CL,[SI+0A]
7D25 FEC1 INC CL
7D27 08D1 OR CL,DL
7D29 8A6C0C MOV CH,[SI+0C]
7D2C 5A POP DX
7D2D 8A740B MOV DH,[SI+0B]
7D30 BB0070 MOV BX,7000
7D33 8EC3 MOV ES,BX
7D35 31DB XOR BX,BX
7D37 B80102 MOV AX,0201 ; Function 02 of INT13
7D3A CD13 INT 13 ; Read 1 sector into Memory

7D3C 722A JB 7D68 ; There was a Read Error!

7D3E 8CC3 MOV BX,ES
7D40 8E06487C MOV ES,[7C48] ; <<<<<<<<>0800 hex]
; Note: 800:0000 = 0000:8000
7D44 60 * PUSHA

7D45 1E PUSH DS
7D46 B90001 MOV CX,0100
7D49 8EDB MOV DS,BX
7D4B 31F6 XOR SI,SI
7D4D 31FF XOR DI,DI
7D4F FC CLD
7D50 F3A5 REP MOVSW

7D52 1F POP DS
7D53 61 * POPA

; This is where we jump to the next stage of the code which GRUB loaded
; from the HDD into Memory locations 0000:8000 hex and following:

7D54 FF26427C JMP [7C42] ; WORD <<< 8000 hex.



Section for Displaying Error Messages

7D58 BE7C7D        MOV     SI,7D7C         ; --> "Geom Error"
7D5B E84000 CALL 7D9E ; Display it on screen.
7D5E EB0E JMP 7D6E ; Finish it and 'lock-up'

7D60 BE817D MOV SI,7D81 ; --> "Hard Disk Error"
7D63 E83800 CALL 7D9E ; Display it on screen.
7D66 EB06 JMP 7D6E ; Finish it and 'lock-up'

7D68 BE8B7D MOV SI,7D8B ; --> "Read Error"
7D6B E83000 CALL 7D9E ; Display it on screen.
7D6E BE907D MOV SI,7D90 ; (For displaying " Error")
7D71 E82A00 CALL 7D9E ; Finish it and 'lock-up'
7D74 EBFE JMP 7D74 ; Locks-up execution in an
; infinite loop! You must
; reboot your computer!


Location of the GRUB ID String
and
Error Messages in Memory
(for versions 0.92/0.93)

                         6  7  8  9  A  B  C  D  E  F
7D76 47 52 55 42 20 00 47 65 6F 6D GRUB .Geom
7D80 00 48 61 72 64 20 44 69 73 6B 00 52 65 61 64 00 .Hard Disk.Read.
7D90 20 45 72 72 6F 72 00 Error.
0 1 2 3 4 5 6

For versions 0.94/0.95, these characters would begin at 7D7B instead.



Display Character Subroutine

7D97 BB0100        MOV     BX,0001
7D9A B40E MOV AH,0E ; Function 0Eh of INT10
7D9C CD10 INT 10 ; Display the character
7D9E AC LODSB
7D9F 3C00 CMP AL,00
7DA1 75F4 JNZ 7D97 ; Loop until finding a zero byte.
7DA3 C3 RET


Location of Sample
Partition Table in Memory

                                                  E  F
7DBE 80 01 ................
7DC0 01 00 07 FE FF 6D 3F 00 00 00 AF 39 D7 00 00 00 .....m?....9....
7DD0 C1 6E 0C FE FF FF EE 39 D7 00 BD 86 BB 00 00 FE .n.....9........
7DE0 FF FF 83 FE FF FF AB C0 92 01 CD 2F 03 00 00 FE .........../....
7DF0 FF FF 0F FE FF FF 78 F0 95 01 83 AF CC 00 55 AA ......x.......U.
0 1 2 3 4 5 6 7 8 9 A B C D E F



Interesting Commands for Linux Users

Some of the following Console commands require you to be root in order to use them, so unless you're already logged in as 'root' you'll need to use the Console command: su.

This command will copy the first drive's MBR sector into a new 512-byte file called MBRhda.bin (in the same directory you run the command from):

# dd if=/dev/hda of=MBRhda.bin bs=512 count=1

If the first drive is SCSI rather than IDE/ATA, replace any occurance of 'hda' with 'sda' in the command. [Placing this file on a couple diskettes and learning how to use a linux rescue system, such as good 'ole "tomsrtbt" or the SuSE 9.1 install CD's "rescue" boot, makes it just as easy to restore the MBR sector with the command: # dd if=MBRhda.bin of=/dev/hda bs=512 count=1 ].

If GRUB is in your MBR, then MBRhda.bin will be a copy of GRUB's stage1 code along with that drive's Partition Table. As a quick check, you could use the command: # cat MBRhda.bin grep "GRUB" to see if the word "GRUB" exists anywhere in the file. If linux outputs: "Binary file (standard input) matches" on the next line, then chances are it's GRUB's stage1. To know for sure, you'd have to check the code against a known source. You can view the file similar to the Disk Editor View of GRUB above, by running the commmand:

# hexdump -Cv MBRhda.bin less

which allows you to scroll up or down while viewing it; just press the "q" key to quit. The letter "v" in "-Cv" means "show every byte" in the file.

To find out where GRUB's stage2 code is located, you need to write down the 4-byte (Quad) Word located at offsets 0044h-0047h in the file, or open MBRhda.bin in that nice GUI Hex Editor, KHexEdit from KDE (which will do the necessary decimal conversion for you too):


Note: highlighted background colors added.

If we didn't have KHexEdit, we'd use a calculator program to convert the Quad Hex word 01934335h to 26428213 decimal. NOTE that your own GRUB MBR will have a different number here! Once again, this is the Absolute Sector location where GRUB's stage2 code begins. If you ever move a Linux partition with the /boot/grub directory, such as resizing it or just replacing the files in the grub directory, GRUB cannot function correctly until you change these four bytes to point to the new location of its stage2 code! To confirm that you've got the correct number, substitute your own sector number for the "skip" value in this command:

# dd if=/dev/hda of=stage2-1s.bin bs=512 skip=26428213 count=1

This means that 'dd' will skip 26,428,213 512-byte sectors (bs=512) from the beginning of the hard drive, before it copies the next sector into the new file "stage2-1s.bin" (just the first sector [count=1]; the whole stage2 file on our system would require 259 sectors). If you believe that you've copied the correct sector on your own HDD, then take a look at it with this familiar command:

# hexdump -C stage2-1s.bin less

You can view our example hexdump screen output here.



Last Update: 29 January 2006. (29.01.2006)



You can write to me using this: online reply form. (It opens in a new window.)

MBR and Boot Records Index

The Starman's Realm Index Page

没有评论: