分析FAT32内部结构-入门篇-
fat32(file allocation table)是一种32位的fat文件系统,微软在1996年8月发布。
fat32的数字32是下面会讲到的fat中每个表项的长度。
磁盘(硬盘)是数据的载体,而文件系统则是将这些数据以某种合理的结构组织起来方便操作系统的管理。
借此文分析一下微软的fat32文件系统格式:(本文分析的fat32分区是d盘,大小为128mb!环境为vmware虚拟机下的xp系统!)
fat32由4个部分组成,分别是dbr,fat1,fat2和data,如图:
dbr:该分区的引导程序,在dbr的结尾部分会有一些重要的保留扇区(这些保留扇区属于dbr,图中未画出)
fat1:fat的首要文件分配表
fat2:文件分配表的备份
data:数据区(最小单位为簇(cluster),一般2个扇区为1簇,是微软规定的一种磁盘存储单位,与linux的block概念类似)
fat32的dbr结构图:
红色:跳转指令,将当前执行流程跳转到引导程序处,占2字节,对应汇编jump 58h; nop;
蓝色:oem代号,由创建该文件系统的厂商规定,占8字节,一般为”msdos5.0”
绿色:bpb(bios paramter block),从dbr的第12个字节开始共占用79字节,记录了文件系统的重要信息,相关字段参数见下表
粉红色:dbr引导程序,如果该分区没安装操作系统那么这段程序是没用的
黄色:dbr结束标记
bpb表: |
||
偏移 |
字节 |
含义 |
bh |
2 |
每扇区的字节个数 |
dh |
1 |
每簇扇区数 |
eh |
2 |
保留的扇区个数 |
10h |
1 |
fat个数 |
11h |
2 |
不使用(根目录数量,fat32已突破此限制,已无效,一般为0) |
13h |
2 |
不使用(扇区总数,小于32m时才使用) |
15h |
1 |
存储介质描述符 |
16h |
2 |
不使用(fat占的扇区数,小于32m时才使用) |
18h |
2 |
每磁道扇区个数 |
1ah |
2 |
磁头数 |
1ch |
4 |
隐藏扇区 |
20h |
4 |
扇区总数(大于32m时使用) |
24h |
4 |
fat占的扇区数(大于32m时使用) |
28h |
2 |
扩展标记 |
2ah |
2 |
版本,一般为0 |
2ch |
4 |
根目录的首簇号 |
30h |
2 |
文件系统整体信息扇区号 |
32h |
2 |
dbr备份所在的扇区号 |
34h |
12 |
保留,固定为0 |
40h |
1 |
bios驱动器号 |
41h |
1 |
不使用,一般为0 |
42h |
1 |
扩展引导标记 |
43h |
4 |
卷序列号 |
47h |
11 |
卷标 |
52h |
8 |
文件系统类型名,固定为”fat32 ” |
fat32文件系统在dbr的保留扇区中有一个文件系统信息扇区,用以记录数据区中空闲簇的数量及下一个空闲簇的簇号,该扇区一般在分区的lab1扇区,也就是紧跟着dbr后的一个扇区,其内如下:
褐色:扩展引导标签,为52 52 61 41,ascii为”rraa”
青色:文件系统信息签名,为72 72 41 61,ascii为”rraa”
蓝色:空闲簇的数量,(1fbb0)=129968,每个簇1k,约等于127mb,即d盘的大小
紫色:下一个空闲簇的簇号
黄色:结束标记
其他字节:不使用,填充0
由于fat紧跟在dbr的保留扇区之后,所以定位到最后一个保留扇区,那么下一个扇区就是fat啦!
在上面的dbr图中可以找到保留扇区的个数为20h=32(20 00是小端表示法),所以dbr往后32个扇区就是首要fat啦,如图:
fat以4字节(32位)为一个表项,每个表项值的含义:
0x0000 0000 |
空闲簇,可用簇 |
0x0000 0001 |
保留簇 |
0x0000 0002 ~ 0x0fff ffef |
该簇已用,其值指向下一个簇号 |
0x0fff fff0 ~ 0x0fff fff6 |
这些值保留,不使用 |
0x0fff fff7 |
坏簇,当一个簇中有一个扇区损坏(如物理损坏、病毒感染)时称为坏簇,这个簇将不被fat32使用 |
0x0fff fff8 ~ 0x0fff ffff |
文件的最后一个簇 |
每个表项的值对应了相应簇的使用情况,如2号表项对应了2号簇的使用情况,3号表项对应了3号簇的使用情况,注意的是,最开头的两个表项是不使用的,它们代表fat的表头,其值是固定的0xfff fff8和0xffff ffff,所以!fat32中不存在0号簇和1号簇,第1个簇是2号簇!画张图:
如果该簇是文件的最后一簇,填入的值为0x0fffffff,如果该簇不是文件的最后一簇,则填入的值为该文件占用的下一簇号(所以可以看出在fat32文件系统中文件是以簇链表的形式保存起来的)。
2号表项存储的是2号簇的使用情况,一般2号簇(也就是文件系统的第1个簇)存储的是文件系统的根目录,虽然在fat32中,根目录的位置不再硬性固定,可以存储在分区内可寻址的任意簇内,不过通常根目录是最早建立的(格式化时)目录,所以基本上都是根目录首簇紧邻fat2,占簇区顺序上的第1个簇(即2号簇),同时,fat32将根目录当做普通的数据文件,所有没有了目录个数的限制,在需要的时候可以分配空簇。这一项(2号表项)的值为0x0fffffff ,说明当前根目录占了1个簇的大小。
现在来分析数据区,数据区是紧接在fat2之后的,所以在dbr往后(保留扇区+fat1占用的扇区+fat2占用的扇区)个扇区就是数据区啦,如图(这是数据区的第一个扇区,也是第一个簇的前半部分):
发现前11字节是我们的卷标!
而在bpb中卷标那项的值永远是”no name ”,也就是说卷标被移动到了数据区的前11字节,而且fat32卷标最长11字节(因为bpb中已经规定了),而ntfs已经突破了这个限制。
其实,卷标是根目录下的第一个文件!
现在来分析一下fat32下文件和目录之间是如何组织的,如何存储的,如何保存属性的:
先低级格式化d盘,以确保分区干净(指的是数据区全填充为0),
低级格式化完成(如果分区较大,低格的速度会变慢,耐心等待)之后,查看一个首要fat表,
有一个表项,它的值为fff ffffh,意思就是结束簇,这就是我们的根目录啦,再查看一个根目录所在的起始簇,
发现前两行(32字节)有数据,后面全是0,然后我们在d盘(根目录)下粘贴一个空文件(在其他盘(如c盘)新建一个文本文件,然后复制到d盘,下面会讲为什么不能直接在d盘新建文件,而非要从其他盘复制),
再去首要fat中查看变化,发现没有变化,看起来新建的文件不占表项,再去根目录所在簇查看一下,
发现!多了2行(32字节),这就是我们刚刚新建(其实是粘贴)的空文件a.txt啦,现在我们要引入一个fat32下短文件目录项概念了,来个表格,
字节偏移 |
字节数 |
说明 |
|
0h |
8 |
文件名 |
|
8h |
3 |
后缀名,扩展名,类型名 |
|
bh |
1 |
文件属性 |
0000 0000b, 0h 读写 |
0000 0001b, 1h 只读 |
|||
0000 0010b, 2h 隐藏 |
|||
0000 0100b, 4h 系统 |
|||
0000 1000b, 8h 卷标 |
|||
0001 0000b, 10h 子目录 |
|||
0010 0000b, 20h 归档 |
|||
ch |
1 |
保留 |
|
dh |
1 |
创建时间的10毫秒位 |
|
eh |
2 |
创建时间 |
|
10h |
2 |
创建日期 |
|
12h |
2 |
最后一次访问的日期 |
|
14h |
2 |
起始簇号的高16位 |
|
16h |
2 |
最近一次修改的时间 |
|
18h |
2 |
最近一次修改的日期 |
|
1ah |
2 |
起始簇号的低16位 |
|
1ch |
4 |
文件长度 |
可以看出文件长度为0,起始簇号高16位为0h,低16位也为0h,也就是指向0号簇,但是0号簇不存在,所以此文件不存在起始簇,也就验证了文件长度为0,现在我们用记事本打开此文件,向其中写几个单词,
再来看看此文件的属性,
现在先去看一下首要fat的变化,
哈哈,多了一个表项哦,这个多的表项的值也是fff ffffh,也就是结束簇,我们再去根目录所在簇看看,
发现了没,起始簇号和文件长度都发现了相应的变化,图中给出了计算值,这符合前面用右键查看的a.txt属性,且起始簇号为3也是正确的,因为簇号2被根目录所占用,那么下一个簇号就是3啦,现在我们去簇号3看看,
现在我们将a.txt增加到几kb(我从网上找了微软的百度百科复制了其中一段),这里需要注意,因为我这个d盘的簇大小仅仅为1kb,所以大于1kb就可以完成本步骤的实验了,如果你们的簇大小为nkb,那么a.txt需要大于nkb才行,
然后我们先去根目录查看一下,
数值又发生了变化,但是一定和上图的属性中一样,这里不再计算,然后我们再去首要fat看一下,
发现,多了6项,且原来的3号表项已经不是fff ffffh了,取而代之的是9号表项变成了fff ffffh,这样就形成了簇链,文件系统读取时就通过这个单链表读取,我们简单计算一下,a.txt最多占7个簇(第7个簇恰好全部用完,即占用空间的大小),最少占6个簇(第7个簇只用了1个字节),那么推测出a.txt大小在[6144+1,7168]字节,而a.txt在其的属性截图中大小为6546字节,占用空间为7168字节,完全符合,
现在我们来看一下fat32的长文件目录项格式(现在基本用的都是长文件名啦),
字节偏移 |
字节数 |
说明 |
0h |
1 |
第7位:保留 |
第6位:1表示长文件最后一个目录项 |
||
第5位:保留 |
||
第0~4位:顺序号 |
||
1h |
10 |
unicode文件名第1部分 |
bh |
1 |
长文件名目录项标志,值默认fh |
ch |
1 |
保留 |
dh |
1 |
校验值(根据短文件名计算) |
eh |
12 |
unicode文件名第2部分 |
1ah |
2 |
起始簇号 |
1ch |
4 |
unicode文件名第3部分 |
我们再从其他盘复制过来一个长文件名的空文本文件,
查看一下根目录,
长文件名是以链表方式存储的,所以能存很长很长的文件名,文件名结束符为’\0’,即0h,如果结束符之后还有剩余的unicode字符则以ffh填充,在长文件目录项的最后两行是它的短目录项存储格式哦,简单说一下为什么长文件目录项第1行第1字节为43h,下图,
至此,简单的fat32组织结构已经介绍完了,欢迎读者深入探究。
附上,一篇写fat32不错的博文:
来说一下为什么上面要复制其他盘的文件到d盘,因为直接在d盘新建一个文本文件是“新建 文本文档.txt”,然后我们将它重名名为“new.txt”,然后在winhex查看,发现还是存在一个“新建 文本文档.txt”的,只不过是删除状态的,所以为了避免这项因素的干扰,我就选择了从其他盘复制文件过来。
附上簇的取值范围表:
分区大小 |
fat32默认簇大小 |
32~256m |
中间还分多个挡位:512b,1k,2k |
257m~8g |
4k |
8g~16g |
8k |
32g~2t |
32k |
题外话,也算是一个练习,验证网上说的“fat32最大只能存放4g的文件”:
因为fat32中目录项中每个文件的文件长度是4字节,4字节最大能寻址的字节数为2的32次,换算为g就是4g啦,即2^32b/1024^3=4g。