第 12 章
使用编程读取和更改 MBR
主引导记录 (MBR) 或主分区表 (MPT)
主引导记录 (MBR) 或有时称为主分区表 (MPT) 是通过执行 FDISK.EXE DOS 命令在硬盘上创建的。
MBR 包含一个小程序,用于从硬盘加载和运行活动(或启动)分区。主引导记录包含有关硬盘上所有四个主分区的信息,例如起始扇区、结束扇区、分区大小等。
MBR 位于绝对扇区 0,或者说位于柱面 0、磁头 0 和扇区 1,并且如果磁盘上有多个分区,那么在扩展分区的每个卷的开头都会有扩展主引导记录。
有关详细描述,请参阅本书前面讨论的“磁盘和操作系统的逻辑方法”章节。
主引导记录格式
我们可以将硬盘划分为多个逻辑驱动器,DOS通常为这些逻辑驱动器分配各自的驱动器号。一次只能将一个分区标记为活动(或启动)分区。

主引导记录在主分区表中最多有 4 个条目。但是,可以使用包含扩展分区表的 MBR 来获取扩展 MBR 的位置,该 MBR 具有与主分区表相同的格式,只是它不包含引导代码,并且 446 字节的空间通常保留用于引导代码并留空。
主引导记录的全部 512 个字节分解如下表所示:

所有扩展分区都必须存在于扩展分区项保留的空间中。仅打算使用两个扩展分区,第一个作为常规分区,第二个作为另一个扩展分区(如果存在)。
因此,使用一个主分区表,我们可以获取其旁边的另一个扩展主分区表的位置(如果有)。
分区表条目格式
MBR 中任意分区的分区表项格式如下表所示。任何 MBR 的每个分区条目都可以分解为以下具有特定含义的字节:

编写程序读取 MBR 分区表
下面是从 MBR 分区表读取所有四个分区条目的程序。该程序显示MBR分区表中记录的分区信息的所有参数。
程序编码如下:
/*读取MBR分区表的程序*/
# 包括 <bios.h>
/*从分区表读取分区条目的结构*/
结构剖面
{
无符号字符可启动; /* 部分的有效字节 */
unsigned char start_side ;/* 起始头 */
无符号整数 start_sec_cyl; /* 组合
起始扇区和
气缸数 */
无符号字符部分类型; /* 文件系统
指示字节*/
无符号字符 end_side; /* 结束标题 */
无符号整数 end_sec_cyl ; /* 组合
起始扇区和
气缸数 */
无符号长整型 part_beg; /* 相对扇区
数字 */
无符号长整数; /* 节长度
部门 */
};
/*读取MBR的结构*/
结构的一部分
{
无符号字符 master_boot[446]; /* IPL(初始
程序加载器)*/
结构分区 pt[4]; /* 分区表 */
int 最后两个; /* 魔法数字 */
};
结构 p 的一部分;
无效的 main()
{
clrscr();
/*读取第一个硬盘的第一个扇区*/
biosdisk ( 2, 0x80, 0, 0, 1, 1, &p ) ;
展示(); /*显示MBR信息
分区表 */
得到();
}
/*用于显示 MBR 分区表信息的函数*/
展示()
{
无符号整数 s_sec, s_trk, e_sec, e_trk, i, t1, t2;
符号类型[20],引导[5];
printf("\n\n部分初始加载位置
最终位置相对数量”);
printf("\n类型 侧 圆柱 扇区
侧圆柱体扇区扇区扇区\n");
对于(i = 0;i <= 3;i++)
{
如果(p.pt[i].bootable == 0x80)
strcpy ( boot, "是" ) ;
别的
strcpy (boot, "否" ) ;
开关(p.pt[i].parttype)
{
情况 0x00:
strcpy (类型,“未使用”);休息 ;
案例 0x1:
strcpy (类型,“FAT12”);休息 ;
案例 0x2:
strcpy (类型,“Xenix”);休息 ;
案例 0x3:
strcpy (类型, "Xenix:usr" ) ;休息 ;
案例 0x4:
strcpy (类型,“FAT16<32M”);休息 ;
案例 0x5:
strcpy (类型,“DOS-Ext。”);休息 ;
案例 0x6:
strcpy (类型,“FAT16>32M”);休息 ;
案例 0x7:
strcpy (类型,“NTFS”);休息 ;
案例 0x0b:
strcpy (类型,“FAT32”);休息 ;
案例 0x0c:
strcpy (类型,“FAT32-LBA”);休息 ;
情况 0x0d:
strcpy (类型,“VFAT16”);休息 ;
情况 0x0e:
strcpy (类型,“VFAT16-LBA”);休息 ;
情况 0x0f:
strcpy (类型,“VFAT EXT”);休息 ;
案例 0x17:
strcpy (类型, "HPFS" ) ;休息 ;
案例 0x81:
strcpy (类型,“旧 LINUX”);休息 ;
案例 0x82:
strcpy (类型,“LinuxSwap”);休息 ;
案例 0x83:
strcpy (类型,“LinuxNative”);休息 ;
案例 0x85:
strcpy (类型,“Linux Ext。”);休息 ;
默认:
strcpy (类型,“未知”);休息 ;
}
s_sec = ( p.pt[i].start_sec_cyl & 0x3f ) ; /* 开始
部门
分割 */
t1 = ( p.pt[i].start_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].start_sec_cyl & 0x00c0 ) << 2 ;
s_trk = t1 | t2; /* 起始气缸 */
复制代码/*结束扇区*/
t1 = ( p.pt[i].end_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].end_sec_cyl & 0x00c0 ) << 2 ;
e_trk = t1 | t2; /* 结束圆柱体 */
printf ( "\n%6s %3s", 类型, 引导 );
printf ( "%4d %6d %8d", p.pt[i].start_side, s_trk, s_sec ) ;
printf ( "%7d %6u %8u", p.pt[i].end_side,e_trk,e_sec ) ;
printf(" %10lu %10lu", p.pt[i].part_beg,
p.pt[i].plen );
}
返回0;
}
程序输出的信息显示如下:

