Chapter – 12
通过编程读取和修改 MBR
主引导记录 (MBR) 或主分区表 (MPT)
主引导记录 (MBR) 或有时称为主分区表 (MPT),是通过执行 DOS 的 FDISK.EXE 命令在硬盘驱动器上创建的。
MBR 包含一个小程序,用于从硬盘驱动器加载和启动活动(或可引导)分区。主引导记录包含有关硬盘驱动器上所有四个主分区的信息,例如起始扇区、结束扇区、分区大小等。
MBR 位于绝对扇区 0,或者我们可以说位于柱面 0、磁头 0 和扇区 1,如果磁盘中存在多个分区,则扩展主引导记录位于每个分区的开头扩展分区卷。
有关详细说明,请参阅本书前面讨论的“磁盘和操作系统的逻辑方法”一章。
主引导记录格式
我们可以将硬盘驱动器划分为几个逻辑驱动器,这些驱动器通常由 DOS 分配它们自己的驱动器号)。一次只能将一个分区标记为活动(或可引导)分区。
主引导记录在主分区表中有四个条目的限制。但是扩展主引导记录的位置可以通过包含扩展分区表的主引导记录来获得,其格式与主分区表完全相同,只是没有引导代码,这个 446 字节的空间通常是为引导代码保留并保持为空。
主引导记录的所有 512Bytes 都被破坏了,如下表所示:
所有扩展分区都应该存在于扩展分区条目保留的空间内。仅打算使用两个扩展分区,第一个作为普通分区,第二个作为另一个扩展分区(如果存在)。
因此,在一个主分区表的帮助下,我们可以获得另一个扩展主分区表的位置,如果存在的话。
分区表项格式
MBR 中任何一个 Partition 的分区表项的格式已在下表中给出。任何 MBR 的每个 Partition Entry 都可以分解为以下字节,并具有其特定含义:
编写程序读取MBR的分区表
下面给出了从 MBR 的分区表中读取所有四个分区条目的程序。程序显示分区信息的所有参数,写入MBR的分区表中。
程序编码如下:
/* 读取MBR分区表的程序 */
# include <bios.h>
/* 从分区表中读取分区条目的结构 */
struct partition
{
unsigned char bootable ; /* Active Partition Byte */
unsigned char start_side ;/* Starting Head */
unsigned int start_sec_cyl ; /* combination of
Starting sector and
cylinder number */
unsigned char parttype ; /* File system
Indicator Byte */
unsigned char end_side ; /* Ending Head */
unsigned int end_sec_cyl ; /* combination of
Starting sector and
cylinder number */
unsigned long part_beg ; /* Relative Sector
Number */
unsigned long plen ; /* Partition length in
sectors */
} ;
/* 读取 MBR 的结构 */
struct part
{
unsigned char master_boot[446] ; /* IPL (Initial
Program Loader)*/
struct partition pt[4] ; /* Partition table */
int lasttwo ; /* Magic Number */
} ;
struct part p ;
void main()
{
clrscr();
/* 读取第一块硬盘的第一扇区 */
biosdisk ( 2, 0x80, 0, 0, 1, 1, &p ) ;
display(); /* Display the information of MBR
Partition Table */
getch();
}
/* MBR分区表信息显示功能 */
display()
{
unsigned int s_sec, s_trk, e_sec, e_trk, i, t1, t2 ;
char type[20], boot[5] ;
printf("\n\nPart. Boot Starting location
Ending Location Relative Number of");
printf("\nType Side Cylinder Sector
Side Cylinder Sector Sectors Sectors\n");
for ( i = 0 ; i <= 3 ; i++ )
{
if ( p.pt[i].bootable == 0x80 )
strcpy ( boot, "Yes" ) ;
else
strcpy ( boot, "No" ) ;
switch ( p.pt[i].parttype )
{
case 0x00 :
strcpy ( type, "Unused" ) ; break ;
case 0x1 :
strcpy ( type, "FAT12" ) ; break ;
case 0x2 :
strcpy ( type, "Xenix" ) ; break ;
case 0x3 :
strcpy ( type, "Xenix:usr" ) ; break ;
case 0x4 :
strcpy ( type, "FAT16<32M" ) ; break ;
case 0x5 :
strcpy ( type, "DOS-Ext." ) ; break ;
case 0x6 :
strcpy ( type, "FAT16>32M" ) ; break ;
case 0x7 :
strcpy ( type, "NTFS" ) ; break ;
case 0x0b :
strcpy ( type, "FAT32" ) ; break ;
case 0x0c :
strcpy ( type, "FAT32-LBA" ) ; break ;
case 0x0d :
strcpy ( type, "VFAT16" ) ; break ;
case 0x0e :
strcpy ( type, "VFAT16-LBA" ) ; break ;
case 0x0f :
strcpy ( type, "VFAT EXT" ) ; break ;
case 0x17 :
strcpy ( type, "HPFS" ) ; break ;
case 0x81 :
strcpy ( type, "Old LINUX" ) ; break ;
case 0x82 :
strcpy ( type, "LinuxSwap" ) ; break ;
case 0x83 :
strcpy ( type, "LinuxNative" ) ; break ;
case 0x85 :
strcpy ( type, "Linux Ext." ) ; break ;
default :
strcpy ( type, "Unknown" ) ; break ;
}
s_sec = ( p.pt[i].start_sec_cyl & 0x3f ) ; /* starting
Sector of the
partition */
t1 = ( p.pt[i].start_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].start_sec_cyl & 0x00c0 ) << 2 ;
s_trk = t1 | t2 ; /* Starting Cylinder */
e_sec = ( p.pt[i].end_sec_cyl & 0x3f ) ; /*Ending Sector */
t1 = ( p.pt[i].end_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].end_sec_cyl & 0x00c0 ) << 2 ;
e_trk = t1 | t2 ; /* ending Cylinder */
printf ( "\n%6s %3s", type, boot ) ;
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 ) ;
}
return 0;
}
程序输出给出的信息显示如下:
编码评论:
结构partition用于读取MBR分区表中partition的partition entry的各种参数。结构部分用于读取MBR信息。
display()函数在屏幕上显示MBR Partition Table参数的信息。正如我们看到程序的输出,开始和结束柱面和扇区号显示如下:
Starting Sector = 1
Starting Cylinder = 0
Ending Sector = 63
Ending Cylinder = 701
这些扇区号和柱面号是根据两个字节的组合计算得出的。下表显示了如何计算这些数字:
因此分区的起始 C-H-S = 0-0-1。
类似地,Ending Cylinder 的 Encoding 和分区的 Sector 编号已在下表中给出:
因此分区的结尾 C-H-S = 701-254-63。
查找所有逻辑分区及其信息的程序
我们前面讨论的程序是从MBR的分区表中读取分区信息。但是仅仅通过读取MBR,我们无法获取磁盘扩展分区中其他逻辑分区的信息。
我们已经讨论过主引导记录在主分区表中有四个条目的限制。但是扩展主引导记录的位置可以通过包含扩展分区表的主引导记录来获得,其格式与主分区表的格式完全相同。
所有扩展分区都应该存在于扩展分区条目保留的空间内。只有两个扩展分区被使用,第一个作为普通分区,第二个作为另一个扩展分区(如果存在)。
因此,在一个主分区表的帮助下,我们可以获取旁边另一个扩展主分区表的位置(如果存在)。
以下程序用于查找所有逻辑分区及其分区条目信息,从磁盘中读取 MBR 和扩展 MBR。该程序的编码如下:
/* 程序读取磁盘中存在的所有逻辑分区的参数 */
#include<dos.h>
har buffer[512], report_par[20];
unsigned drive_num =0x80;
unsigned long star_sec[20], sec;
/* 磁盘地址数据包格式的结构,由 readabsolutesectors 函数使用 */
struct diskaddrpacket
{
char packetsize ; /* Size of Packet, generally 10H */
char reserved ; /* Reserved (0) */
int blockcount ; /* Number of Blocks to Transfer */
char far *bufferaddress ; /* address to Transfer
Buffer */
unsigned long blocknumber[2] ; /* Starting Absolute
Block Number */
} ;
void main()
{
int no_par,i;
clrscr();
no_par = 0;
All_partition_information (star_sec,&no_par, &sec, buffer,
report_par);
printf(" \n\n Total Partitions in Disk = %d\n ",
no_par);
for(i=0;i<no_par;i++)
{
printf("\n Starting Sector Number of Partition %d =
%lu " , i+1, star_sec[i]);
}
printf("\n");
getch();
}
程序的输出将显示如下:
Partition 1 - FAT32
Partition 2 - FAT32
Partition 3 - FAT32
Total Partitions in Disk = 3
Starting Sector Number of Partition 1 = 63
Starting Sector Number of Partition 2 = 11277693
Starting Sector Number of Partition 3 = 25623738
编码评论:
结构体diskaddrpacket用于读取磁盘地址包格式,供readabsolutesectors函数使用。
函数All_partition_information( )用于从分区表项中找出所有分区的所有参数。
虽然在这个程序中,我们只显示了磁盘中所有可用逻辑分区的文件系统和相关扇区信息,但您也可以使用函数All_partition_information()打印分区信息的其他参数信息 还有一些 printf。
函数编码如下:
/* 查找所有逻辑分区的功能’读取其分区条目的信息 */
All_partition_information( unsigned long *star_sec,
unsigned *no_par,
long *sec, char *buffer,
unsigned char *report_par )
{
unsigned long fat_check;
unsigned long *sectors_part;
static long se_p;
int temp_var1,active_offset,active_pos=0,i, extended_pos=0, partloc1;
unsigned long b_sec,se;
unsigned char active_par;
long relative_sec;
long no_sectors;
if(*sec==0 || *sec==1)
se_p=0;
do{
se=*sec;
/* 读取 *sec 指定的绝对扇区 */
readabsolutesectors (drive_num,*sec,1,buffer);
/* ***** 检查活动分区 ***** */
if(*sec==se && *no_par==0) /*if primary
partition */
{
*sec=se=0;
for(active_offset=446; active_offset<=494;active_offset+=16)
{
active_par=buffer[active_offset];
if(active_par==0x80) /* check for active
partition */
break;
else
active_pos++; /* position of active
partition */
}
/* 用于扩展分区 */
for(active_offset=450; active_offset<=511;active_offset+=16)
{
active_par=buffer[active_offset];
if(active_par==0x05 | active_par==0x0F)
/*check for extended partition */
break;
else
extended_pos++; /*position of extended
partition */
}
if(active_pos==4)
active_pos=1;
if(extended_pos==4)
extended_pos=1;
partloc1=0x1C0+extended_pos*16;
}
else
{
active_pos=0;
extended_pos=1;
partloc1=0x1D0;
if(se_p!=0)
{
*sec=se=se_p; /*starting of extended
partition */
}
}
/* 分区中的相对扇区 */
relative_sec= *(unsigned long *)(buffer+454+active_pos*16);
/* 分区中的扇区数 */
no_sectors=*(long *)(buffer+458+active_pos*16);
/* 识别文件系统指示字节 */
if( buffer[0x1C2+active_pos*16]==0x04 ||
buffer[0x1C2+active_pos*16]==0x05 ||
buffer[0x1C2+active_pos*16]==0x06 ||
buffer[0x1C2+active_pos*16]==0x0B ||
buffer[0x1C2+active_pos*16]==0x0C ||
buffer[0x1C2+active_pos*16]==0x0E ||
buffer[0x1C2+active_pos*16]==0x0F ||
buffer[0x1C2+active_pos*16]==0x07)
{
switch(buffer[0x1C2+active_pos*16])
{
/* For NTFS Partition */
case 0x07: report_par[*no_par]='N';
printf("\n Partition -%d = NTFS",
*no_par+1);
break;
/* 对于 FAT32 分区 */
case 0x0B:
case 0x0C: report_par[*no_par]='3';
printf("\n Partition -%d = FAT32",
*no_par+1);
break;
/* 对于 FAT16 分区 */
case 0x04:
case 0x06:
case 0x0E: report_par[*no_par]='1';
printf("\n Partition -%d = FAT16",
*no_par+1);
break;
} // End of the Switch
b_sec=*sec+relative_sec;
sectors_part[*no_par]=no_sectors; /* Array to store Number of sectors of partitions */
} //End of if Condition
else
{ /* if partition indicator not match */
if(*sec==0)
{ no_par=0;
break;
}
if((fat_check!=0x3631)&&(fat_check!=0x3233))
b_sec=*sec=0;
}
if((b_sec!=0)&&(sec!=0))
{
star_sec[*no_par]=b_sec;
(*no_par)++;
}
else
break;
/* 检查是否存在扩展分区 */
if(buffer[0x1C2+extended_pos*16]==0x05 ||
buffer[0x1C2+extended_pos*16]==0x0F )
{
temp_var1=(unsigned )buffer[partloc1];
*sec=temp_var1 & 0x003F; /* sector of
extended
partition */
if(*sec!=0)
{
se_p=se+relative_sec+no_sectors;
*sec=se_p;
}
else
{ *sec=-1;
break;
}
} //close of if statement
else
{
if(*sec>0)
*sec=-1;
break;
}
} while(1); // close of do–while loop
/* 检查扇区 0 上的其他非活动主分区 */
if(*sec==0)
{
for(i=0;i<4;i++)
{
active_par=buffer[446+i*16];
/* 识别文件系统指示符字节 */
if((buffer[0x1C2+i*16]==(char)0x06 ||
buffer[0x1C2+i*16]==(char)0x0B ||
buffer[0x1C2+i*16]==(char)0x0C ||
buffer[0x1C2+i*16]==(char)0x07 ||
buffer[0x1C2+i*16]==(char)0x0E ||
buffer[0x1C2+i*16]==(char)0x04) && active_par!=0x80)
{
switch(buffer[0x1C2+active_pos*16])
{
/* 对于 NTFS 分区 */
case 0x07: report_par[*no_par]='N';
printf("\n Partition -%d = NTFS",
*no_par+1);
break;
/* 对于 FAT32 分区 */
case 0x0B:
case 0x0C: report_par[*no_par]='3';
printf("\n Partition -%d = FAT32",
*no_par+1);
break;
/* 对于 FAT16 分区 */
case 0x04:
case 0x06:
case 0x0E: report_par[*no_par]='1';
printf("\n Partition -%d = FAT16",
*no_par+1);
break;
} // End of switch
/* 相关扇区 分区数 */
relative_sec=*(long *)(buffer+454+i*16);
no_sectors=*(long *)(buffer+458+i*16); /* number of
sectors in
partition*/
sectors_part[*no_par]=no_sectors; /* Array to store
Number of
sectors of
partitions */
*sec=star_sec[*no_par]=relative_sec;
(*no_par)++;
}
} //loop close of for(i=0;i<4;i++)
} //loop close of if(*sec==0)
return;
}
编码评论:
该函数开始从 MBR 读取分区信息,然后在需要时读取 Extended 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>
/* 结构从分区表中读取分区条目 */
struct partition
{
unsigned char bootable ; /* Active Partition
Byte */
unsigned char start_side ; /* Starting Head */
unsigned int start_sec_cyl ; /* combination of
Starting sector and
cylinder number */
unsigned char parttype ; /* File system
Indicator Byte */
unsigned char end_side ; /* Ending Head */
unsigned int end_sec_cyl ; /* combination of
Starting sector and
cylinder number */
unsigned long part_beg ; /* Relative Sector
Number */
unsigned long plen ; /* Partition length in
sectors */
} ;
/* 读取 MBR 的结构 */
struct part
{
unsigned char master_boot[446] ; /* IPL (Initial
Program Loader)*/
struct partition pt[4] ; /* Partition table*/
int lasttwo ; /* Magic Number */
} ;
struct part p ;
void main()
{
unsigned int t1,t2;
clrscr();
biosdisk ( 2, 0x80, 0, 0, 1, 1, &p ) ;
display(); /* display the partition
Table information */
getch();
/* 假设我们要从 MBR 的分区表中修改第二个分区条目的分区信息,这些值 */
p.pt[1].bootable = 0x80; /* Active Boot Partition */
p.pt[1].parttype = 0x7; /* NTFS Partition */
p.pt[1].start_side = 0; /* Starting Head =0 */
p.pt[1].end_side = 31; /* Ending Head == 31 */
p.pt[1].part_beg = 808416;/* Relative Sector = 808416 */
p.pt[1].plen = 405216; /* Total Sectors in Partition = 405216 */
/* 向 MBR 写入新信息 *\
/* 要将值写入 MBR 分区表,请取消注释下面给出的 biosdisk 函数 */
// biosdisk ( 3, 0x80, 0, 0, 1, 1, &p ) ;
display(); /* Display the Modified
Information */
getch();
}
编码评论:
上面给出的程序是一个示例程序,展示了我们如何修改MBR 的分区表项 的值。如果要修改这些逻辑分区的分区条目的值,它们位于扩展分区,你必须修改扩展MBR的分区表中的值。
这里给出的修改分区表项的值只是为了演示,如何修改。切勿使用非法或不合逻辑的值修改分区表。这样做可能会导致整个分区无法访问。
结构分区用于从分区表中读取分区条目,结构部分用于读取MBR。要真正对分区表进行修改,请取消注释 biosdisk( ) 函数。
如果要修改分区的起止值、扇区号和柱面号,计算数值,如程序注释中所述,读取并显示MBR的分区表,在本章开头讨论。