• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья ASM. FAT32 – описание файловой системы

FAT датируется аж 1977-годом и пришла к нам из проприетарного DOS. Благодаря простой реализации, сейчас это способ хранения данных на таких носителях как USB-брелки, карты-памяти телефонов и ЦФК, в EFI для хранения кода загрузчика, в системах реального времени RTOS, и многое другое. В данной статье рассматриваются гендерные признаки зоопарка FAT, и в какую сущность превратила эволюция конкретно взятую FAT32. Детальный её разбор позволит понять алгоритм восстановления удалённых файлов, а так-же подтолкнёт на создание своих утилит форматирования, с широким спектром логической геометрии. Из инструментов понадобится любой (способный работать с дисками) HEX-редактор типа WinHex или HxD, штатный калькулятор Win, и от Microsoft.

Logo.png



Оглавление:

1. Вводная часть;
2. Загрузочная запись и блок BPB – Bios Parametr Block;
3. Таблицы FAT – File Allocation Table;
4. Корневой каталог "RootDir";
5. Практика – разбор служебных структур;
6. Заключение.



1. Вводная часть

В основе проектирования операционных систем лежит работа с информацией. Здесь перед инженерами встают три задачи: во-первых определиться с типом накопителей (HDD, SSD, Flash), чтобы предоставить системе соответствующий драйвер. Второй момент – это способ хранения данных в виде файловых систем Ext, HPFS, FAT, NTFS и прочие. Но что ещё немаловажно, это предусмотреть возможность восстановления инфы в случае критических сбоев. Последний вопрос стоит ребром, что побудило Microsoft создать до блеска отшлифованную NTFS (New-Technology-File-System). Её мы не будем пока трогать, и отправив на скамейку запасных, рассмотрим только FAT.

1.1. Иерархия: диск[сектор] + том[кластер]

Для начала разберём геометрию накопителей.

На нижнем уровне, производитель делит всю поверхность на 512-байтные сектора (отсчёт с единицы). Но утилиты и ОС могут предоставлять нам секторы > 512-байт, однако они по-любому будут кратны 512, например 1024-байт. Сектор – мин.порция обмена с диском, мы не сможем прочитать с него кол-во байт не кратное 512. Это нужно учитывать при вызове функций Read/WriteFile().

Самый первый сектор всегда отводится под MBR (MasterBootRecord), где указывается объём накопителя, и смещение к его логическому тому. Вместо устаревшей трехмерной CHS с лимитом в 8 ГБ, в современном мире используется уже линейный подход LBA48 (48-bit Logical Block Address), в которой секторы нумеруются с нуля, а не как прежде с единицы. LBA позволяет адресовать диск размером 128 ПетаБайт, при размере блока 512.

ОС не может хранить файлы на дисках без логических разделов (том, volume, макс=26 с литерами от А: до Z:). Значит нужно создать этот том, и установить на него какую-нибудь файловую систему (далее FS), чтобы ОС знала о порядке расположения файлов – эта операция известна как форматирование. Бинарные файлы можно сбрасывать и на "сырой" RAW-том, только за доступ к ним будем отвечать уже сами. Такой изврат применяется редко, например при работе с дисками без поддержки их со-стороны ОС (см.прерывание BIOS int-13h).

Допустим, имеем первый раздел C:\. Его смещение от начала диска указывается в секторе(1) MBR. У всех накопителей, первый раздел начинается как-минимум с сектора(63). Если учесть, что непосредственно данные и код MBR занимают всего 1-сектор, следовательно перед началом тома в загашнике остаётся аж 62 свободных, а это: 62*512=31 КБ пространства, где можно скрыть от FS конфиденциальную инфу. Важно понять, что FS устанавливается не на диск, а на его логический том, поэтому драйвер считает началом тома LBA(0), хотя в глазах биоса это сектор(63).

Такой расклад создаёт путаницу, но достаточно запомнить, что функцией CreateFile() можно открыть как физ.диск (тогда LBA(0) указывает на его начало), а можно запросить и логический том диска, например по букве C:\. В этом случае LBA(0) считается уже указателем на начало раздела. То-есть всё зависит от того, какой объект мы открываем.

Каждая запись в таблице FAT указывает на определённый блок-информации в области данных. Если указателями адресовать мелкие секторы диска, то во-первых нужно будет увеличить разрядность этих указателей до 64-бит (т.к. диски могут быть исполинских размеров), а во-вторых потребуется огромное кол-во самих линков, что повлияет на размер таблицы. Файлы до 512-байт встречаются редко, а значит нет смысла искать их в каждом секторе. По этой причине, несколько секторов всегда собираются в "кластер", что позволяет увеличить полезное пространство, за счёт уменьшения таблицы FAT.

