FAT16文件系统解析(C#版本)

简介: 今天FAT文件系统总算告一个段落了,已经可以非常完美的读取包含FAT16文件系统的磁盘了。由于是采用C#编写,直接借鉴的代码很少,并且考虑到MF不支持二进制序列化,所以对数据结构的解析,是一个一个字节进行的,所以很耗费时间。

今天FAT文件系统总算告一个段落了,已经可以非常完美的读取包含FAT16文件系统的磁盘了。由于是采用C#编写,直接借鉴的代码很少,并且考虑到MF不支持二进制序列化,所以对数据结构的解析,是一个一个字节进行的,所以很耗费时间。下面就是程序运行后的结果(可以识别物理磁盘及物理磁盘的分区)。
image.png

下图是用文件浏览器查看的结果。
image.png

磁盘系统的MDR和DBR信息如下。
image.png

为了便于后来者,把主要的结构声明代码罗列如下,希望有借鉴意义。

//基本类 [叶帆工作室] http://blog.csdn.net/yefanqiu/
public class DiskBase
{
    #region //MBR http://blog.csdn.net/yefanqiu/
    public struct PartitionTable
    {
        public byte BootFlag;            //分区活动标志 只能选00H和80H。80H为活动,00H为非活动
        public CHS StartCHS;             //分区开始的柱面、磁头、扇区  
        public byte SystemID;            //分区类型  01 FAT32  04 FAT16<32M  06 FAT16 07 HPFS/NTFS  05 0F 扩展分区
        public CHS EndCHS;               //分区结束的柱面、磁头、扇区  
        public UInt32 RelativeSectors;   //分区起始扇区数,指分区相对于记录该分区的分区表的扇区位置之差 (该分区表:LBA=0x0)。 
        public UInt32 TotalSectors;      //分区总扇区数 
    }
    public struct CHS
    {
        public byte Head;                //磁头
        public byte Sector;              //扇区 六位
        public UInt16 Cylinder;          //柱面 十位
    }
    public struct MBR
    {
        public byte[] bytBootCode;       //ofs:0.引导代码446字节 "FA 33 C0 8E D0 BC…"
        public PartitionTable[] PT;      //ofs:446.64个字节 分区表 length=4*16
        public UInt16 EndingFlag;        //ofs:510.结束标识:0xAA55。

        public MBR(byte[] bytData)
        {
            int i;
            bytBootCode = new byte[446];
            for (i = 0; i < 446; i++) bytBootCode[i] = bytData[i];

            PT = new PartitionTable[4];
            for (i = 0; i < 4; i++)
            {
                PT[i].BootFlag = bytData[446 + i * 16 + 0];
                PT[i].StartCHS.Head = bytData[446 + i * 16 + 1];
                PT[i].StartCHS.Sector = (byte)(bytData[446 + i * 16 + 2] & 0x3f);
                PT[i].StartCHS.Cylinder = (UInt16)(((bytData[446 + i * 16 + 2] & 0xc0) << 2) | bytData[446 + i * 16 + 3]);
                PT[i].SystemID = bytData[446 + i * 16 + 4];
                PT[i].EndCHS.Head = bytData[446 + i * 16 + 5];
                PT[i].EndCHS.Sector = (byte)(bytData[446 + i * 16 + 6] & 0x3f);
                PT[i].EndCHS.Cylinder = (UInt16)(((bytData[446 + i * 16 + 6] & 0xc0) << 2) | bytData[446 + i * 16 + 7]);
                PT[i].RelativeSectors = (UInt32)(bytData[446 + i * 16 + 11] << 24 | bytData[446 + i * 16 + 10] << 16 | bytData[446 + i * 16 + 9] << 8 | bytData[446 + i * 16 + 8]);
                PT[i].TotalSectors = (UInt32)(bytData[446 + i * 16 + 15] << 24 | bytData[446 + i * 16 + 14] << 16 | bytData[446 + i * 16 + 13] << 8 | bytData[446 + i * 16 + 12]);
            }
            EndingFlag = (UInt16)(bytData[510] << 8 | bytData[511]);
        }

    #endregion        

    #region //DBR  http://blog.csdn.net/yefanqiu/
    //系统引导记录(兼容FAT16和FAT32)
    public struct DBR
    {
        public byte[] BS_JmpBoot;            //ofs:0.典型的如:0xEB,0x3E,0x90。
        public byte[] BS_OEMName;            //ofs:3.典型的如:“MSWIN4.1”。 
        public UInt16 BPB_BytsPerSec;        //ofs:11.每扇区字节数。 
        public byte BPB_SecPerClus;          //ofs:13.每簇扇区数。 
        public UInt16 BPB_RsvdSecCnt;        //ofs:14.保留扇区数,从 DBR到 FAT的扇区数。
        public byte BPB_NumFATs;             //ofs:16.FAT的个数。 
        public UInt16 BPB_RootEntCnt;        //ofs:17.根目录项数。 
        public UInt16 BPB_TotSec16;          //ofs:19.分区总扇区数(<32M时用)。 
        public byte BPB_Media;               //ofs:21.分区介质标识,优盘一般用 0xF8。 
        public UInt16 BPB_FATSz16;           //ofs:22.每个 FAT占的扇区数。 
        public UInt16 BPB_SecPerTrk;         //ofs:24.每道扇区数。 
        public UInt16 BPB_NumHeads;          //ofs:26.磁头数。 
        public UInt32 BPB_HiddSec;           //ofs:28.隐藏扇区数,从 MBR到 DBR的扇区数。 
        public UInt32 BPB_TotSec32;          //ofs:32.分区总扇区数(>=32M时用)。

        //---------------------
        //FAT32特有
        public UInt32 BPB_FATSz32;          //ofs:36.每个 FAT占的扇区数。
        public UInt16 BPB_ExtFlags;         //ofs:40.FAT标志
        public UInt16 BPB_FSVer;            //ofs:42.版本号 高字节主版本 低字节次版本号
        public UInt32 BPB_RootClus;         //ofs:44.根目录所在第一个簇的簇号,通常该数值为2,但不是必须为2。
        public UInt16 BPB_FSInfo;           //ofs:48.保留区中FAT32 卷FSINFO 结构所占的扇区数,通常为1。
        public UInt16 BPB_BkBootSec;        //ofs:50.如果不为0,表示在保留区中引导记录的备份数据所占的扇区数,通常为6。同时不建议使用6 以外的其他数值。
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public byte[] BPB_Reserved;         //ofs:52.备用     

        //---------------------
        public byte BS_drvNum;              //ofs:64/36.软盘使用 0x00,硬盘使用 0x80。 
        public byte BS_Reserved1;           //ofs:65/37.保留。 
        public byte BS_BootSig;             //ofs:66/38.扩展引导标记:0x29。 
        public byte[] BS_VolID;             //ofs:67/39.盘序列号。
        public byte[] BS_VolLab;            //ofs:71/43.“Msdos      ”。 
        public byte[] BS_FilSysType;        //ofs:82/54.“FAT32   ”。 
        public byte[] ExecutableCode;       //ofs:90/62.引导代码。 
        public UInt16 EndingFlag;           //ofs:510.结束标识:0xAA55。

        //---------------------
        //0-未知 1-FAT12 2-FAT16 3-FAT32 其它值为未知
        public byte FATType;

        //获取信息
        public DBR(byte[] bytData)
        {
            FATType = IsType(bytData);
            int i;
            BS_JmpBoot = new byte[3];
            for (i = 0; i < 2; i++) BS_JmpBoot[i] = bytData[i];
            BS_OEMName = new byte[8];
            for (i = 0; i < 8; i++) BS_OEMName[i] = bytData[i + 3];
            BPB_BytsPerSec = (UInt16)(bytData[12] << 8 | bytData[11]);
            BPB_SecPerClus = bytData[13];
            BPB_RsvdSecCnt = (UInt16)(bytData[15] << 8 | bytData[14]);
            BPB_NumFATs = bytData[16];
            BPB_RootEntCnt = (UInt16)(bytData[18] << 8 | bytData[17]);
            BPB_TotSec16 = (UInt16)(bytData[20] << 8 | bytData[19]);
            BPB_Media = bytData[21];
            BPB_FATSz16 = (UInt16)(bytData[23] << 8 | bytData[22]);
            BPB_SecPerTrk = (UInt16)(bytData[25] << 8 | bytData[24]);
            BPB_NumHeads = (UInt16)(bytData[27] << 8 | bytData[26]);
            BPB_HiddSec = (UInt32)(bytData[31] << 24 | bytData[30] << 16 | bytData[29] << 8 | bytData[28]);
            BPB_TotSec32 = (UInt32)(bytData[35] << 24 | bytData[34] << 16 | bytData[33] << 8 | bytData[32]);
            //----------
            if (FATType == 3)
            {
                //FAT32
                BPB_FATSz32 = (UInt32)(bytData[39] << 24 | bytData[38] << 16 | bytData[37] << 8 | bytData[36]);
                BPB_ExtFlags = (UInt16)(bytData[41] << 8 | bytData[40]);
                BPB_FSVer = (UInt16)(bytData[43] << 8 | bytData[42]);
                BPB_RootClus = (UInt32)(bytData[47] << 24 | bytData[46] << 16 | bytData[45] << 8 | bytData[44]);
                BPB_FSInfo = (UInt16)(bytData[49] << 8 | bytData[48]);
                BPB_BkBootSec = (UInt16)(bytData[51] << 8 | bytData[50]);
                BPB_Reserved = new byte[12];
                for (i = 0; i < 12; i++) BPB_Reserved[i] = bytData[i + 52];
                //----------
                BS_drvNum = bytData[64];
                BS_Reserved1 = bytData[65];
                BS_BootSig = bytData[66];
                BS_VolID = new byte[4];
                for (i = 0; i < 4; i++) BS_VolID[i] = bytData[67 + i];
                BS_VolLab = new byte[11];
                for (i = 0; i < 11; i++) BS_VolLab[i] = bytData[71 + i];
                BS_FilSysType = new byte[8];
                for (i = 0; i < 8; i++) BS_FilSysType[i] = bytData[82 + i];
                ExecutableCode = new byte[420];
                for (i = 0; i < 420; i++) ExecutableCode[i] = bytData[90 + i];
            }
            else
            {
                //FAT16
                BS_drvNum = bytData[36];
                BS_Reserved1 = bytData[37];
                BS_BootSig = bytData[38];
                BS_VolID = new byte[4];
                for (i = 0; i < 4; i++) BS_VolID[i] = bytData[39 + i];
                BS_VolLab = new byte[11];
                for (i = 0; i < 11; i++) BS_VolLab[i] = bytData[43 + i];
                BS_FilSysType = new byte[8];
                for (i = 0; i < 8; i++) BS_FilSysType[i] = bytData[54 + i];
                ExecutableCode = new byte[448];
                for (i = 0; i < 448; i++) ExecutableCode[i] = bytData[62 + i];

                //FAT32
                BPB_FATSz32 = 0;
                BPB_ExtFlags = 0;
                BPB_FSVer = 0;
                BPB_RootClus = 0;
                BPB_FSInfo = 0;
                BPB_BkBootSec = 0;
                BPB_Reserved = new byte[12];
            }
            //----------
            EndingFlag = (UInt16)(bytData[510] << 8 | bytData[511]);
        }
    #endregion

    //文件系统判断(采用微软的判断方法)        
    public static byte IsType(byte[] bytData)
    {
        //不是合法BPB扇区数据
        if (bytData[510] != 0x55 || bytData[511] != 0xaa) return 0;

        //跳转指令不合法
        if (bytData[0] != 0xeb && bytData[0] != 0xe9) return 0;

        //每扇区包含的字节数(一般为512个字节)
        UInt16 BPB_BytsPerSec = (UInt16)(bytData[12] << 8 | bytData[11]);

        //仅处理512个字节的扇区
        if (BPB_BytsPerSec != 512) return 0;

        //每簇扇区数
        byte BPB_SecPerClus = bytData[13];
        //保留扇区数
        UInt16 BPB_RsvdSecCnt = (UInt16)(bytData[15] << 8 | bytData[14]);
        //FAT表的个数
        byte BPB_NumFATs = bytData[16];

        //FAT表的个数必须为2
        if (BPB_NumFATs != 2) return 0;

        //根目录项数(32字节为单位)
        UInt16 BPB_RootEntCnt = (UInt16)(bytData[18] << 8 | bytData[17]);
        //分区总扇区数(<32M时用)
        UInt16 BPB_TotSec16 = (UInt16)(bytData[20] << 8 | bytData[19]);
        //每个FAT占的扇区数
        UInt16 BPB_FATSz16 = (UInt16)(bytData[23] << 8 | bytData[22]);
        //分区总扇区数(>=32M时用)
        UInt32 BPB_TotSec32 = (UInt32)(bytData[35] << 24 | bytData[34] << 16 | bytData[33] << 8 | bytData[32]);
        //每个FAT占的扇区数(FAT32)
        UInt32 BPB_FATSz32 = (UInt32)(bytData[39] << 24 | bytData[38] << 16 | bytData[37] << 8 | bytData[36]);

        UInt64 FATSz = 0, TotSec = 0, DataSec = 0;
        UInt64 RootDirSectors = (UInt64)(((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec);

        if (BPB_FATSz16 != 0)
            FATSz = BPB_FATSz16;
        else
            FATSz = BPB_FATSz32;

        if (BPB_TotSec16 != 0)
            TotSec = BPB_TotSec16;
        else
            TotSec = BPB_TotSec32;

        DataSec = TotSec - (BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors);
        UInt64 CountofClusters = DataSec / BPB_SecPerClus;
        if (CountofClusters < 4085)
        {
            /* FAT 类型是FAT12 */
            return 1;
        }
        else if (CountofClusters < 65525)
        {
            /* FAT 类型是FAT16 */
            return 2;
        }
        else
        {
            /* FAT 类型是FAT32*/
            return 3;
        }
    }           
}
相关文章
|
2月前
|
XML 前端开发 C#
C#编程实践:解析HTML文档并执行元素匹配
通过上述步骤,可以在C#中有效地解析HTML文档并执行元素匹配。HtmlAgilityPack提供了一个强大而灵活的工具集,可以处理各种HTML解析任务。
184 19
|
6月前
|
存储 算法 安全
如何控制上网行为——基于 C# 实现布隆过滤器算法的上网行为管控策略研究与实践解析
在数字化办公生态系统中,企业对员工网络行为的精细化管理已成为保障网络安全、提升组织效能的核心命题。如何在有效防范恶意网站访问、数据泄露风险的同时,避免过度管控对正常业务运作的负面影响,构成了企业网络安全领域的重要研究方向。在此背景下,数据结构与算法作为底层技术支撑,其重要性愈发凸显。本文将以布隆过滤器算法为研究对象,基于 C# 编程语言开展理论分析与工程实践,系统探讨该算法在企业上网行为管理中的应用范式。
188 8
|
6月前
|
存储 监控 算法
解析公司屏幕监控软件中 C# 字典算法的数据管理效能与优化策略
数字化办公的时代背景下,企业为维护信息安全并提升管理效能,公司屏幕监控软件的应用日益普及。此软件犹如企业网络的 “数字卫士”,持续记录员工电脑屏幕的操作动态。然而,伴随数据量的持续增长,如何高效管理这些监控数据成为关键议题。C# 中的字典(Dictionary)数据结构,以其独特的键值对存储模式和高效的操作性能,为公司屏幕监控软件的数据管理提供了有力支持。下文将深入探究其原理与应用。
162 4
|
7月前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
197 2
visual studio 2022 社区版 c# 环境搭建及安装使用【图文解析-小白版】
这篇文章提供了Visual Studio 2022社区版C#环境的搭建和安装使用指南,包括下载、安装步骤和创建C#窗体应用程序的详细图文解析。
visual studio 2022 社区版 c# 环境搭建及安装使用【图文解析-小白版】
|
8月前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
148 4
|
编译器 C# 开发者
C# 9.0 新特性解析
C# 9.0 是微软在2020年11月随.NET 5.0发布的重大更新,带来了一系列新特性和改进,如记录类型、初始化器增强、顶级语句、模式匹配增强、目标类型的新表达式、属性模式和空值处理操作符等,旨在提升开发效率和代码可读性。本文将详细介绍这些新特性,并提供代码示例和常见问题解答。
278 7
C# 9.0 新特性解析
|
11月前
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。
|
C# 开发者
C# 10.0 新特性解析
C# 10.0 在性能、可读性和开发效率方面进行了多项增强。本文介绍了文件范围的命名空间、记录结构体、只读结构体、局部函数的递归优化、改进的模式匹配和 lambda 表达式等新特性,并通过代码示例帮助理解这些特性。
234 2
|
JavaScript 前端开发 索引
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
458 4

热门文章

最新文章

推荐镜像

更多
  • DNS
  • 下一篇
    oss云网关配置