FAT32

FAT 32 to jeden ze starszych systemów plików. Jest już z nami od lat i wydawało się już że czasy jego świetności przeminęły, kiedy pojawił się Android i inne urządzenia mobilne. FAT32 jako system prosty i kompatybilny prawie ze wszystkim, znowu okazał się dobrym rozwiązaniem.

Na FAT składają się następujące opisane poniżej struktury.

Obszar zarezerwowany

Zwykle 32 sektory licząc od początku dysku/woluminu. Zaczyna się on od VBR (Volume Boot Record) – pierwszy sektor zawierające  dane konfiguracyjne FAT-u oraz boot code. W sektorze 1 znajduje się FSINFO czyli informacja o ilości wolnych klastrów na systemie plików oraz adresie następnego wolnego klastra.  W sektorze 6 znajduje się kopia zapasowa VBR. Pozostałe sektory obszaru zarezerwowanego zawierają pozostałą część kodu startowego (boot code), a często są po prostu puste – mogą być więc również użyte do ukrycia danych przez hakera lub złośliwy kod.

Tabela FAT (File Allocation Table)

Tabela FAT to ciąg 32 bitowych (4-bajtowych) wartości. Od tych 32 bitów pochodzi 32 w nazwie wersji FAT-u. Wartości te to w zasadzie współrzędne lokalizacji danych na dysku. Jeżeli plik ma 20 kilobajtów a klaster na dysku 8 to plik zajmuje 3 klastry. Tabela FAT wskazuje lokalizacje klastra 2 i 3. Lokalizacja klastra 1 zawarta jest w rekordzie katalogu (directory entry) o czym niżej.

Drugą funkcją tabeli FAT-u jest wskazanie, które klastry są zajęte, a które wolne.

Działa to w następujący sposób:

  1. Dane na dysku podzielone są na klastry ponumerowane od 2 w górę.
  2. Tabela FAT składa się z 32 bitowych wartości. Pierwsze 4 bajty to klaster 0, następne to klaster 1, kolejne klaster 2 i tak dalej. Wartość nie zawiera w sobie numeru klastra –  trzeba to sobie obliczyć na podstawie pozycji w tabeli FAT.
  3. W FAT32 nie istnieje klaster 0 i 1. W tabeli FAT używa się tej pozycji do określenie rodzaju dysku – 0xFFFFFFF0 to nośnik wymienny a 0xFFFFFFF8 to nośnik stały. Pozycja dla nieistniejącego klastra numer 1 odpowiada za tak zwany dirty status, czyli mówi nam czy nośnik został prawidłowo odmontowany.
  4. Zapis pliku 20 KB, klastry 8 KB:
    1. Adres pierwszego klastra zawarty jest w directory entry, założmy , że jest to klaster 7
    2. W tabel FAT znajdujemy offset dla 7 klastra. Liczonego od zera czyli 8-ego 🙂  W każdym razie są to bajty 24-27 w tabeli FAT. W bajtach tych zawarty będzie adres kolejnego klastra. Po znalezieniu w tabeli FAT bajtów odpowiedzialnych za klaster numer 2 znajdziemy w nim adres 3 i ostatniego.
    3. Na tej ostatniej pozycji znajduje się wartość 0xF8FFFF0F oznaczającą koniec pliku.
    4. Odczytując powyższe dane z tabeli FAT, system operacyjny wie, które trzy klastry należy odczytać aby odtworzyć plik.
  5. Wartość 0x00000000 oznacza wolny klaster w którym zapisywać można nowe pliki
  6. Wartość 0xF7FFFF0F oznacza zły klaster (bad cluster), który nie powinien być użyty, przez system operacyjny. Używanie fałszywych złych klastrów może być kolejnym sposobem na ukrycie danych. Wystarczy ręcznie zmodyfikować tabelę FAT.
  7. 4 bity w tabeli FAT nie są używane, dlatego przedostatnia cyfra to zwykle 0.

Po tabeli FAT numer 1 w systemie plików zapisana jest identyczna kopia zapasowa – tabela FAT numer 2.

W zasadzie tabela FAT powinna znajdować się zaraz po zarezerwowanej przestrzeni, jednak może być ona dalej. Na przykład w moim przykładzie zaczyna się ona w sektorze 6654.

Przykładowy początek tabeli FAT

Przykładowy początek tabeli FAT

Obszar danych

Po tabeli FAT na dysku rozpoczyna się obszar danych. Sektory (dawniej 512 bajtowe, obecnie także 4 kilobajtowe) pogrupowane są w klastry. Typowe wielkości klastrów to 4,8,16,32 i 64 KB. Pliki zapisywane są właśnie w klastrach. Wielkość pliku dzieli się przez wielkość klastra i przydziela odpowiednią ilość klastrów do zapisu. System operacyjny stara się ustawić te klastry tak by następowały jeden po drugim. Nie zawsze jest to jednak możliwe, stąd fragmentacja dysku.