C дефолтными настройками, все FS создают кластеры размером 4096-байт, а это 8-секторов. В служебной таблице FAT лежат теперь указатели не на секторы, а на кластеры, и соответственно размер её сокращается в 8-раз! Но помимо плюсов есть и минусы.. Если в txt-файле у нас 11-байтная строка "Hello World", на диске этот файл будет занимать 4 КБ, т.к. мин.блоком хранения данных уже получается кластер. Зайдите в свойства любого файла и посмотрите, какой он имеет реальный размер, и сколько ему выделено на диске:


Hello.png


Как видим – картина удручающая, и при кластерном распределении у нас улетает в трубу огромная часть пространства. Но говорят, "случaйнaя удaчa принocит бoльшe рaдocти, чeм зaкoнoмeрный уcпex", и видимо инженеры с этим согласны. Здесь они вынуждены сесть на шпагат и только надеяться, что большинство файлов юзера будут размером больше 4КБ. Исключительно в этом случае экономия на размере таблицы оправдает себя.

Чтобы предотвратить суицид крайне впечатлительных юзверей, инженеры построили себе хату-с-краю, и предложили нам самостоятельно выбирать кол-во секторов в кластере – данный параметр имеется у большинства приличных утилит форматирования и называется "SectorPerCluster". Если у вас много мелких файлов и вы считаете 4КБ для хранения каждого из них слишком расточительным, то всегда можете изменить размер кластера в диапазоне от 512-байт, до 64КБ. Вот скрин одной из таких утилит "Fat32format":


Fat32Format.png



1.2. Организация FAT – вид сверху

На рис.ниже представлены основные отличия FAT12/16, от более совершенной FAT32.
Здесь видно, что раньше, в системной области четыре служебные структуры располагались плотно друг-к-другу: это блок параметров BPB с адресом LBA(0) (не путать с MBR), далее основная таблица FAT, следом её резервная копия FAT2, и в хвосте корневой каталог. Размер таблицы зависит от общего кол-ва секторов в накопителе. Очевидно, что чем больше размер диска, тем больше в нём кластеров, и соответственно больше указателей на кластеры в таблице FAT.

Теперь посмотрим на организацию FAT32 справа..
Корневой каталог уже съехал с насиженных мест и может находится где угодно. Драйвер FS считает его обычным файлом с доступом на запись, что даёт возможность каталогу RootDir расширяться динамически. Кроме того, блок BPB разбух с одного до трёх секторов, хотя в FSInfo нет ничего интересного, а последний отправлен в резерв для наших потомков. Причём имеется и зеркало ВРВ, всегда по адресу LBA(6). В доке на FAT32 эту область назвали "Резервные сектора", сразу после которых расположилась уже таблица FAT со-своей копией. Благодаря такой организации, драйвер FS может восстанавливать теперь не только таблицу FAT, но и загрузочный сектор ОС в блоке параметров BPB.


FAT_16_32.png



2. Загрузочная запись и блок BPB – Bios Parametr Block

В самом начале тома с адресом LBA(0), во всех версиях FAT лежит блок параметров биос ВРВ. Он занимает ровно один 512-байтный сектор и содержит в себе базовые параметры файловой системы, а так-же код загрузчика ОС с сигнатурой 0x55AA. Запустим HEX-редактор и через меню "Инструменты" откроем в нём свою флэш – вот как это выглядит у меня:


FAT_BPB.png


Загрузчик нас не интересует, ..более того, если Flash не загрузочная, Boot-block вообще забит нулями, и это часто практикуется (например на microSD телефонов). Зато ВРВ критически важен для драйвера, посколько именно в нём зарыта инфа о расположении оставшихся трёх/системных структур – это парочка FAT, и корневой каталог RootDir. Для программного доступа к этому блоку я создал инклуд, и поместил в него структуру ВРВ с описанием каждого поля. Здесь-же перечислю только те поля, которые откроют нам доступ к FAT и корневому каталогу:


C-подобный:
struct BPB    ;//<------------- BIOS Parameter Block
  JmpBoot         rb  3     ;// JMP на загрузчик (0xEB5890)
  OemName         rb  8     ;// <----- строка форматера ОС
  BytePerSector   dw  0     ;// Байт в секторе
  SecPerCluster   db  0     ;// Секторов к кластере
  RsvdSecCounter  dw  0     ;// Резервная область в секторах
  NumFats         db  0     ;// Сколько копий FAT-таблицы
  RootEntCnt      dw  0     ;// Объектов в корневом каталоге (нуль для FAT-32)
  TotSec16        dw  0     ;// Всего секторов на диске (нуль для FAT-32)
  Media           db  0     ;// Тип диска (F8)
  FATSz16         dw  0     ;// Размер таблицы FAT-16 в секторах (нуль для FAT-32)
  SecPerTrk       dw  0     ;// Секторов в дорожке
  NumHeads        dw  0     ;// Всего головок Head
  HiddSec         dd  0     ;// Cекторов перед началом раздела
  TotSec32        dd  0     ;// Всего секторов на диске
ends

struct BPB_32  ;//<------------ FAT-32
  Header          BPB       ;// Заголовок (одинаковый для FAT16/32)
  FATSz32         dd  0     ;// Размер таблицы FAT-32 в секторах
  ExtFlags        dw  0     ;// Флаги
  FSVer           dw  0     ;// Версия файловой системы
  RootCluster     dd  0     ;// Кластер корневого каталога
  FSInfo          dw  0     ;// Сектор структуры FSINFO
  BkBootSec       dw  0     ;// Сектор копии этой записи (6)
  Reserved        rb  12    ;//
  DrvNum          db  0     ;// Номер диска для INT-13h (00 или 80h)
  Reserved1       db  0     ;//
  BootSig         db  0     ;// Сигнатура 29h, если имеются сл.три поля
  VolID           dd  0     ;// Серийник тома
  VolLabel        rb  11    ;//<----- Строка с меткой тома
  FilSysType      rb  08    ;//<----- Строка "FAT32   ".
  Reserved2       rb  420   ;//
  Signature       dw  0     ;// 55AAh
ends

FAT_BPB_Decode.png


В спеке на FAT32 от Microsoft говорится, что положение корневого каталога "RootDir" строго не регламентируется, и он может лежать в произвольном кластере области данных. Однако на практике, Root в моих тестах всегда расположен сразу после двух таблиц FAT, т.е. адрес его фиксирован. Не знаю, может и есть в природе отбившиеся от стада утилиты форматирования, но лично мне таковые ещё не встречались.

В любом случае, найти каталог можно по приведённой выше формуле, и если это штатный форматер Win (как и большинство других), то вторая часть формулы (RootCluster-2) * SectorPerCluster будет всегда возвращать нуль. Тогда получается, что каталог лежит сразу после резервных секторов + размеры двух таблиц FAT (см.первый скрин FAT16). А вообще, жить без идеологии и надеяться на случай не есть гуд, а потому лучше использовать полную формулу, тем-более что мягкие уже грозно предупредили нас об этом.


3. Таблица FAT – File Allocation Table

Теперь выйдем из матрицы, и поговорим о более насущном..
Организация любой файловой системы – невероятно сложный механизм. Однако в случае с FAT, это утверждение сильно притянуто за-уши. Именно благодаря свой простоте она дожила до наших дней и не собирается уходить со-сцены, а наоборот мутировала в 64-битную ExFAT.

Значит чтобы получить указатель на таблицу, нужно прочитать поле "RsvdSectorCount" структуры ВРВ. В данном случае там прописан сектор(1000), и если умножить его на 512, получим смещение в байтах от начала тома = 0x0007D000. Далее, читаем из той-же ВРВ поле "FATSz32" с размером, и передаём оба полученных значения функции ReadFile(). Так, в приёмном буфере функции получим дамп таблицы FAT, для её детального разбора.

Таблица – это массив указателей на кластеры, без каких-либо служебных полей. Отсчёт кластеров в массиве начинается с нуля, а разрядность указателей 32-бит (отсюда и название FAT). Старшие 4-бита отправлены в резерв, остаётся 28. Тогда получается, что FAT32 может адресовать всего: 2^28=268.435.456 кластеров, и если размер каждого из них 4096-байт, то макс.объём диска = 1ТБ. Однако это теоретический предел, ведь всегда можно увеличить размер кластера в 2,4,8 и даже в 16-раз, при этом оставив 28-битные линки. Так-что на практике становится доступным диск 16ТБ, а если требуется больше, нужно переходить на ExFAT, в которой разрядность указателей расширена до 64-бит.

На рисунке ниже представлен фрагмент таблицы FAT (для удобства, в меню "Вид" сгруппируйте отображение по 4-байта).

• Два/первых элемента массива всегда равны 0x0FFFFFF8 + 0xFFFFFFFF, и не используются в качестве указателей на кластеры – это просто сигнатура таблицы, а байт(F8) определяет "MediaType" (варианты: F0=Floppy, F8=Hard, FA=Ram). Когда блок ВРВ и его резервная копия безвозвратно утеряны (бэд-сектор или вирь), по данной сигнатуре можно будет найти таблицу, чтобы попытаться спасти информацию.


