在系统启动过程:通电源 -->BIOS 加电自检( 内存地址为 0ffff:0000 ) --> 将硬盘第一个扇区 (0头0道1扇区, 也就是Boot Sector) 读入内存地址 0000:7c00 处。
用winhex - 工具 - 打开磁盘 - 找到MBR - 内存的前0x200字节:
第一部分:446字节,引导代码。
第二部分:64字节,4个分区表,一个节区表4字节。
第三部分:2字节,结尾0x55,0xAA,结束的标志
分区表结构(一共0x10字节):
+0x0 BYTE State : 分区状态, 0 = 未激活, 0x80 = 激活 (注意此项)
+0x1 BYTE StartHead : 分区起始磁头号
+0x2 WORD StartSC : 分区起始扇区和柱面号, 底字节的低6位为扇区号 , 高2位为柱面号的第 9,10 位, 高字节为柱面号的低 8 位
+0x4 BYTE Type : 分区类型, 如 0x0B = FAT32, 0x83 = Linux 等,00 表示此项未用
+0x5 BYTE EndHead : 分区结束磁头号
+0x6 WORD EndSC : 分区结束扇区和柱面号, 定义同前
+0x8 DWORD Relative : 在线性寻址方式下的分区相对扇区地址 (对于基本分区即为绝对地址)
+0xC DWORD Sectors : 分区大小 (总扇区数)
分析开始(以xp的MBR分析):
一,MBR第一部分(拷贝代码0x7C00去0x600):
这一段反汇编,可以看到它的作用就是将7C00,也就是这里开始的200字节的代码拷贝到0x600处去,然后用retf修改IP为0x61E(因为这段复制的代码长度就是0x1E)去执行。
二,MBR第二部分(核心代码):
其实也就是接着第一部分后面的代码,只是说地址变了。
1.判断活动分区数是否为1个
这一部分是对MBR的四个分区表的第一个字节进行判断,每个节区表的第一个字节如果是0x80则表示是活动分区,如果是0x0则表示是非活动分区 ,然后对活动分区进行一个计数,四个分区判断完后,再判断活动分区是否只有一个,如果只有一个则继续continue_check其他项,否则则jmp去通过执行int 10h打印“Invalid partition table”这段字符串。
2.判断末尾标志是否为0x55,0xAA
通过int 13h的41h功能号去检验末尾是否为0x55,0xAA,如果末尾标志不是这两个,则跳向错误处理,在磁盘上寻找其他的启动介质 ,如果判断标志正确 ,就直接将0x1AD(0x7AD)偏移处作为DAP的地址:
DAP( 磁盘地址数据包 ) 结构:
struct DiskAddressPacket
{
BYTE PacketSize; // 数据包尺寸(16字节)
BYTE Reserved; // ==0
WORD BlockCount; // 要传输的数据块个数(以扇区为单位)
DWORD BufferAddr; // 传输缓冲地址(segment:offset)
QWORD BlockNum; // 磁盘起始绝对块地址
};
然后对DAP初始化值,传输缓冲地址赋值为0x7C00,磁盘起始绝对块地址赋值为活动分区结构的Relative部分。
3.如果第二部分判断标志0x55,0xAA错误:
通过int 13h的2号功能 , 读取磁盘的其他启动介质到0x7C00地址处,再对0x7C00 + 0x1FE的值判断是否为0x55,0xAA,如果不是,则jmp去通过执行int 10h打印“Missing operating system”这段字符串。如果int 13重新获取到的介质的标志是0x55 , 0xAA,则跳转到0x7C00处去执行活动扇区的代码。
4,如果2部分判断正确:
则直接跳转到0x7C00处去执行活动扇区的代码。
5.打印错误部分:
通过int 10h 来打印出si指向地址的信息。所以传过来的si地址是什么信息就会什么信息,如"Missing operating system"和
"Invalid partition table"。
MBR总共的分析就end了,总结一下全部过程:
1.检测4个分区中活动分区的个数,为1则继续检查,为其他值则跳转打印"Invalid partition table"。
2.检测MBR末尾两个字节是否为0x55,0xAA,检测正确则利用int 13h将活动扇区的代码复制到0x7C00处,到0x7C00处执行特定系统的启动程序。检测错误则再次利用int 13h将其他的启动介质赋值到0x7C00处,则检测0x7C00+0x1FE处的标志是否为0x55,0xAA,如果检测正确也跳转到0x7C00处执行,否则去打印"Missing operating system"。
所以MBR干的事就是从0x7C00将0x200个字节复制到0x600处去进行执行,然后再将特定系统的启动程序复制到0x7C00处,再跳转回0x7C00处去执行,然后启动系统。