Adresowanie zaczyna się od klastra numer dwa, z przyczyn nie mających obecnie większego znaczenia.

Oprócz plików na obszarze danych zapisywane są także foldery. Foldery to tak zwane wpisy katalogu (directory entries). Wpis katalogu to z kolei lista jego zawartości, czyli plików i folderów w nim się znajdujących. Istnieje pewna niespójność z nazewnictwem – wpisem katalogu nazywa się zarówno konkretny rekord dla danego pliku znajdującego się w folderze, jak i całą listę, całej zawartości dla całego folderu. Całą listę będę nazywał po prostu katalogiem a rekordy na tej liście wpisem.

Katalogi mają charakter hierarchiczny, tak ja to widzimy w Eksploratorze Windows. Root katalog, czyli najwyższy katalog w strukturze znajduje się w miejscu, którego adres wskazany jest  w VBR. Ten katalog zawiera sub-katalogi, których lokalizacja wskazana jest w ich wpisach.

Załóżmy że w oknie CMD uruchamiamy komendę

delete c:\users\maria\documents\test.txt

Co robi system operacyjny, żeby znaleźć plik?:

  1. Sprawdza w VBR lokalizację katalogu root (katalogu głównego), załóżmy, że to klaster 1033
  2. Odczytuje wpisy w katalogu root, szukając wpisu dla katalogu “Users”. Wpis ten będzie zawierał numer klastra, w którym ten katalog się znajduje.
  3. Odczytuje katalog “users”, szukając wpisu dla “documents”. Wpis będzie zawierał numer klastra.
  4. Odczytuje katalog “documents”, szukając wpisu dla “test.txt”. Wpis ten będzie zawierał pierwszy klaster pliku. Jeżeli plik zawiera więcej klastrów, ich adresy będą dostępne w tabeli FAT, jak wskazano powyżej.

A oto jak wygląda directory entry dla folderu “System Volume Information” zawierającego, no właśnie co zawierającego ???:

Directory entry

Każdy wpis katalogu w katalogu ma 32 bajty. Każda linia heksu na zrzucie ekranu ma 16 bajtów. Mamy 10 linii heksu, więc 5 wpisów. Nie każdy jednak wpis to plik lub folder:

  1. Pierwszy wpis zaczynający się od kropki (0x2E) to wpis dla bieżącego katalogu.
  2. Drugi wpis, zaczynający się od dwóch kropek (0x2E0x2E) to wpis dla katalogu wyższego poziomu. To właśnie tych kropek używa się w celu ręcznego znalezienia osuniętych katalogów. Grep: 0x2e…….0x2e0x2e – gdzie kropka reprezentuje dowolny bajt.
  3. Trzeci i czwarty zawiera nazwę pliku dla nazw plików dłuższych niż dosowski format 8.3. Nazwa zakodowana jest od końca czyli najpierw wpis 4 a potem 3. Nazwa pliku to IndexerVolumeGuid bez rozszerzenia
  4. Piąty to właściwy plik katalogu, zaczyna się od nazwy w formacie 8.3. 8 bajtów nazwy pliku i trzy bajty rozszerzenia. Nie ma bajtu na kropkę – jest ona domniemana. W moim pliku nie ma rozszerzenia, wiec miejsce pozostało puste – 0x20 0x20 0x20

A oto szczegółowa struktura wpisu katalogu:

  1. Bajty 0-10 – nazwa pliku wraz z rozszerzeniem,
  2. Bajt 11 – atrybuty
  3. Bajty 12-13 – zarezerwowane
  4. Bajty 14-17 – data utworzenia w formacie DOS
  5. Bajty 18-19 – data dostępu, tylko data, nie ma czasu
  6. Bajty 20-21 – dwa wyższe bajty numeru klastra gdzie znajduje się początek pliku
  7. Bajty 22-25 – czas modyfikacji w formacie DOS
  8. Bajty 26-27 – dwa niże bajty początku klastra
  9. Bajty 28-31 – wielkość pliku w bajtach, zero dla folderów

Data w DOS-e zakodowana jest na poziomie bitów. Cały timestamp ma 32 bity, przyjmijmy, że każda litera jest bitem to data zakodowana jest tak:

YYYYYYMMMMDDDDDHHHHHMMMMMMSSSSS

Odkodować datę należy odnaleźć bity odpowiedzialne na przykład za dzień (D) i zinterpretować je jako liczbę – będzie ona oznaczała dzień miesiąca. Podobnie dla pozostałych elementów daty z tym że, liczbę sekund należy pomnożyć x 2, natomiast do roku należy dodać 1980.