FAT_Entry.png


• 32-битные элементы начиная со-смещения(8), отображают уже состояние соответствующих кластеров в области-данных диска. Он может быть свободным(0), выделенным(2), зарезервированным(F6) или повреждённым(F7). Элемент с макс.значением 0x0FFFFFFF является маркером последнего кластера файла EOC (EndOfCluster). Если файл большой и занимает несколько кластеров, указатели связываются в цепочку до тех пор, пока не встретится этот маркер (выделены красным).

• Древовидная структура расположения файлов на диске, начинается с корневого каталога – мы не видим его среди привычных нам папок, но он присутствует всегда. Ему назначается имя в виде "метки тома", которую запрашивает у нас система, при форматировании раздела. Именно поэтому второй элемент таблицы по смещению(8) имеет маркер окончания 0x0FFFFFFF, т.к. под корневой каталог отводится всего один кластер(2) (см.структуру ВРВ-->RootCluster).

Чтобы продемонстрировать организацию таблицы FAT, я форматнул свою флэш и сбросил на неё всего два файла. Первый размером 7.4КБ, поэтому занимает на диске два 4К-байтных кластера (выделен зелёным), а второй размером 20.2КБ и как видим, загребает 5 (выделенных синим) кластеров. Далее следуют null-элементы, которые информируют о свободном пространстве.

Обратите внимание, что после кластера(2), следующий элемент таблицы указывает сразу на кластер 0х00000004. Это-же касается и записи после маркера EOC по смещению(14h) – перепрыгнув через один, она линкует сразу на кластер(6). Здесь возникает вопрос: куда подевались кластеры 3 и 5, ведь значения 0х0FFFFFFF это не указатели, а просто маркеры окончания цепочки?

Дело в том, что в таблице FAT хранятся указатели на следующие кластеры файла, а первый – прописывается в корневом каталоге RootDir, где лежит полный паспорт файла. То-есть когда файл меньше 4096-байт, выделенный под него первый кластер указывается в корневом каталоге, а в таблице получим лишь связанный с ним EOC. Но если файл выходит за границы одного кластера, то следующие указываются уже в таблице FAT, пока не встретится маркер окончания.


4. Корневой каталог "RootDirectory"

Файловая система требует базы, где хранилась-бы информация о всех, существующих на диске, файлах. Для FAT32(16) такой базой является "корневой каталог". Чтобы описать свойства одного файла, инженерам потребовался блок размером в 32-байта, где можно найти: имя файла/каталога в формате 8.3, его размер, дату создания, атрибуты, а так-же номер первого кластера, с которого начинается данный файл. Если-же хвост файла торчит снаружи выделенного кластера, номер следующего указывается уже в таблице FAT. Иначе, в таблице будет прописан только маркер окончания EOC. Таким образом, таблица и корневой-каталог логически связаны между собой, и потеря любого из них приведёт к краху всей FS.

В доках, 32-байтную запись каталога назвали SFN, что подразумевает "Short File Name". Короткая (short) она потому, что хранит имя в досовском формате(8.3). Но в современном мире мы уже отвыкли от всевозможных рамок, и предпочитаем для файлов более длинные имена, которые Win всё-же ограничивает до 256-символов. Поэтому в FAT предусмотрена родственная к SFN структура LFN (Long) – она имеет такой-же размер 32-байт, но является не самостоятельной, а дополнением к SFN. Если в имени файла меньше 8-ми символов, структура LFN может вообще отсутствовать, но SFN в наличии всегда.

Нужно признать, что организация длинных имён в FAT выполнена через известное место. Так, каждая структура LFN несёт в себе макс. 13-символов имени файла, а если их больше, то добавляется ещё одна структура и получаем 26-символов. Если-же и этого мало, то подключается сл.структура и т.д. Причём имя задаётся в Unicode (где на каждый символ расходуются по 2-байта) и внутри одной структуры разбросано по трём/разным полям. Более того, порядок следования цепочки структур обратный – т.е. сначала идёт последняя LFN(x), после неё LFN(x-1) и далее в том-же духе. Общее кол-во структур указывается в первом байте "Ordinal". Здесь становится очевидным, что разработчик явно был под веществами.


C-подобный:
;//*********************************************************************
;// Время: Биты[4:0]= пары секунд (0-29), [10:5]= минуты, [15:11]= часы
;// Дата:  Биты[4:0]= день, [8:5]= месяц, [15:9]= год начиная с 1980
;//*********************************************************************