编码注释:
分区结构用于读取MBR分区表中该分区的分区项的各种参数。结构部分用于读取MBR信息。
display()函数在屏幕上显示有关 MBR 分区表参数的信息。正如我们看到的程序输出,起始和结束磁柱和扇区号显示如下:
起始扇区 = 1
起始气缸 = 0
结束扇区 = 63
结束圆柱体 = 701
这些扇区和柱面号是根据两个字节的组合计算得出的。下表显示了这些数字的计算方法:

因此分区的起始 CHS= 0-0-1。
类似地,分区的结束磁柱和扇区号的编码如下表所示:

因此分区的结束 CHS = 701-254-63。
查找所有逻辑分区及其信息的程序
我们前面讨论的程序是从MBR的分区表中读取分区信息。但仅仅通过读取MBR,我们无法获得磁盘扩展分区中其他逻辑分区的信息。
我们已经讨论过,主引导记录在主分区表中的条目限制为四个。但是,可以借助包含扩展分区表的主引导记录来获取扩展主引导记录的位置,其格式与主分区表完全相同。
所有扩展分区都应存在于扩展分区项保留的空间内。仅两个扩展分区可供使用,第一个作为普通分区,第二个作为另一个扩展分区(如果存在)。
因此,在一个主分区表的帮助下,我们可以获得其旁边的另一个扩展主分区表的位置(如果存在)。
以下程序用于查找所有逻辑分区及其分区条目信息,从磁盘读取 MBR 和扩展 MBR 。该程序的编码如下:
/*程序读取磁盘中所有逻辑分区的参数*/
#include<dos.h>
har 缓冲区[512],report_par[20];
无符号驱动器号 = 0x80;
无符号长整型 star_sec[20],sec;
/*磁盘地址包格式的结构,供 readabsolutesectors 函数使用*/
磁盘地址包结构
{
字符数据包大小; /*数据包大小,一般为10H*/
字符保留; /* 保留 (0) */
int 块数; /* 要传输的块数 */
char far *缓冲区地址; /* 要传输的地址
缓冲 */
无符号长整型块号[2]; /* 起始绝对值
区块编号 */
};
空主()
{
int no_par,我;
clrscr();
无标准杆=0;
All_partition_information (star_sec,&no_par,&sec,buffer,
报告标准);
printf(" \n\n磁盘分区总数 = %d\n ",
无标准);
对于(i = 0;i <no_par;i ++)
{
printf("\n 分区的起始扇区号 %d =
%lu " , i+1, star_sec[i]);
}
打印(“ \ n”);
获取();
}
该程序的输出将显示类似如下内容:
分区 1 - FAT32
分区 2 - FAT32
分区 3 - FAT32
磁盘中的分区总数 = 3
分区 1 的起始扇区号 = 63
分区 2 的起始扇区号 = 11277693
分区 3 的起始扇区号 = 25623738
编码注释:
结构diskaddrpacket用于读取磁盘地址包格式,供 readabsolutesectors 函数使用。
函数All_partition_information( )用于从分区条目中找出所有分区的所有参数。
虽然在这个程序中我们只显示了磁盘中所有可用逻辑分区的文件系统和相关扇区信息,但是您也可以通过使用函数All_partition_information( )和一些 printf 来打印分区信息的其他参数的信息。
该函数的编码如下:
/*通过读取分区条目来查找所有逻辑分区信息的函数*/
All_partition_information(无符号长整型*star_sec,
无符号*no_par,
长*秒,字符*缓冲区,
无符号字符 *report_par )
{
无符号长整型fat_check;
无符号长整型*sectors_part;
静态长se_p;
int temp_var1,active_offset,active_pos=0,i,extended_pos=0,partloc1;
无符号长整型b_sec,se;
无符号字符active_par;
长相对秒;
长无扇区;
如果(*秒==0 || *秒==1)
s_p=0;
做{
se=*秒;
/*读取 *sec 指定的绝对扇区*/
读取绝对扇区(drive_num,*sec,1,缓冲区);
/* *****检查活动分区***** */
如果(*sec==se && *no_par==0)/*如果是主
分割 */
{
*sec=se=0;
对于(活动偏移=446;活动偏移<=494;活动偏移+=16)
{
活动标准=缓冲区[活动偏移];
if(active_par==0x80) /* 检查是否活动
分割 */
休息;
别的
活动位置++; /* 活动位置
分割 */
}
/*对于扩展分区*/
对于(活动偏移=450;活动偏移<=511;活动偏移+=16)
{
活动标准=缓冲区[活动偏移];
如果(活动标准杆==0x05 | 活动标准杆==0x0F)
/*检查扩展分区*/
休息;
别的
扩展_pos++; /*扩展的位置
分割 */
}
如果(active_pos==4)
活动位置=1;
如果(扩展位置==4)
扩展位置=1;
部分loc1=0x1C0+扩展位置*16;
}
别的
{
活动位置=0;
扩展位置=1;
部分loc1=0x1D0;
如果 (se_p!=0)
{
*sec=se=se_p; /*扩展的开始
分割 */
}
}
/*分区中的相关扇区*/
相对秒数=*(无符号长整型*)(缓冲区+454+活动位置*16);
/*分区中的扇区数*/
无扇区=*(长*)(缓冲区+458+活动位置*16);
/*识别文件系统指示字节*/
如果(缓冲区[0x1C2 + active_pos*16] == 0x04 ||
缓冲区[0x1C2 + active_pos*16]==0x05 ||
缓冲区[0x1C2 + active_pos*16]==0x06 ||
缓冲区[0x1C2 + active_pos*16]==0x0B ||
缓冲区[0x1C2+active_pos*16]==0x0C ||
缓冲区[0x1C2 + active_pos*16]==0x0E ||
缓冲区[0x1C2 + active_pos*16]==0x0F ||
缓冲区[0x1C2+active_pos*16]==0x07)
{
开关(缓冲区[0x1C2 + active_pos * 16])
{
/* 对于 NTFS 分区 */
情况 0x07: report_par[*no_par]='N';
printf("\n分区 -%d = NTFS",
*无标准杆+1);
休息;
/*对于 FAT32 分区*/
案例 0x0B:
情况 0x0C: report_par[*no_par]='3';
printf("\n分区 -%d = FAT32",
*无标准杆+1);
休息;
/*对于 FAT16 分区*/
案例 0x04:
案例 0x06:
情况 0x0E: report_par[*no_par]='1';
printf("\n分区 -%d = FAT16",
*无标准杆+1);
休息;
} // 开关结束
b_sec=*sec+相对秒;
扇区部分[*无扇区]=无扇区; /* 用于存储分区扇区数量的数组 */
} //if 条件结束
别的
{ /* 如果分区指示符不匹配 */
如果(*秒==0)
{ 无标准杆=0;
休息;
}
如果((fat_check!=0x3631)&&(fat_check!=0x3233))
b_sec=*sec=0;
}
如果((b_sec!=0)&&(sec!=0))
{
star_sec[*no_par]=b_sec;
(*无标准杆)++;
}
别的
休息;
/*检查扩展分区是否存在*/
如果(缓冲区[0x1C2 + extend_pos*16] == 0x05 ||
缓冲区[0x1C2+extended_pos*16]==0x0F)
{
temp_var1 =(无符号)缓冲区[partloc1];
*sec=temp_var1&0x003F; /* 部门
延长
分割 */
如果(*秒!= 0)
{
se_p=se+相对秒数+无扇区数;
*sec=se_p;
}
别的
{ *秒=-1;
休息;
}
} //结束 if 语句
别的
{
如果(*秒> 0)
*秒=-1;
休息;
}
}虽然(1); // 关闭 do–while 循环
/*检查扇区 0 上的其他非活动主分区*/
如果(*秒==0)
{
对于(i = 0;i < 4;i ++)
{
active_par=缓冲区[446+i*16];
/*识别文件系统指示字节*/
如果((缓冲区[0x1C2+i*16]==(char)0x06 ||
缓冲区[0x1C2+i*16]==(char)0x0B ||
缓冲区[0x1C2+i*16]==(char)0x0C ||
缓冲区[0x1C2+i*16]==(char)0x07 ||
缓冲区[0x1C2+i*16]==(char)0x0E ||
缓冲区[0x1C2+i*16]==(char)0x04) && active_par!=0x80)
{
开关(缓冲区[0x1C2 + active_pos * 16])
{
/*对于 NTFS 分区*/
情况 0x07: report_par[*no_par]='N';
printf("\n分区 -%d = NTFS",
*无标准杆+1);
休息;
/*对于 FAT32 分区*/
案例 0x0B:
情况 0x0C: report_par[*no_par]='3';
printf("\n分区 -%d = FAT32",
*无标准杆+1);
休息;
/*对于 FAT16 分区*/
案例 0x04:
案例 0x06:
情况 0x0E: report_par[*no_par]='1';
printf("\n分区 -%d = FAT16",
*无标准杆+1);
休息;
} // 开关结束
/*分区的相对扇区数*/
相对秒=*(长*)(缓冲区+454+i*16);
无扇区=*(long *)(缓冲区+458+i*16); /* 数量
部门
分割*/
扇区部分[*无扇区]=无扇区; /* 要存储的数组
数量
部门
分区 */
*sec=star_sec[*no_par]=relative_sec;
(*无标准杆)++;
}
} //for循环关闭(i=0;i<4;i++)
} //if(*sec==0) 循环关闭
返回;
}
编码注释:
该函数开始从 MBR 读取分区信息,然后根据需要读取扩展 MBR。函数 readabsolutesectors 读取 *sec 指定的绝对扇区。
sectors_part[*no_par] 是一个用于存储分区扇区数量的数组。分区号由*no_par 指定,从 0 开始。
no_sectors 是分区中的扇区数,relative_sec 是该分区的相对扇区数。
star_sec[*no_par] 是一个用于存储分区的起始扇区号的数组。分区号由*no_par 指定,从 0 开始。
star_cyl、star_hea 和 star_sec 是保存每个分区以 CHS 为单位的起始信息的数组。 star_cyl 存储有关起始柱面的信息,star_hea 存储有关起始磁头的信息,star_sec 存储有关分区起始扇区的信息。
有关 readabsolutesectors 函数的描述,请参阅本书前面给出的章节。
通过编程修改MBR
下面给出了示例程序来展示如何修改MBR 分区表条目的值。该程序修改MBR分区表的第二个分区表项的值。
该程序的代码如下:
/*修改MBR分区表项值的程序*/
# 包括 <bios.h>
/*从分区表读取分区条目的结构*/
结构分区
{
无符号字符可启动; /* 活动分区
字节 */
无符号字符起始端; /* 起始头 */
无符号整数 start_sec_cyl; /* 组合
起始扇区和
气缸数 */
无符号字符部分类型; /* 文件系统
指示字节 */
无符号字符 end_side; /* 结尾头 */
无符号整数 end_sec_cyl; /* 组合
起始扇区和
气缸数 */
无符号长整型 part_beg; /* 相关部门
数字 */
无符号长整数; /* 分区长度
部门 */
};
/*读取 MBR 的结构*/
结构部分
{
无符号字符 master_boot[446]; /* IPL(初始
程序加载器)*/
结构分区 pt[4]; /* 分区表 */
int 最后两个; /* 魔法数字 */
};
结构部分 p;
空主()
{
无符号整数t1,t2;
clrscr();
biosdisk ( 2, 0x80, 0, 0, 1, 1, &p ) ;
展示(); /*显示分区
表信息 */
获取();
/*假设我们要从 MBR 的分区表中修改第二个分区条目的分区信息,使用这些值*/
p.pt[1].可启动 = 0x80; /* 活动启动分区 */
p.pt[1].零件类型 = 0x7; /* NTFS 分区 */
p.pt[1].起始侧 = 0; /* 起始头 = 0 */
p.pt[1].结束端 = 31; /* 结束标题 == 31 */
p.pt[1].part_beg = 808416;/* 相对扇区 = 808416 */
p.pt[1].完整=405216; /* 分区中的扇区总数 = 405216 */
/*将新信息写入MBR *\
/*要将值写入MBR分区表,请取消注释下面的biosdisk函数*/
// biosdisk ( 3, 0x80, 0, 0, 1, 1, &p ) ;
展示(); /* 显示已改变
信息 */
得到();
}
编码注释:
上述程序是一个示例程序,展示如何更改MBR 分区表项的值。如果您想更改位于扩展分区中的此类逻辑分区的分区项值,则需要更改扩展 MBR 分区表中的值。
这里给出的更改分区表条目的值只是为了演示如何更改。切勿使用无效或不合逻辑的值来修改分区表。其结果是,整个部分可能变得无法访问。
结构段用于从分区表中读取分区记录,结构部分用于读取MBR。要更改分区表,请取消注释biosdisk()函数。
如果要更改分区的起始和结束值、扇区和柱面号,请按照本章开头讨论的读取和显示MBR 分区表的程序的注释中所述计算这些值。