by QuickMouse(quickmouse@263.net)
GURB是一个很好的OS Loader,通常我们用它来引导Linux系统。一般来说,很多Linux Fans都会在自己的机器上装Linux系统,通常都是和windows共存的。不过用久一点,就开始使用VMWare,然后就把Linux的主战场搬进虚拟机了。再久一点,就会删除本机上的Linux系统,战场就只在虚拟机系统里了,呵呵,同时顺手会用fdisk/mbr把GRUB清除掉。我就是这么干的,或许你会说这不是一个标准的fans。
不过偶尔还是会要临时用一下非虚拟机的,也就是直接拿本机当作Linux机器用。一般来说都是拿一个kernel加一个小文件系统(initrd)临时boot起来用一用。最开始我是用win98 + loadlin来这么干的。不过久而久之发现win98渐渐不好找了,更麻烦的是loadlin似乎有一个很大的缺陷——当initrd大到一定程度的时候(例如压缩后6M字节),展开的文件系统不完整。这就不可忍受了。最开始以为是制作的小文件系统有缺陷,但慢慢发现用GRUB引导很正常,于是彻底抛弃了loadlin。想想也是很正常的事情,loadlin可是工作在DOS 16bit模式下的程序了。
于是,我开始思考在windows的机器上装GRUB。很多人也知道有GRUB for DOS,况且本来GRUB就可以安装在windows平台上,但是我的要求比较奇怪,就是不要GRUB去更改BR/MBR,也就是保持windows系统的干净。从windows 2000开始,boot.ini文件当中已经可以指定独立的文件作为BR(boot record)。就像在win98下面安装windows2000,但是不覆盖老的win98,则win2000启动的时候会有一个选项是previous windows system,从那里可以启动win98。仔细看一下实现方法,其实也就是把老的win98的BR(512B)单独存放为一个文件,由win2000的NTloader去再引导。
再来看看GRUB的大致引导过程。GRUB引导分为stage1和stage2两个部分。如果安装GRUB于MBR当中,则stage1直接写入了MBR,这样机器一启动就将控制权交给了GRUB的代码。如果GRUB安装在BR当中,则需要有另外一个OS loader读入BR当中的stage1后才能把控制权交给GRUB。接下来的步骤是stage1找到stage2并将控制权交给stage2。这里有两种方式,一种是直接方式,stage1根据记载的stage2所在硬盘的位置直接将stage2找到;另一种是间接方式,这需要stage1_5也就是通常说的stage1.5,通过stage1.5对文件系统的分析,通过路径方式找到stage2。stage1.5根据不同的文件系统有不同的版本,如果使用stage1.5,则该文件实际是嵌入到BR/MBR之后的若干扇区内的。也就是说,安装好的GRUB,实际发挥作用的是stage2,而stage1/1.5都直接内嵌到BR/MBR当中,存在于硬盘文件上的它们是不起作用的。因此,如果不希望改动windows系统的BR/MBR,则需要采用直接方式,找到stage2的位置,而stage1则使用直接的文件由NT loader去调用(此时stage1文件是起作用的!)。
GRUB引导的详细过程是(无stage1.5文件):
1、系统上电加载stage1(512B)
2、在stage1 0x40位置上得到boot drive信息(通常是0x80),0x41位置上得到force LBA标志
3、在0x42~43位置上(1 word)得到stage2加载到内存中的地址——0x8000,若使用stage1.5此值应当为0x2000
4、在0x44~47位置上得到stage2首扇区的位置
5、在0x48~49位置上得到stage2加载到内存当中的段地址,因为是实模式工作,所以参照第三条,应当分别是0x0800和0x0200
6、根据step 4得到的位置加载stage2首扇区到内存0x8000位置
7、从stage2首扇区末8字节得到stage2下一个blocklist位置,这8个字节的格式是:0~3,4字节表示的起始扇区位置,4~5,2字节表示的这个blocklist所包含的扇区数,6~7,2字节表示这个blocklist的数据应当放置到的内存段地址,第一个blocklist的段地址都是0x0820(0x8000 + 512B = 0x8200,然后取段地址)
8、在7的基础上再往前seek 8字节,若不为0,则表明数据为下一个blocklist,按照7的数据说明将它们加载到内存。重复本步骤直到所有的blocklist加载到内存
9、此时stage2已全部加载完毕,控制权可交给stage2
从详细过程可以看出:引导过程最关键的部分在于如何加载stage2。在没有stage1.5的情况下,stage2的加载完全依靠stage2首扇区末尾的blocklist标识来完成的。这一方面表明stage1/2的加载可以不依赖分区/文件系统格式,同时也说明stage2在没有stage1.5的情况下对文件所在磁盘位置的脆弱性。因此,如果希望在不改动BR/MBR的情况下安装GRUB,关键就在于如何去构造stage2首扇区后面的blocklist。
GRUB的shell界面有一个命令叫做blocklist,它就可以直接生成这样的数字。因此,我们首先可以将stage1、stage2复制到C盘上,然后将VMWare打开,在Linux虚拟机当中增加一个硬盘——整个物理硬盘。这样,在启动虚拟机的时候进入grub的shell界面,使用blocklist得到类似如下的结果:
grub> blocklist /grub/stage2
(hd1,0)33356+24,33382+232
以上面为例,stage2第一个扇区的起始位置是分区hd0,0的33356。需要注意的是,blocklist 8字节格式当中的起始扇区数是指绝对扇区数,因此对于windows系统的C盘来说,就是上面的33356再加上63(首分区从63扇区开始)。如果你想把stage2放到D盘或者更后面的话你就需要分析分区表了。再要注意的是,stage2首扇区末8字节(第一个blocklist)标识的应当是stage2第二个扇区开始的信息(stage2第一个扇区位置已经在stage1当中标明了),同时该blocklist当中列的扇区数不应计入首扇区,所以上面的例子当中blocklist应当分别是:
1、0x0000828c, 0x0017, 0x0820;(0x828c=33420=33356+63+1,0x17=24-1)
2、0x000082a5, 0x00e8, 0x0b00;(0x82a5=33445=33382+63,0xe8=232,上面0x8200 + (24-1) * 512 = 0xb000,取段地址0x0b00)
在Linux下用二进制编辑器(例如heme)将stage2文件的首扇区后16字节改为:
a5 82 00 00 e8 00 00 0b 8c 82 00 00 17 00 20 08
注意低位在前高位在后。不要使用windows下的编辑器,它们在保存时会改变文件的位置,使得blocklist的数值变得无效。
接着,可以在windows下打开stage1,在偏移量0x44~47位置上写入8b 82 00 00 (33356 + 63),同时检查偏移量0x42~43,0x48~49位置上的数值是否为0x8000和0x0800。最后修改boot.ini,加入:
C:\grub\stage1="GRUB"
即可。
此方法纯属技术技巧探讨,不一定有实际的意义,取决于各人的看法角度。
Trackback: http://tb.donews.net/TrackBack.aspx?PostId=385213