struct SFN     ;//<----------- 32-байта под описатель файла (DirEntry)
  DIR_Name        rb  11   ;// 00 = Имя 8.3
  DIR_Attr        db  0    ;// 0B = см.ATTR_xx
  DIR_NTRes       db  0    ;// 0C = Резерв
  DIR_CrtTimeMs   db  0    ;// 0D = Сотые времени создания файла
  DIR_CrtTime     dw  0    ;// 0E = Время создания файла в 2-сек.
  DIR_CrtDate     dw  0    ;// 10 = Дата создания
  DIR_LstAccDate  dw  0    ;// 12 = Дата посл.доступа
  DIR_FstClusHi   dw  0    ;// 14 = Первый кластер файла/каталога (High)
  DIR_WrtTime     dw  0    ;// 16 = Время записи
  DIR_WrtDate     dw  0    ;// 18 = Дата записи
  DIR_FstClusLo   dw  0    ;// 1A = Первый кластер файла/каталога (Low)
  DIR_FileSize    dd  0    ;// 1С = Размер файла
ends

struct LFN     ;//<----------- 32-байта под длинное имя файла
  LDIR_Ord        db  1    ;// Маска 40h + кол-во структур LFN
  LDIR_Name       rb  10   ;// Символы 1-5 Unicode имени
  LDIR_Attr       db  0    ;// см.ATTR_xx
  LDIR_Type       db  0    ;// Резерв
  LDIR_Chksum     db  0    ;// CRC
  LDIR_Name2      rb  12   ;// Символы 6-11
  LDIR_FstClusLo  dw  0    ;// Резерв
  LDIR_Name3      rb  4    ;// Символы 12-13
ends

  ATTR_READ_ONLY   =  0x01
  ATTR_HIDDEN      =  0x02
  ATTR_SYSTEM      =  0x04
  ATTR_VOLUME_ID   =  0x08
  ATTR_LONG_NAME   =  0x0F
  ATTR_DIRECTORY   =  0x10
  ATTR_ARCHIVE     =  0x20

Выше упоминалось, что LBA каталога можно найти по формуле из ВРВ:
RsvdSecCount + (FATSz*2) + ((RootCluster-2) * SecPerCluster)

Для своей флэш я получил LBA=16384, и прыгнув на него в редакторе, попал в начало каталога.
Проясним некоторые моменты..

1. Первая структура SFN (синий блок) будет всегда описывать корневую папку диска. Это единственная структура, которой может быть присвоен атрибут "VolumeID" со-значением 08h. Атрибуты файлов/каталогов лежат по смещению(0Вh) от начала структур, и я заключил их в синий овал. Все остальные поля первой структуры SFN считаются не действительными!
2. На моей флэш всего два файла с именами "dCrypt.asm" и "usbVIDbase.txt". Чем руководствуется драйвер FAT при добавлении к SFN структуры LFN остаётся загадкой, поскольку имя первого файла меньше 8-ми символов, но дров всё-равно вставил LFN (см.красный блок). А вот имя второго файла уже 10-символов, и для него выделено две LFN в зелёном блоке. Обратите внимание, что у первой в поле-ординала лежит значение(1) (+40h маска), а у второй LFN ординал равен(2). Эти поля я выделил чёрным, и они определяют кол-во структур LFN для текущего файла. Записи LFN можно обнаружить по атрибуту(0F).

FAT_RootDir.png


Из чёрной таблицы с расшифровкой полей видно, что номер первого кластера разделён на две части Hi/Low. Первый файл "dCrypt.asm" размером 7.4КБ и начинается с кластера(3). Но поскольку он требует два 4К-байтных, следующий указывается уже в таблице FAT (см.зелёный dword на предыдущем скрине). Здесь нужно отметить, что номера кластеров представляются относительно начала области-данных, которая совпадает с началом рассматриваемого корневого-каталога RootDir.

Так как-же найти злополучный файл на диске?
Для этого в спеке FAT32 приведена формула такого вида: FileFirstCluster = RootDir + (SFN.FstClus - RootCluster)
Если учесть, что в стандартной реализации FAT32 поле "RootCluster" в структуре ВРВ всегда равно 2, то для первого файла получаем следующий сектор LBA:


Код:
RootDir       = сектор 16384
SFN.FstClus   = кластер(3)
Формула: 3-2  = кластер(1) = 8 секторов
-----------------------------------------
FileFirstClus = 16384 + 8 = сектор(16392)

FAT_File.png



4.1. Алгоритм поиска удалённых файлов

