zoukankan      html  css  js  c++  java
  • 第5课

    第5课 - 主引导程序的扩展(下)

    1. 在 FAT12 根目录中查找目标文件

      在前面课程的学习中,我们知道主引导程序有一个 512 字节的限制,如何突破这种限制呢?我们想到的办法是:再写一个程序(LOADER)放到存储介质中,在主引导程序中将该程序加载到内存中,并将控制权转交给该程序(jump)。

      那如何找到存储介质中的程序(LOADER)呢?答案就是需要借助一个文件系统(FAT12),将该程序放到软盘中(软盘的文件系统格式是 FAT12),根据文件系统数据组织的方式便能方便的找到这个程序。那如何具体的实现呢,请看本节。

    1.1 根目录区的大小和位置

      

        其中BPB_RootEntCnt 表示根目录区的目录项的个数(0xE0,224

            RootEntry 表示根目录区每个目录项的大小(32 Bytes

            BPB_BytsPerSec 表示每扇区的字节数(512 Bytes

            224 * 32 = 7168Bytes          7168 / 512 = 14扇区

      

    1.2 FAT12 文件系统中的根目录区

      根目录区由目录项构成,每一个目录项代表根目录中的一个文件索引。

      

      文件名 8 字节,扩展名 3 字节,未占用的部分用 “空格” 代替,ascii = 0x20

      在 FAT12 中,1簇 = 1扇区

      目录项中的关键成员:

      • DIR_Name

           文件名(用于判断是否为目标文件)

      • DIR_FstClus

           文件数据起始存储位置(用于确定读取位置)

      • DIR_FileSize

           文件大小(用于确定读取的字节数)

    1.3 实验:读取 FAT12 文件系统的根目录信息

      — 步骤:

      • 创建 RootEntry 结构体类型(根据前面目录项的那个表格创建)

          

      • 使用文件流顺序读取每个项的内容
      • 解析并打印相关的信息

    实验1:读取根目录区的目录项】 (实验的环境为 Qt)

      1 #include <QtCore/QCoreApplication>
      2 #include <QFile>
      3 #include <QDataStream>
      4 #include <QDebug>
      5 #include <QVector>
      6 #include <QByteArray>
      7 #include <stdio.h>
      8 
      9 #pragma pack(push)
     10 #pragma pack(1)
     11 
     12 struct Fat12Header
     13 {
     14     char BS_OEMName[8];
     15     ushort BPB_BytsPerSec;
     16     uchar BPB_SecPerClus;
     17     ushort BPB_RsvdSecCnt;
     18     uchar BPB_NumFATs;
     19     ushort BPB_RootEntCnt;
     20     ushort BPB_TotSec16;
     21     uchar BPB_Media;
     22     ushort BPB_FATSz16;
     23     ushort BPB_SecPerTrk;
     24     ushort BPB_NumHeads;
     25     uint BPB_HiddSec;
     26     uint BPB_TotSec32;
     27     uchar BS_DrvNum;
     28     uchar BS_Reserved1;
     29     uchar BS_BootSig;
     30     uint BS_VolID;
     31     char BS_VolLab[11];
     32     char BS_FileSysType[8];
     33 };
     34 
     35 struct RootEntry
     36 {
     37     char DIR_Name[11];
     38     uchar DIR_Attr;
     39     uchar reserve[10];
     40     ushort DIR_WrtTime;
     41     ushort DIR_WrtDate;
     42     ushort DIR_FstClus;
     43     uint DIR_FileSize;
     44 };
     45 
     46 #pragma pack(pop)
     47 
     48 void PrintHeader(Fat12Header& rf, QString p)
     49 {
     50     QFile file(p);
     51 
     52     if( file.open(QIODevice::ReadOnly) )
     53     {
     54         QDataStream in(&file);
     55 
     56         file.seek(3);
     57 
     58         in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf));
     59 
     60         rf.BS_OEMName[7] = 0;
     61         rf.BS_VolLab[10] = 0;
     62         rf.BS_FileSysType[7] = 0;
     63 
     64         qDebug() << "BS_OEMName: " << rf.BS_OEMName;
     65         qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec;
     66         qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus;
     67         qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt;
     68         qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs;
     69         qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt;
     70         qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16;
     71         qDebug() << "BPB_Media: " << hex << rf.BPB_Media;
     72         qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16;
     73         qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk;
     74         qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads;
     75         qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec;
     76         qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32;
     77         qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum;
     78         qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1;
     79         qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig;
     80         qDebug() << "BS_VolID: " << hex << rf.BS_VolID;
     81         qDebug() << "BS_VolLab: " << rf.BS_VolLab;
     82         qDebug() << "BS_FileSysType: " << rf.BS_FileSysType;
     83 
     84         file.seek(510);
     85 
     86         uchar b510 = 0;
     87         uchar b511 = 0;
     88 
     89         in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510));
     90         in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511));
     91 
     92         qDebug() << "Byte 510: " << hex << b510;
     93         qDebug() << "Byte 511: " << hex << b511;
     94     }
     95 
     96     file.close();
     97 }
     98 
     99 RootEntry FindRootEntry(Fat12Header& rf, QString p, int i)
    100 {
    101     RootEntry ret = {{0}};
    102 
    103     QFile file(p);
    104 
    105     if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) )
    106     {
    107         QDataStream in(&file);
    108 
    109         file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry));
    110 
    111         in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret));
    112     }
    113 
    114     file.close();
    115 
    116     return ret;
    117 }
    118 
    119 void PrintRootEntry(Fat12Header& rf, QString p)
    120 {
    121     for(int i=0; i<rf.BPB_RootEntCnt; i++)
    122     {
    123         RootEntry re = FindRootEntry(rf, p, i);
    124 
    125         if( re.DIR_Name[0] != '' )
    126         {
    127             qDebug() << i << ":";
    128             qDebug() << "DIR_Name: " << re.DIR_Name;  //这里打印字符串其实越界了,不过RootEntry的reserve会终止打印,reserve初始化为0
    129             qDebug() << "DIR_Attr: " << re.DIR_Attr;
    130             qDebug() << "DIR_WrtDate: " << re.DIR_WrtDate;
    131             qDebug() << "DIR_WrtTime: " << re.DIR_WrtTime;
    132             qDebug() << "DIR_FstClus: " << re.DIR_FstClus;
    133             qDebug() << "DIR_FileSize: " << re.DIR_FileSize;
    134         }
    135     }
    136 }
    137 
    138 
    139 int main(int argc, char *argv[])
    140 {
    141     QCoreApplication a(argc, argv);
    142     QString img = "F:\data.img";
    143     Fat12Header f12;
    144 
    145     qDebug() << "Read Header:";
    146 
    147     PrintHeader(f12, img);
    148 
    149     qDebug() << endl;
    150 
    151     qDebug() << "Print Root Entry:";
    152 
    153     PrintRootEntry(f12, img);
    154 
    155     qDebug() << endl;
    156 
    157     return a.exec();
    158 }
    读取根目录区的目录项1

      

      打印结果中出现的 0 2 两项,是由于虚拟软盘 data.img 是在 FreeDos 中格式化,然后挂载到 linux 中写入文件导致的,是一些没有意义的垃圾数据

      改变打印格式,验证上面的说法:

      1 #include <QtCore/QCoreApplication>
      2 #include <QFile>
      3 #include <QDataStream>
      4 #include <QDebug>
      5 #include <QVector>
      6 #include <QByteArray>
      7 
      8 #pragma pack(push)
      9 #pragma pack(1)
     10 
     11 struct Fat12Header
     12 {
     13     char BS_OEMName[8];
     14     ushort BPB_BytsPerSec;
     15     uchar BPB_SecPerClus;
     16     ushort BPB_RsvdSecCnt;
     17     uchar BPB_NumFATs;
     18     ushort BPB_RootEntCnt;
     19     ushort BPB_TotSec16;
     20     uchar BPB_Media;
     21     ushort BPB_FATSz16;
     22     ushort BPB_SecPerTrk;
     23     ushort BPB_NumHeads;
     24     uint BPB_HiddSec;
     25     uint BPB_TotSec32;
     26     uchar BS_DrvNum;
     27     uchar BS_Reserved1;
     28     uchar BS_BootSig;
     29     uint BS_VolID;
     30     char BS_VolLab[11];
     31     char BS_FileSysType[8];
     32 };
     33 
     34 struct RootEntry
     35 {
     36     char DIR_Name[11];
     37     uchar DIR_Attr;
     38     uchar reserve[10];
     39     ushort DIR_WrtTime;
     40     ushort DIR_WrtDate;
     41     ushort DIR_FstClus;
     42     uint DIR_FileSize;
     43 };
     44 
     45 #pragma pack(pop)
     46 
     47 void PrintHeader(Fat12Header& rf, QString p)
     48 {
     49     QFile file(p);
     50 
     51     if( file.open(QIODevice::ReadOnly) )
     52     {
     53         QDataStream in(&file);
     54 
     55         file.seek(3);
     56 
     57         in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf));
     58 
     59         rf.BS_OEMName[7] = 0;
     60         rf.BS_VolLab[10] = 0;
     61         rf.BS_FileSysType[7] = 0;
     62 
     63         qDebug() << "BS_OEMName: " << rf.BS_OEMName;
     64         qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec;
     65         qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus;
     66         qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt;
     67         qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs;
     68         qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt;
     69         qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16;
     70         qDebug() << "BPB_Media: " << hex << rf.BPB_Media;
     71         qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16;
     72         qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk;
     73         qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads;
     74         qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec;
     75         qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32;
     76         qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum;
     77         qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1;
     78         qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig;
     79         qDebug() << "BS_VolID: " << hex << rf.BS_VolID;
     80         qDebug() << "BS_VolLab: " << rf.BS_VolLab;
     81         qDebug() << "BS_FileSysType: " << rf.BS_FileSysType;
     82 
     83         file.seek(510);
     84 
     85         uchar b510 = 0;
     86         uchar b511 = 0;
     87 
     88         in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510));
     89         in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511));
     90 
     91         qDebug() << "Byte 510: " << hex << b510;
     92         qDebug() << "Byte 511: " << hex << b511;
     93     }
     94 
     95     file.close();
     96 }
     97 
     98 RootEntry FindRootEntry(Fat12Header& rf, QString p, int i)
     99 {
    100     RootEntry ret = {{0}};
    101 
    102     QFile file(p);
    103 
    104     if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) )
    105     {
    106         QDataStream in(&file);
    107 
    108         file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry));
    109 
    110         in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret));
    111     }
    112 
    113     file.close();
    114 
    115     return ret;
    116 }
    117 
    118 void PrintRootEntry(Fat12Header& rf, QString p)
    119 {
    120     for(int i=0; i<rf.BPB_RootEntCnt; i++)
    121     {
    122         RootEntry re = FindRootEntry(rf, p, i);
    123 
    124         if( re.DIR_Name[0] != '' )
    125         {
    126             qDebug() << i << ":";
    127             qDebug() << "DIR_Name: " << hex << re.DIR_Name;   //采用十六进制打印输出
    128             qDebug() << "DIR_Attr: " << hex << re.DIR_Attr;
    129             qDebug() << "DIR_WrtDate: " << hex << re.DIR_WrtDate;
    130             qDebug() << "DIR_WrtTime: " << hex << re.DIR_WrtTime;
    131             qDebug() << "DIR_FstClus: " << hex << re.DIR_FstClus;
    132             qDebug() << "DIR_FileSize: " << hex << re.DIR_FileSize;
    133         }
    134     }
    135 }
    136 
    137 int main(int argc, char *argv[])
    138 {
    139     QCoreApplication a(argc, argv);
    140     QString img = "F:\data.img";
    141     Fat12Header f12;
    142 
    143     qDebug() << "Read Header:";
    144 
    145     PrintHeader(f12, img);
    146 
    147     qDebug() << endl;
    148 
    149     qDebug() << "Print Root Entry:";
    150 
    151     PrintRootEntry(f12, img);
    152 
    153     qDebug() << endl;
    154 
    155     return a.exec();
    156 }
    读取根目录区的目录项2

      

     2.  介绍 FAT 表

    2.1 FAT 表 - FAT12 的数据组织核心

      (1)FAT1 和 FAT2 是相互备份的关系,数据内容完全一致

      (2)FAT 表是一个关系图,记录了文件数据的先后关系

      (3)每一个 FAT 表项占用 12 比特

      (4)FAT 表的前 2 个表项规定不使用

    2.2 FAT 表中的先后关系

      (1)以簇(扇区)为单位存储文件数据

      (2)每个表项( vec[i] )表示文件数据的实际位置(簇)

        • DIR_FstClus 表示文件第 0 簇(扇区)的位置
        • vec[DIR_FstClus] 表示文件第 1 簇(扇区)的位置
        • vec[vec[DIR_FstClus]] 表示文件第 2 簇(扇区)的位置
        • ......

    2.3 FAT12 数据物理组织示意

      

    2.4 FAT12 数据逻辑组织示意

      

    2.5 实验:加载 FAT12 中的文件数据

      — 步骤:

      • 在根目录区查找目标文件对应的项
      • 获取目标文件的起始簇号和文件大小
      • 根据 FAT 表中记录的逻辑先后关系读取数据

    3. 小贴士

    3.1 小贴士一

      (1)FAT 表中的每个表项只占用 12 比特(1.5字节)

      (2)FAT 表一共记录了 BPB_BytsPerSec * 9 * 2 / 3 个表项

      (3)可以使用一个 short 表示一个表项的值

      (4)如果表象值大于等于 0xFF8 ,则说明已经到达最后一个簇

      (5)如果表项值等于 0xFF7 ,则说明当前簇已经损坏

    3.2 小贴士二

      (1)数据区起始簇()号为33,地址为 0x4200

      (2)数据区起始地址所对应的编号为 2(不为 0)

      (3)因此,DIR_FstClus 对应的地址为:

      • 0x4200 + ( DIR_FstClus - 2 ) * 512

    编程实验:读取指定文件内容】

    4. 小结

      (1)FAT12 根目录区记录了文件的起始簇号长度

      (2)通过查找根目录区能够确定是否存在目标文件

      (3)FAT12 文件数据的组织使用了单链表的思想

          文件数据离散的分布于存储介质中

          文件数据通过 FAT 项进行关联 

    注:本文整理于《狄泰12月提升计划》课程内容

    狄泰QQ群:199546072

    本人QQ号:502218614

  • 相关阅读:
    [翻译]TempDB剩余空间监视与纠错
    SQL Server 检查SQL连接错误问题的步骤
    SQL 根据父节点查询所有子节点
    ASP.NET 回滚事务
    SQL 根据子节点查询所有父节点
    .Net 夯实基础
    WCF学习笔记(六)WCF基础
    离下班还有几分钟,做个小玩意儿
    c#预处理指令
    .Net垃圾回收
  • 原文地址:https://www.cnblogs.com/shiwenjie/p/9021779.html
Copyright © 2011-2022 走看看