NTFS文件系统详解(二)MBR\EBR基本信息
经过上一篇文章对硬盘的基本结构的详细介绍后,我们再来看看MBR和EBR的结构。
一、MBR结构分析
MBR(Main Boot Record 主引导记录区)位于整个硬盘的0磁道0柱面1扇区。不过,在总共512字节的主引导扇区中,MBR只占用了其中的446个字节,另外的64个字节交给了 DPT(Disk Partition Table硬盘分区表),最后两个字节“55,AA”是分区的结束标志。这个整体构成了硬盘的主引导扇区。
主引导记录中包含了硬盘的一系列参数和一段引导程序。其中的硬盘引导程序的主要作用是检查分区表是否正确并且在系统硬件完成自检以后引导具有激活标志的分区上的操作系统,并将控制权交给启动程序。MBR是由分区程序(如fdisk.exe)所产生的,它不依赖任何操作系统,而且硬盘引导程序也是可以改变 的,从而实现多系统共存。
其中从0x01BE到0x01FD这六十四个字节表示的是4个主分区的信息。每个主分区16个字节描述,这16反个字节的含义是:
存贮字节位 | 内容及含义 |
---|---|
第1字节 | 引导标志,若值为80H表示活动分区,若值为00H表示非活动分区。 |
第2\3\4字节 | 本分区的起始磁头号、扇区号、柱面号。其中: 磁头号–第2字节; 扇区号–第3字节的低6位; 柱面号–第3字节高2位+第4字节8位。 |
第5字节 | 分区类型符; 00H–表示该分区未用(即没有指定); 06H–FAT16基本分区; 0BH–FAT32基本分区; 05H–扩展分区; 07H–NTFS分区; 0FH–(LBA模式)扩展分区(83H–Linux分区等) |
第6\7\8字节 | 本分区的结束磁头号、扇区号、柱面号。其中: 磁头号–第6字节; 扇区号–第7字节的低6位; 柱面号–第7字节高2位+第8字节8位。 |
第9\10\11\12字节 | 本分区之前已用了的扇区数(分区起始扇区)。 |
第13\14\15\16字节 | 本分区的总扇区数 |
注:由于硬盘的发展,2\3\4字节的分区起始扇区所在柱面,磁头,扇区,6\7\8字节的分区终止扇区所在的柱面,磁头,扇区信息已不正确,如果硬盘稍微大点,chs逻辑硬盘地址方案中的c值有可能就不能由现在分配的位数表达了。因而,在柱面超过3FF的时候,将失效,这三个值一般为FE FF FF(16450560个扇区之后,也就是扇区位置超过7.44G時设为这个值),在MBR规范中,直接舍弃“virtual CHS values逻辑硬盘地址方案”,而采用LBA方案,更详细的分区类型符如下:
- | - | - | - |
---|---|---|---|
00 | 空,mocrosoft不允许使用。 | 63 | GNU HURD or Sys |
01 | FAT32 | 64 | Novell Netware |
02 | XENIX root | 65 | Novell Netware |
03 | XENIX usr | 70 | Disk Secure Mult |
04 | FAT16<32M | 75 | PC/IX |
05 | Extended | 80 | Old Minix |
06 | FAT16 | 81 | Minix/Old Linux |
07 | HPFS/NTFS | 82 | Linux swap |
08 | AIX | 83 | Linux |
09 | AIX bootable | 84 | OS/2 hidden C: |
0A | OS/2 Boot Manage | 85 | Linux extended |
0B | Win95 FAT32 | 86 | NT volume set |
0C | Win95 FAT32 | 87 | NT volume set |
0B | Win95 FAT16 | 93 | Amoeba |
0F | Win95 Extended(>8GB) | 94 | Amoeba BBT |
10 | OPUS | A0 | IBM Thinkpad hidden |
11 | Hidden FAT12 | A5 | BS0/386 |
12 | Compaq diagnost 116 Open BSD | ||
16 | HiddenFAT16 | A7 | NextSTEP |
14 | Hidden FAT16<32GB | B7 | BSDI fs |
17 | Hidden HPFS/NTFS | B8 | BSDI swap |
18 | AST Windows swap | BE | Solaris boot partition |
1B | Hidden FAT32 | - | - |
1C | Hidden FAT32 partition (using LBA-mode INT 13 extensions) | C0 | DR-DOS/Novell DOS secured partition |
- | - | C1 | DRDOS/sec |
1E | Hidden LBA VFAT partition | C4 | DRDOS/sec |
24 | NEC DOS | C6 | DRDOS/sec |
3C | Partition Magic | C7 | Syrinx |
40 | Venix 80286 | DB | CP/M/CTOS |
41 | PPC PreP Boot | E1 | DOS access |
42 | SFS | E3 | DOS R/0 |
40 | QNX4.x | E4 | SpeedStor |
4E | QNX4.x 2nd part | EB | BeOS fs |
4F | QNX4.x 3rd part | F1 | SpeedStor |
50 | OnTrack DM | F2 | DOS 3.3+ secondary partition |
51 | OnTrack DM6 Aux | - | - |
52 | CP/M | F4 | SpeedStor |
53 | oNtRACK DM6 Aux | FE | LAN step |
54 | OnTrack DM6 | FF | BBT |
55 | EZ-Drive | - | - |
56 | Golden Bow | - | - |
5C | Priam Edisk | - | - |
61 | Speed Stor | - | - |
知道MBR的基本信息之后,我们再来看看实际的情况事怎样的.我将我电脑磁盘分了五个区,其中三个基本分区,三个扩展分区(其中一个分区格式化成FAT16)。
用WinHex查看MBR的数据,可以看到446字节的主引导程序和64字节的分区表项+两字节的”55 AA”有效分区结束标志。
1. 第一个分区表项
80 01 01 00 07 FE FF FF 3F 00 00 00 C1 FF BF 03
偏移(offset) | 描述 |
---|---|
80 | 活动分区 |
01 01 00 | 磁头号1,扇区号1,柱面号0 |
07 | NTFS分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
3F 00 00 00 | 分区起始扇区号63 |
C1 FF BF 03 | 分区所占扇区数03BFFFC1(62914497),30G大小 |
2. 第二个分区表项
00 FE FF FF 07 FE FF FF 00 00 C0 03 00 00 40 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
07 | NTFS分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
00 00 C0 03 | 分区起始扇区号62914560 |
00 00 40 00 | 分区所占扇区数(4194304),2G大小 |
3. 第三个分区表项
00 FE FF FF 07 FE FF FF 00 00 00 04 00 00 40 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
07 | NTFS分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
00 00 00 04 | 分区起始扇区号67108864 |
00 00 40 00 | 分区所占扇区数(4194304),2G大小 |
4. 第四个分区表项
00 FE FF FF 0F FE FF FF 00 00 40 04 00 00 C0 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
0F | 扩展分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
00 00 40 04 | 分区起始扇区号71303168 |
00 00 C0 00 | 分区所占扇区数(12582912),6G大小 |
二、EBR结构分析
通过分区表项,我们便可以得到分区所在的扇区范围,找到分区相应的扇区.但是如果是扩展分区,还需要EBR找到相应扩展分区所在的位置
扩展分区中的每个逻辑驱动器都存在一个类似于MBR的扩展引导记录(Extended Boot Record)EBR,也有人称之为虚拟MBR或扩展MBR,意思是一样的。扩展引导记录包括一个扩展分区表和该扇区的标签。扩展引导记录将记录只包含扩展分区中每个 逻辑驱动器的第一个柱面的第一面的信息。一个逻辑驱动器中的引导扇区一般位于相对扇区32或63。
但是,如果磁盘上没有扩展分区,那么就不会有扩展引导记 录和逻辑驱动器。
第一个逻辑驱动器的扩展分区表中的第一项指向它自身的引导扇区。第二项指向下一个逻辑驱动器的EBR。如果不存在进一步的逻辑驱动器,第二项就不会使用,而且被记录成一系列零。如果有附加的逻辑驱动器,那么第二个逻辑驱动器的扩展分区表的第一项会指向它本身的引导扇区。第二个逻辑驱动器的扩展分区表的第二项指向下一个逻辑驱动器的EBR。扩展分区表的第三项和第四项永远都不会被使用。
下图是扩展分区的一般结构。
跟上面一样,我们通过WinHex查看EBR的数据,可以看到在01BE开始处有着扩展分区的分区表项。
1. 第一个分区表项
00 FE FF FF 07 FE FF FF 40 00 00 00 C0 07 40 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
07 | NTFS分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
40 00 00 00 | 分区起始扇区号64(相对扩展分区首扇区号),实际64+71303168=71303232 |
C0 07 40 00 | 分区所占扇区数(4196288),2G大小 |
2. 第二个分区表项
00 FE FF FF 05 FE FF FF 00 08 40 00 00 08 40 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
05 | 扩展分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
00 08 40 00 | 分区起始扇区号4196352(相对本逻辑驱动器首扇区号),实际4196352+71303168=75499520 |
00 08 40 00 | 分区所占扇区数(4196352),2G大小 |
可以看到这里是第二个逻辑驱动器的第一个扇区,有两个分区表项。
2.1 第一个分区表项
00 FE FF FF 07 FE FF FF 40 00 00 00 C0 07 40 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
07 | NTFS分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
40 00 00 00 | 分区起始扇区号64(相对本逻辑驱动器首扇区号),实际64+75499520=75499584 |
C0 07 40 00 | 分区所占扇区数(4196288),2G大小 |
2.2 第二个分区表项
00 FE FF FF 05 FE FF FF 00 10 80 00 00 F0 3F 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
05 | 扩展分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
00 10 80 00 | 分区起始扇区号8392704(相对扩展分区首扇区号),实际8392704+71303168=79695872 |
00 F0 3F 00 | 分区所占扇区数(4190208),2G大小 |
可以看到,因为是最后一个扩展驱动器,所以分区项只有一项。
2.3 第三个分区表项
00 FE FF FF 0E FE FF FF 40 00 00 00 C0 EF 3F 00
偏移(offset) | 描述 |
---|---|
00 | 非活动分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
0E | FAT16分区 |
FE FF FF | 超过第16450560个扇区,设为最大值,可忽视 |
40 00 00 00 | 分区起始扇区号64(相对本逻辑驱动器首扇区号),实际64+79695872=79695936 |
C0 EF 3F 00 | 分区所占扇区数(4190144),2G大小 |
通过实际的实验,我发现,在MBR里只有四个分区项,分别保存着三个基本分区和一个扩展分区的基本信息,在扩展分区里,又分为多个扩展逻辑分区,每个分区的扩展分区表里保存着本扩展分区的实际数据开始扇区信息和下一个扩展分区的基本信息,直到最后一个扩展分区
下面附加一段C语言代码,可用于读取任一扇区信息。
/*******************************************************************************
程序员 : enjoy5512
最后修改时间 : 2016年3月22日 20:56:41
程序说明 : 本程序,用于读取给定输入的磁盘扇区的扇区号的512字节内容,并回显到
命令行界面
测试环境 : Windows XP SP2 + VC6.0
待解决问题 : 磁盘第二个扇区的信息获取不到
因为测试的原因,程序只有在输入超大的扇区号才会退出
*******************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
/*
函数说明:
这个函数用于按照指定的格式输出一个扇区的512字节的信息,字符ascll码低于32或者大于127
的都用'.'来代替,其余的输出指定的字符
输入参数:
char buff[] : 字符串数组
输出参数:
0
*/
int ShowBuff(char buff[])
{
int i = 0;
int j = 0;
system("cls"); //清屏
system("mode con cols=80 lines=37"); //设定命令行窗口大小
printf("********************************************************************************");
printf("********************************************************************************");
printf("\t 0 1 2 3 4 5 6 7 8 9 A B C D E F |\n");
for(i = 0;i < 32;i++) //每行输出16个字节,后面跟着相应的字符
{
printf("%#011X:",i*16);
for(j = 0;j < 16;j++)
{
//每四位单独输出,否则会被当作八位有符号整数输出,暂无解决办法
printf(" %X%X",(buff[j+i*16]&240)>>4,buff[j+i*16]&15);
}
printf(" | ");
for(j = 0;j < 16;j++)
{
if(buff[j+i*16] > 31 && buff[j+i*16] < 127)
{
//输出相应的字符,若对应ascll码低于32或者大于127的都用'.'来代替
printf("%c",buff[j+i*16]);
}
else
{
printf(".");
}
}
printf("\n");
}
return 0;
}
/*
函数说明:
这个函数用于按照给定的扇区号返回相应扇区512字节信息
输入参数:
HANDLE hDisk : 磁盘句柄
char buff[] : 字符串数组
int n : 扇区号
DWORD* DCount: 读取的字节数
输出参数:
0
*/
int GetBuff(HANDLE hDisk,char buff[] , int n,DWORD* DCount)
{
LARGE_INTEGER li={0}; //记得初始化
int h_sector = 0; //32位系统,用于当超出32位寻址范围时保存高地址
int l_sector = 0; //32位系统,用于当超出32位寻址范围时保存低地址
l_sector = n%8388607; //取低地址部分
h_sector = n/8388607; //取高地址部分
li.LowPart = l_sector * 512; //我32位系统int取四位,FFFFFFFF/200H = 8388607
li.HighPart = h_sector; //高位直接传过去
//如果超出寻址范围则退出,否则将指针移动到相应扇区
if(-1 == SetFilePointer(hDisk,li.LowPart,&li.HighPart,FILE_BEGIN))
{
MessageBox(NULL,"Error sector!The program will exit!!","Error",MB_OK);
exit(-1);
}
//读取512字节内容到buff
ReadFile(hDisk, //要读的文件句柄
buff, //存放读出内容
512, //要读的字节数
DCount, //实际读取字节数
NULL);
return 0;
}
/*
函数说明:
这个函数用于按照给定的磁盘号,返回相应的句柄
输入参数:
HANDLE hDisk : 磁盘句柄
int nDisk : 磁盘号号
输出参数:
0
*/
int GetHandle(HANDLE* hDisk,int nDisk)
{
char szDriverBuffer[128]; //保存相应磁盘路径信息
memset(szDriverBuffer,0,128); //格式化设备文件名称
sprintf(szDriverBuffer,"\\\\.\\PhysicalDrive%d",nDisk);//获取设备名称
*hDisk = CreateFileA(
szDriverBuffer, // 设备名称,这里指第一块硬盘,多个硬盘的自己修改就好了
GENERIC_READ, // 指定读访问方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享模式为读 | 写,只读的话我总是出错..
NULL, // NULL表示该句柄不能被子程序继承
OPEN_EXISTING, // 打开已经存在的文件,文件不存在则函数调用失败
NULL, // 指定文件属性
NULL);
if (*hDisk==INVALID_HANDLE_VALUE) //如果获取句柄失败,则退出
{
MessageBox(NULL,"Error open disk!","Error",MB_OK);
exit(-1);
}
return 0;
}
/*
函数说明:
主函数
输入参数:
0
输出参数:
0
*/
int main(void)
{
char buff[513] = {0}; //保存512字节信息,因为字符串数组后面会加\0,所以申请513字节
HANDLE hDisk; //磁盘句柄
DWORD DCount=0; //返回读取的字节数
int sector = 0; //扇区号
int nDisk = 0; //磁盘号
printf("请输入需要读取的磁盘号:"); //获取磁盘号
scanf("%d",&nDisk);
ShowBuff(buff); //初始化界面
GetHandle(&hDisk,nDisk); //获取磁盘句柄
for(;;) //循环显示扇区信息
{
printf("please input the number of sector : "); //获取扇区号
scanf("%d",§or);
GetBuff(hDisk,buff,sector,&DCount); //获取扇区信息
ShowBuff(buff); //显示扇区信息
}
CloseHandle(hDisk); //释放句柄
return 0;
}