Когда мы удаляем файл с диска FAT, он фактически остаётся на своём месте и так-же занимает свой кластер. Просто в соответствующей структуре SFN, первому байту присваивается маркер(Е5). Аналогичную ситуацию наблюдаем и в случае "Быстрого форматирования" тома. Такой расклад способствует восстановлению удалённых файлов. Чтобы создать их список, достаточно в корневом-каталоге с шагом в 32, проверять каждый/первый байт на маркер(Е5).

В качестве доказательства, я удалил со-своей флэш "usbVIDbase.txt", а второй файл оставил. В результате, SFN удалённого файла (и две предшествующие ей LFN) приобрели вид как на скрине ниже, а причастные к файлу элементы таблицы FAT сбросились в нуль. т.е. кластеры освободились для последующей записи. Таким образом, информация в кластерах удалённых файлов будет доступна нам до тех пор, пока драйвер FS не перезапишет их новыми данными.


FAT_Deleted.png



5. Практика – разбор служебных структур

Теперь попробуем автоматизировать весь изложенный процесс..
Код ниже покажет основную информацию об USB-Flash или карте-памяти, если они имею формат FAT32.

Здесь есть нюанс, на который советую обратить внимание..
Значит получаем букву диска, и проверяем его на съёмный носитель "Removable". Если ок, то открываем раздел диска через CreateFile(), и пытаемся прочитать с него сектор ВРВ. Суть в том, что читать рекомендуется в асинхронном режиме, поскольку том может быть занят кем-то другим, например антивирусом. Если функцией ReadFile() читать синхронно, то функция может вернуть ошибку, мол диск недоступен. Поэтому мы используем асинхронный режим, в котором запрос ставится в очередь, а нам после вызова необходимо выждать пару секунд при помощи Sleep(). Этот способ требует в параметрах ReadFile() указатель на структуру "OVERLAPPED" и никогда не даёт осечек.


C-подобный:
format  pe console
include 'win32ax.inc'
include 'equates\fat.inc'
entry   start
;//----------
.data
fatBuff    rb  512*3
ntName     db  '\\?\GLOBALROOT'
dosName    db  32 dup(0)
usbHndl    dd  0

align      16
fpuRes0    dq  0
fpuRes1    dq  0
fpuRes2    dq  0

secSize    dd  0
clsSize    dd  0
kByte      dd  1024
mByte      dd  1024*1024
gByte      dd  1024*1024*1024

ol        OVERLAPPED
rootDir   dd   0
dumpByte  dd   16
dumpLen   db   8
dumpStr   db   16 dup('.'),0
buff      db   0
;//----------
.code
start:  invoke  SetConsoleTitle,<'*** USB-Flash FAT info ***',0>

;//---- Проверим наличие клиента в портах USB
        invoke  GetLogicalDriveStringsA,64,buff
        mov     esi,buff
@@:     push    esi
        invoke  GetDriveType,esi     ;// запрашиваем тип драйва..
        pop     esi
        cmp     eax,DRIVE_REMOVABLE  ;// извлекаемое устройство?
        je      @ok
        add     esi,4
        cmp     byte[esi],0          ;// последнее в списке?
        jne     @b
       cinvoke  printf,<10,' ERROR! USB-Flash not found.',0>
        jmp     @exit

;//---- Получаем имя и открываем девайс на асинх.чтение
@ok:    push    esi
       cinvoke  printf,<10,' MS-DOS name...:  %s',0>,esi
        pop     esi
        mov     byte[esi+2],0
        invoke  QueryDosDevice,esi,dosName,32
       cinvoke  printf,<10,' Object link...:  %s',10,0>,ntName

        invoke  CreateFile,ntName,GENERIC_READ + GENERIC_WRITE,\
                                FILE_SHARE_READ + FILE_SHARE_WRITE,\
                                0,OPEN_EXISTING,\
                                FILE_FLAG_OVERLAPPED + FILE_ATTRIBUTE_NORMAL,0
        mov     [usbHndl],eax

;//---- Читаем структуру "Bios Parametr Block"
        invoke  ReadFile,[usbHndl],fatBuff,512,0,ol
        invoke  Sleep,1000

;//---- Проверим ф.систему на FAT32, иначе ошибка
        mov     esi,fatBuff
        cmp     word[esi+BPB.FATSz16],0
        je      @f
       cinvoke  printf,<10,' ERROR! This is FAT16.',0>
        jmp     @exit

