1.启动盘制作
1.1.u盘操作
1.一个尺寸足够小的u盘(尺寸100MB左右为宜)。
2.采用DiskGenius操作u盘
2.1.清空u盘所有扇区。
2.2.转换启动模式--转换为usb-fdd。文件系统选择fat-12。
这样就得到一个启动模式为usb-fdd,文件系统为fat-12的u盘。
1.2.u盘访问
对u盘这样无扇区,磁头,磁道的存储介质。BIOS提供如下方法来提供访问方法。
INT 13h, AH=42h
1.DL=磁盘驱动器号
2.DS:SI指向硬盘地址包结构
硬盘地址包可分16B,24B。16B中,缓存区地址32位;24B中,如偏移04h处为FFFFh:FFFFh,则缓存区地址采用64位;
偏移 尺寸 描述
00h 1B 结构尺寸
01h 1B 保留
02h 2B 传输扇区数
04h 4B 缓存区地址(段:偏移)
08h 8B LBA型起始扇区编号(从0开始)
10h 8B 64位缓存区地址
1.3.u盘起始扇区翻译(按FAT12文件系统)
用DiskGenius查看u盘首个扇区二进制数据,按表格进行解析即可。
偏移 | 尺寸 | 内容 | 简写 | |
0 | 3 | 跳转指令 | BS_jmpBoot | |
3 | 8 | 生产商 | BS_OEMName | |
11 | 2 | 每扇区字节数 | BPB_BytesPerSec | |
13 | 1 | 每簇扇区数 | BPB_SecPerClus | |
14 | 2 | 保留扇区数 | BPB_RsvdSecCnt | |
16 | 1 | FAT表数 | BPB_NumFATs | |
17 | 2 | 根目录支持项数 | BPB_RootEntCnt | |
19 | 2 | 扇区总数 | BPB_TotSec16 | |
21 | 1 | 介质 | BPB_Media | |
22 | 2 | 每FAT扇区数 | BPB_FATSz16 | |
24 | 2 | 每磁道扇区数 | BPB_SecPerTrk | |
26 | 2 | 磁头数 | BPB_NumHeads | |
28 | 4 | 隐藏扇区数 | BPB_HiddSec | |
32 | 4 | 扇区总数 | BPB_TotSec32 | |
36 | 1 | 驱动器号 | BS_DrvNum | |
37 | 1 | 保留 | BS_Reserved1 | |
38 | 1 | 扩展引导标记 | BS_BootSig | |
39 | 4 | 卷序列号 | BS_VolID | |
43 | 11 | 卷标 | BS_VolLab | |
54 | 8 | 文件系统类型 | BS_FileSysType | |
62 | 448 | 自由使用 | Free | |
510 | 2 | 结束标志 |
进一步说明:
因为我们前面对u盘执行了扇区清理,格式化为FAT12文件系统,设置为USB-FDD启动。所以,u盘首个扇区的数据组织及其含义就必须按上述规范来。
上述规范是文件系统,引导扇区综合作用的结果。
文件系统关注的是在指定偏移,精确定义文件系统起作用所依赖的各项参数。
引导扇区关注的是,扇区将被BIOS自动加载到物理地址0x7c00处,并从扇区起始位置取指令,执行指令。扇区尺寸是512字节。扇区结尾两个字节必须是0xAA55。
1.4.FAT12文件系统解析(USB-FDD启动模式下)
以下对FAT12文件系统进行详细解析。限定在USB-FDD启动模式下。
1.4.1..u盘首个扇区既是引导扇区,同时也是FAT12文件系统首个扇区。
FAT12文件系统首个扇区定义如下
偏移 | 尺寸 | 内容 | 简写 | |
0 | 3 | 跳转指令 | BS_jmpBoot | |
3 | 8 | 生产商 | BS_OEMName | |
11 | 2 | 每扇区字节数 | BPB_BytesPerSec | |
13 | 1 | 每簇扇区数 | BPB_SecPerClus | |
14 | 2 | 保留扇区数 | BPB_RsvdSecCnt | |
16 | 1 | FAT表数 | BPB_NumFATs | |
17 | 2 | 根目录支持项数 | BPB_RootEntCnt | |
19 | 2 | 扇区总数 | BPB_TotSec16 | |
21 | 1 | 介质 | BPB_Media | |
22 | 2 | 每FAT扇区数 | BPB_FATSz16 | |
24 | 2 | 每磁道扇区数 | BPB_SecPerTrk | |
26 | 2 | 磁头数 | BPB_NumHeads | |
28 | 4 | 隐藏扇区数 | BPB_HiddSec | |
32 | 4 | 扇区总数 | BPB_TotSec32 | |
36 | 1 | 驱动器号 | BS_DrvNum | |
37 | 1 | 保留 | BS_Reserved1 | |
38 | 1 | 扩展引导标记 | BS_BootSig | |
39 | 4 | 卷序列号 | BS_VolID | |
43 | 11 | 卷标 | BS_VolLab | |
54 | 8 | 文件系统类型 | BS_FileSysType | |
62 | 448 | 自由使用 | Free | |
510 | 2 | 结束标志 |
即,首个扇区偏移[3, 62)区间内的数据规定了FAT12文件系统所必须的属性信息。
1.4.2.FAT12文件系统整体布局
1.4.3.FAT12文件系统首个扇区解析
参考1.4.1.
1.4.4.FAT12文件系统FAT表解析
既然是表,就是由一个个表项构成的区域。
FAT一个表项尺寸是12个比特位。
索引0的表项,磁盘标示字。低字节与BPB_Media数值一致。
索引1的表项,值固定为0xFFF。
从索引2开始的表项才是可被自由使用的表项。
对索引为x的表项(x>=2)的值value的解释如下:
0x000,表示这个值代表的簇尚未存储文件数据。
0x002~0xFEF,表示值为x的簇所存储文件的后续部分存储在值为value的簇中。
0xFF0~0xFF6,表示值为x的簇是保留簇。
0xFF7,表示值为x的簇是坏簇。
0xFF8~0xFFF,表示值为x的簇是所存储文件的最后一个簇。
1.4.5.FAT12文件系统根目录区解析
根目录区也可成为根目录表。
既然是表,就是由一个个表项构成的区域。
根目录区的一个表项尺寸是32字节。
名称 | 偏移 | 长度 | 描述 |
DIR_Name | 0x00 | 11 | 文件名占据前8B,扩展名占据后3B |
DIR_Attr | 0x0B | 1 | 文件属性 |
保留 | 0x0C | 10 | 保留 |
DIR_WrtTime | 0x16 | 2 | 最后一次写入时间 |
DIR_WrtDate | 0x18 | 2 | 最后一次写入日期 |
DIR_FstClus | 0x1A | 2 | 起始簇号 |
DIR_FileSize | 0x1C | 4 | 文件尺寸 |
关于DIR_Name进一步解释:
假设我们在FAT12文件系统的u盘存储了一个名字为loader.bin的文件,则在这个文件对应的目录项的DIR_Name字段里面11字节的内容将是:'LOADER BIN'。
即目录项的文件名字母全部采用大写字母。不足部分采用空格符填充。
1.4.6.如何在FAT12文件系统搜索文件
1.首先定位到FAT12根目录起始扇区
根目录起始扇区编号=1+FAT1区域占据扇区数+FAT2区域占据扇区数
2.借助usb-fdd启动模式的u盘访问机制逐个将根目录区域内每个扇区加载到物理内存
3.对加载到物理内存的每个根目录区域的扇区逐个分析扇区内每个根目录项。
4.如果某个根目录项的DIR_Name与要搜索的文件名完全匹配。则基于这个目录项的DIR_FstClus字段即完成目标文件搜寻工作。
假设DIR_FstClus字段数值为x。这个x的含义是告诉我们目标文件起始部分存储在数值为x的簇中。
5.如果我们对整个根目录区域内每个扇区的每个目录项分析后,均无法找到匹配。则认为目标文件在文件系统中不存在。
1.4.7.如何将FAT12文件系统内的目标文件完整加载到物理内存
1.假设我们现在已经寻找了匹配的根目录项,其DIR_FstClus字段数值为x。
2.这个x代表了一个簇大小的数据区域。一个簇尺寸=BPB_SecPerClus*BPB_BytesPerSec
我们首先要得到这个值为x的簇的起始扇区编号p。
p = (x - 2) * BPB_SecPerClus + 1 + FAT1表占据扇区数 + FAT2表占据扇区数 + 根目录占据扇区数。
FAT1表占据扇区数=BPB_FATSz16
FAT2表占据扇区数=BPB_FATSz16
根目录占据扇区数=(BPB_RootEntCnt * 32) / BPB_BytesPerSec
3.有了起始扇区编号,扇区数目。我们就可以借助1.2提供的u盘访问方法将目标区域内多个扇区读取到指定物理地址位置。
4.一个尺寸稍微打点的文件,需要由多个簇才能完整存储文件数据。现在通过目录项的DIR_FstClus我们获知了首个簇存储的文件数据。现在需要知道下个簇内存储的文件数据。
首先需要获取下个簇在那里。
5.已知当前簇,获取下个簇方法是
假设当前簇数值为x。
在FAT1区域取得索引为x的表项值。
表项值在 0x002~0xFEF范围内时,表项值就是下个簇。
表项值在0xFF8~0xFFF范围内时,表示当前簇就是文件最后一个簇。
这样我们可以继续读取下个簇内的文件数据。且重复3,4,5过程。直到完整读取了文件在u盘的数据。
1.4.8.向u盘拷贝文件时发生了什么
我们之所以,可以按上述方式去搜索并定位一个文件在文件系统中的存储位置。必然会要求向文件系统存储新文件时,必须遵循文件系统的规范。
这个规范概况起来可以这么说:
1.搜索根目录区域,找到一个空闲根目录项。将此目录项用于记录目标文件。
2.在FAT1表中搜索一个数值为0x000的表项。这样,假设这个表项的索引为x。这样我们就可以用簇x的区域存储文件起始部分。并对应设置文件关联目录项的DIR_FstClus为x。
3.如果文件剩余部分存在,则继续搜索FAT1表。继续寻找数值为0x000的表项。找到这个表项后,假设当前簇是pre,找到的表项索引是next。 一方面我们需要将FAT1表表项pre的值设置为next。一方面我们需要继续将文件剩余部分存储到簇next对应区域内。并重复这一过程。如果文件剩余部分不存在。假设当前簇是pre。则我们需要将FAT1表表项pre的值设置为0xFFF即可。
这样我们就完成了向文件系统写入文件。既包含文件数据在数据区的存储,也包含文件信息在文件系统根目录区,FAT1表区域的注册。
1.5.数据结构在小端存储系统的物理存储
假设我们有一个这样的结构定义及赋值。
struct Table
{
int8_t nSize;
int8_t nRsvd;
int16_t nNum;
int16_t nOff;
int16_t nSec;
int64_t nStartNo;
};
Table stA;
stA.nSize = 0x10;
stA.nRsvd = 0x00;
stA.nNum = 0x02;
stA.nOff = 0x8000;
stA.nSec = 0x00;
st.nStartNo = 0x12345678;
则上述stA在小端存储系统里物理内存中存储情况如下:
对nSize部分,我们只能说物理地址(char*)(&stA)位置内1字节数值为0x10。
对nRsvd部分,我们只能说物理地址(char*)(&stA)+1位置内1字节数值为0x00。
对nNum部分,我们只能说物理地址(char*)(&stA)+2位置内1字节数值为0x02。物理地址(char*)(&stA)+3位置内1字节数值为0x00。
对nOff部分,我们只能说物理地址(char*)(&stA)+4位置内1字节数值为0x00。物理地址(char*)(&stA)+5位置内1字节数值为0x80。
对nSec部分,我们只能说物理地址(char*)(&stA)+6位置内1字节数值为0x00。物理地址(char*)(&stA)+7位置内1字节数值为0x00。
对nStartNo部分,我们只能说物理地址(char*)(&stA)+8位置内1字节数值为0x78。物理地址(char*)(&stA)+9位置内1字节数值为0x56。物理地址(char*)(&stA)+10位置内1字节数值为0x34。物理地址(char*)(&stA)+11位置内1字节数值为0x12。物理地址(char*)(&stA)+12位置内1字节数值为0x00。物理地址(char*)(&stA)+13位置内1字节数值为0x00。物理地址(char*)(&stA)+14位置内1字节数值为0x00。物理地址(char*)(&stA)+15位置内1字节数值为0x00。
上述精确到了物理内存字节级存储细节。
进一步,通过以下实例来精确到小端存储系统存储数值的比特位级别的细节。
FAT表一个表项占据12个比特位。
我们假设FAT表前4个表项数值分别为0x001,0x002,0x123,0x224。
为了说明比特位级别的数值存储,我们不妨把这4个表项看成一个尺寸为6字节的物理区域。共有48个比特位。
区域首先存储0x001,
按比特位级别展开后0b 0000 0000 0001,该区域最低比特位存储比特1,高一级存储比特0,如此类推。
然后顺序存储0x002,
按比特位级别展开后0b 0000 0000 0010,该区域最低比特位存储比特0,高一级存储比特1,如此类推。
如此类推。