;//---- Есть FAT32! - собираем инфу..
@@:     mov     eax,BPB_32.FilSysType
        add     eax,esi
        movzx   ebx,[esi+BPB_32.Header.BytePerSector]
        movzx   ecx,[esi+BPB_32.Header.SecPerCluster]
        mov     edx,ecx
        imul    ecx,ebx
        mov     [secSize],ebx
        mov     [clsSize],ecx
        shr     ecx,10
       cinvoke  printf,<10,' File system..........:  %.8s',\
                        10,' Sector  size.........:  %d Byte',\
                        10,' Sector per Cluster...:  %d',\
                        10,' Cluster size.........:  %d KB',10,0>,\
                        eax,ebx,edx,ecx

;//------------------------
        mov     esi,fatBuff
        mov     eax,[esi+BPB_32.Header.HiddSec]
        mov     ebx,[esi+BPB_32.Header.TotSec32]
        movzx   ecx,[esi+BPB_32.Header.RsvdSecCounter]
        mov     edx,ecx
        shl     edx,9
        shr     edx,10
        push    0 ebx
        fild    qword[esp]
        fimul   [secSize]
        fidiv   [gByte]
        fstp    [fpuRes0]
        add     esp,8
       cinvoke  printf,<10,' Volume first LBA.....:  %u',\
                        10,' Volume total LBA.....:  %u',\
                        10,' Volume capacity......:  %.2f GB',\
                        10,' BPB reserved sectors.:  %d  (%d KB)',10,0>,\
                        eax,ebx,dword[fpuRes0],dword[fpuRes0+4],ecx,edx

;//------------------------
        mov     esi,fatBuff
        movzx   eax,[esi+BPB_32.Header.NumFats]
        movzx   ebx,[esi+BPB_32.BkBootSec]
        movzx   ecx,[esi+BPB_32.Header.RsvdSecCounter]
        mov     edx,[esi+BPB_32.FATSz32]
        imul    edx,[secSize]
        shr     edx,10
       cinvoke  printf,<10,' FATs count...........:  %d',\
                        10,' BPB backup LBA.......:  %d',\
                        10,' FAT first  LBA.......:  %d',\
                        10,' FAT size (sectors)...:  %d  (%d KB)',0>,\
                        eax,ebx,ecx,[esi+BPB_32.FATSz32],edx

;//------------------------
;// RootDir = (RsvdSecCount + (FATSz32 * NumFats)) + (RootCluster - 2) * SecPerCluster
        mov     esi,fatBuff
        mov     eax,[esi+BPB_32.FATSz32]
        movzx   ebx,[esi+BPB_32.Header.RsvdSecCounter]
        shl     eax,1
        add     eax,ebx      ;// RsvdSecCount + (FATSz32 * NumFats)

        mov     ebx,[esi+BPB_32.RootCluster]
        sub     ebx,2
        movzx   ecx,[esi+BPB_32.Header.SecPerCluster]
        imul    ebx,ecx      ;// (RootCluster - 2) * SecPerCluster
        add     eax,ebx
        mov     [rootDir],eax
       cinvoke  printf,<10,' Root Dir cluster.....:  %d',\
                        10,' Root Dir LBA.........:  %d',10,0>,\
                        [esi+BPB_32.RootCluster],eax

;//------------------------
        mov     esi,fatBuff
        mov     eax,BPB_32.Header.OemName
        mov     ebx,BPB_32.VolLabel
        mov     ecx,[esi+BPB_32.VolID]
        movzx   edx,cx
        shr     ecx,16
        add     eax,esi
        add     ebx,esi
       cinvoke  printf,<10,' Volume formater......:  %.8s',\
                        10,' Volume label.........:  %.11s',\
                        10,' Volume serial........:  %04X-%04X',10,0>,eax,ebx,ecx,edx

;//---- Читаем таблицу FAT, и выводим фрагмент её дампа
        xor     al,al
        mov     ecx,sizeof.OVERLAPPED
        mov     edi,ol
        rep     stosb
        mov     esi,fatBuff
        movzx   eax,[esi+BPB_32.Header.RsvdSecCounter]
        shl     eax,9
        mov     [ol.Offset],eax
        invoke  ReadFile,[usbHndl],fatBuff,512,0,ol
        invoke  Sleep,1000
       cinvoke  printf,<10,' FAT dump....: ',0>
        call    PrintDump

;//---- Читаем корневой каталог, и так-же дампим фрагмент
        xor     al,al
        mov     ecx,sizeof.OVERLAPPED
        mov     edi,ol
        rep     stosb
        mov     eax,[rootDir]
        shl     eax,9
        mov     [ol.Offset],eax
        invoke  ReadFile,[usbHndl],fatBuff,512,0,ol
        invoke  Sleep,1000
       cinvoke  printf,<10,' RootDir dump: ',0>
        call    PrintDump

@exit:  invoke  CloseHandle,[usbHndl]
       cinvoke  _getch
       cinvoke  exit,0
;//------------------------------------------
;// Вспомогательная процедура - выводит дамп
;//------------------------------------------
PrintDump:
        mov     esi,fatBuff
@next:  mov     edi,dumpStr
        xor     ebx,ebx
@prn:   xor     eax,eax
        lodsb
        push    ebx eax
       cinvoke  printf,<'%02X',0>,eax
        pop     eax
        cmp     al,20h
        jb      @f
        mov     byte[edi],al
@@:     inc     edi
        pop     ebx
        inc     ebx
        cmp     ebx,4
        jb      @f
       cinvoke  printf,<' ',0>
        xor     ebx,ebx
@@:     dec     [dumpByte]
        jnz     @prn

        push    esi
        mov     [dumpByte],16
        invoke  CharToOem,dumpStr,dumpStr
       cinvoke  printf,<'  %.16s',0>,dumpStr
        mov     eax,'....'
        mov     edi,dumpStr
        mov     ecx,4
        rep     stosd
       cinvoke  printf,<10,15 dup(' '),0>
        pop     esi
        dec     [dumpLen]
        jnz     @next
        mov     [dumpLen],8
ret
;//----------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll'
include  'api\msvcrt.inc'
include  'api\kernel32.inc'
include  'api\user32.inc'

FAT_Result_1.png


Во-втором случае я решил поинтересоваться картой-памяти своего телефона и обнаружил, что размер кластера у неё 32КБ, поэтому и таблица FAT намного меньшего размера: была 7.692 секторов в длину, а стала 951. Более того, и сам том начинается не как обычно с физ.сектора(63), а съехал аж к сектору(8192), о чём говорит поле "Volume First LBA". В такой организации пространства есть смысл, т.к. нам заранее известны ориентировочные размеры файлов – это фотки, мультимедиа и прочие, т.е. больше стандартного кластера 4КБ:


FAT_Result_2.png



6. Заключение

Если не брать во-внимание детали оформления служебных структур, основным недостатком FAT32 является полное отсутствие "инстинкта самосохранения" при критических сбоях. Хорошо хоть инженеры предусмотрели некую пародию, в виде резервных блоков ВРВ и таблицы FAT2. Зато в NTFS всё продумано до мелочей, и в этом большое её преимущество. Например метафайл NTFS под названием "$LogFile" представляет собой журнал последней удачной конфигурации. Если случается крах, то при следующей перезагрузке, все параметры считываются уже из этого журнала, который драйвер NTFS обновляет при обычном выключении компьютера. Однако выбор у пользователей должен быть всегда, и FAT как-раз нам его предоставляет.
В скрепке лежит инклуд, и ехе-файл для тестов. Увидимся теперь на пороге Нового года, пока!
 

Вложения

  • FAT32.ZIP
    3,2 КБ · Просмотры: 378

rusrst

Green Team
04.10.2020
22
27
BIT
0
Ох, сколько всего интересного в этой fs, особенно в 32-й, ведь именно там увеличили размер файла до 4гб. :)



Я помню писал свой драйвер для МК, но только для чтения, только корневого раздела и только wav файлов :)
 
  • Нравится
Реакции: Marylin

Marylin

Mod.Assembler
Red Team
05.06.2019
309
1 392
BIT
380
ведь именно там увеличили размер файла до 4гб
Всё верно.
Размер файла определяет поле "DIR_FileSize" в структуре SFN.
В обоих FAT32/16 под него выделяется DWORD, однако в FAT16 макс.значение было ограничено числом со-знаком (т.е. 31-бит), поэтому макс.размер файла был 2 ГБ. Начиная с FAT32 данное ограничение было снято, и теперь это DWORD без знака 32-бит = 4 ГБ.
 
  • Нравится
Реакции: Mikl___ и rusrst

rusrst

Green Team
04.10.2020
22
27
BIT
0
Всё верно.
Размер файла определяет поле "DIR_FileSize" в структуре SFN.
В обоих FAT32/16 под него выделяется DWORD, однако в FAT16 макс.значение было ограничено числом со-знаком (т.е. 31-бит), поэтому макс.размер файла был 2 ГБ. Начиная с FAT32 данное ограничение было снято, и теперь это DWORD без знака 32-бит = 4 ГБ.
Да, у меня даже где-то в закромах оригинальный guide от мс 9х какого-то лохматого года) надо будет как-нибудь полноценную навигацию с обходом директорий)
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!