O co chodzi
Problem z odzyskiwaniem kontenerów TrueCrypt polega przede wszystkim na:
- braku cech charakterystycznych (nagłówka pliku, stopki),
- dużych rozmiarach kontenerów, a co za tym idzie - prawdopodobnej fragmentacji,
We wspominanym wpisie pokazywałem co prawda, że pliki TC mają jednak jedną cechę charakterystyczną - wysoką entropię. Można próbować wykorzystać to do wytypowania miejsc na dysku, w których fragmenty kontenera są zapisane. Można próbować, ale skuteczność takich poszukiwań może być mizerna. Wiele innych plików, lub ich fragmentów, może charakteryzować się dużą entropią. Ciężko jest odróżnić, czy w danym klastrze zapisany jest fragment kontenera TrueCrypt, czy może fragment jakiegoś skompresowanego pliku.
Nawet jeśli takie typowanie miejsc, w których znajdują się fragmenty kontenera, odbyło by się ze 100% skutecznością, to dopiero połowa problemu. Druga kwestia - fragmenty pliku trzeba ułożyć we właściwej kolejności. Oczywiście, może zdarzyć się tak, że cały kontener zapisany jest w jednym ciągłym kawałku, jednak bardzo prawdopodobne jest to, że kawałków będzie kilka. I wcale nie jest powiedziane, że każdy kolejny fragment pliku będzie zapisany na dysku po kawałku poprzednim.
Oczywiście, można próbować ułożyć wszystkie możliwe układy, jest ich (n-1)! ponieważ pierwszy fragment pliku (ten z nagłówkiem) możemy zidentyfikować dość skutecznie. Stąd właśnie n - 1, pierwszy kawałek zawsze jest pierwszy, a pozostałe kawałki (czyli właśnie n-1) układamy we wszystkie możliwe permutacje. Im więcej kawałków, tym liczba możliwych układów jest większa, silnia pod tym względem jest dość paskudna. Do tego trzeba dodać problem choćby z przygotowaniem warunku sukcesu - jak sprawdzić, że wszystkie kawałki zostały ułożone we właściwej kolejności?
Czy można zadanie odzyskania pliku/kontenera TrueCrypt zrealizować inaczej? Tak i metoda ta nie ogranicza się do tego typu plików. Po prostu można próbować odnaleźć na dysku rekord, który utracony/zagubiony plik opisuje. W przypadku systemu NTFS takie informacje zapisane są w $Mft. Tu z góry uprzedzam, że nie będę opisywał dokładnie struktury NTFS. Jeśli ktoś jest zainteresowany tym tematem, to dobrym źródłem informacji jest opracowanie NTFS Documentation, trzeba szukać pliku ntfsdoc.pdf, bo "oryginalny" link (http://data.linux-ntfs.org/ntfsdoc.pdf) nie działa.
Eksperyment
By pokazać jak można ustalić położenie fragmentów pliku na dysku, przeprowadzę mały eksperyment. Utworzę specjalnie sfragmentowany kontener TrueCrypt, następnie znajdę odpowiadający mu rekord MFT i odczytam z niego informacje o ilości fragmentów, w których plik jest zapisany i ich rozmieszczeniu na dysku.
Przygotowanie materiału
Do stworzenia "środowiska testowego" wykorzystam niewielki "dysk" o rozmiarze 1GiB. Został on sformatowany jako NTFS. Ponieważ zależy mi na tym, by kontener TrueCrypt był sfragmentowany, najpierw utworzyłem na tym testowym dysku kilka plików:
E:\>dir
Volume in drive E has no label.
Volume Serial Number is 2C93-FE01
Directory of E:\
2011-02-02 19:21 134 217 728 test1.dat
2011-02-02 19:21 134 217 728 test2.dat
2011-02-02 19:21 134 217 728 test3.dat
2011-02-02 19:21 134 217 728 test4.dat
2011-02-02 19:21 134 217 728 test5.dat
2011-02-02 19:21 134 217 728 test6.dat
2011-02-02 19:21 134 217 728 test7.dat
7 File(s) 939 524 096 bytes
0 Dir(s) 126 145 536 bytes free
A następnie usunąłem część z nich:
E:\>dir
Volume in drive E has no label.
Volume Serial Number is 2C93-FE01
Directory of E:\
2011-02-02 19:21 134 217 728 test1.dat
2011-02-02 19:21 134 217 728 test3.dat
2011-02-02 19:21 134 217 728 test5.dat
2011-02-02 19:21 134 217 728 test7.dat
4 File(s) 536 870 912 bytes
0 Dir(s) 528 798 720 bytes free
Dzięki tym zabiegom stworzony później kontener TrueCrypt był przyzwoicie sfragmentowany:
E:\>contig -a kontener.tc
Contig v1.55 - Makes files contiguous
Copyright (C) 1998-2007 Mark Russinovich
Sysinternals - www.sysinternals.com
E:\kontener.tc is in 8 fragments
Summary:
Number of files processed : 1
Average fragmentation : 8 frags/file
W zasadzie w tej chwili powinienem jeszcze "uszkodzić" system plików na tym "dysku", ale do pokazania tego, co chcę pokazać nie jest to konieczne.
Wyszukanie rekordu pliku
Plik $Mft zawiera rekordy, które opisują umiejscowienie każdego pliku i katalogu na dysku. W szczególności zapisane są w nim informacje o tym, gdzie fragmenty pliku są fizycznie zapisane na dysku.
Każdy rekord MFT zaczyna się od charakterystycznego znacznika: FILE0. Nazwa pliku jest natomiast zapisana w kodowaniu UNICODE. Wystarczy więc znaleźć odpowiedni rekord MFT (jeśli taki na dysku ocalał) i... Dalszą część pokażę z użyciem narzędzia FTK Imager, choć równie dobrze można to samo osiągnąć z użyciem dowolnego edytora HEX.
Najpierw wyszukując po nazwie pliku (kontener.tc) znajduję rekord (albo to, co z niego zostało) MFT. Wyszukiwać będę po nazwie pliku, przy czym trzeba pamiętać, że nazwa ta zapisana jest w Unicode. Większość narzędzi pozwala na określenie encodingu wyszukiwanego stringa, trzeba więc pamiętać by wybrać właściwe kodowanie. Znaleziony rekord wygląda następująco:
Na zrzucie zaznaczyłem znacznik początku rekordu (FILE0) oraz nazwę pliku. Warto zwrócić uwagę gdzie występuje znacznik. Wielkość rekordu MFT jest stała i wynosi 1024 bity, siłą rzeczy więc znacznik rekordu nie może wystąpić w dowolnym miejscu. Tak jak zapowiadałem, nie będę się zagłębiał w dokładny opis struktury rekordu MFT.
Interpretacja atrybutu DATA
Po znalezieniu rekordu szukamy w nim atrybutu $DATA (0x80). Może być ich więcej niż jeden (alternate data stream), w tym przypadku jest tylko jeden atrybut tego typu. Dla małych plików atrybut ten zawiera również treść pliku, w przypadku większych - informacje gdzie dane są zapisane. Właśnie ta informacja będzie potrzebna do odzyskania fragmentów kontenera (lub dowolnego innego pliku).
Atrybut $DATA można znaleźć po jego identyfikatorze, tu (prawie) cały atrybut zaznaczony na zrzucie:
Na poniższym zrzucie zaznaczone są istotne elementy atrybutu DATA, w kolejności:
- nagłówek atrybutu - 80 00 00 00,
- rozmiar atrybutu - 78 00 00 00 (co daje 120 w little endian),
- informacja, że dane są zapisane poza MFT - 0x01,
- offset od początku atrybutu do początku run list - 0x40 (co daje 64 w little endian),
- run list - na zrzucie zaznaczony jest właściwie tylko jej początek,
To właśnie run list jest tym, czego szukaliśmy i co pozwoli poskładać fragmenty pliku rozsiane po całym dysku w całość. Jedyny problem to prawidłowa interpretacja run list (data runs).
Jeśli kogoś interesuje skąd poszczególne wartości się wzięły, dobry opis znajduje się na przykład tu: FAQ NTFS. Opis pracy i odzyskiwania danych z partycji NTFS.
Interpretacja data runs
33 F8 03 02 08 00 02 33 00 00 02 28 01 08 33 CF
FE 01 00 00 04 33 9E A9 00 31 55 F9 22 4D 25 B2
D9 31 24 F0 CF 00 31 04 4D 55 FD 13 26 FE 00 35
00 00 00 00 00 00 00 00
Data runs to sekwencja elementów, każdy element można w uproszczeniu przedstawić jako FLAB, gdzie:
- F (1 bajt) - rozmiar pola z informacją o offsecie fragmentu pliku,
- L (1 bajt) - rozmiar pola z informacją o długości fragmentu pliku,
- A - długość fragmentu pliku,
- B - offset fragmentu pliku,
Dla oszczędności miejsca pola A i B mają rozmiar zależny od potrzeb (opisany w F i L). Trzeba pamiętać, że offset zapisany jest jako signed integer i może mieć wartość ujemną. Pierwszy element data run informuje o pierwszym klastrze pierwszego fragmentu pliku. Informacja o kolejnych fragmentach zapisana jest już jako offset do poprzedniego fragmentu, więc w szczególności może to być liczba ujemna, jeśli kolejny fragment znajduje się na dysku "przed" fragmentem go poprzedzającym (w pliku).
Pierwszy bajt znalezionego data run ma wartość 0x33 co oznacza, że zarówno offset jak i długość fragmentu pliku zapisana jest na trzech bajtach. Są to odpowiednio:
F8 03 02
08 00 02
Dowiadujemy się z tego, że pierwszy fragment pliku ma długość 132 088 klastrów:
Pierwszy klaster tego fragmentu pliku to 131 080:
Dla pewności numer pierwszego klastra sprawdźmy w inny sposób:
Kolejne fragmenty mają następujące parametry (długość, offset):
- 131 072, 524 584
- 130 767, 262 144
- 43 422, -436 943
- 9 549, -9 806
- 36, 53 232
- 4, -174 771
- 65 062, 53
W takim układzie pierwsze klastry fragmentów pliku to:
- 131 080
- 655 664 (+524 584)
- 917 808 (+262 144)
- 480 865 (-436 943)
- 471 059 (-9 806)
- 524 291 (+53 232)
- 349 520 (-174 771)
- 349 573 (+53)
Zgodnie z oczekiwaniami fragmentów jest osiem. Przy okazji plik sfragmentował się na tyle sympatycznie, że fragmenty pliku "przeplatają się". Próbując otworzyć plik ze zidentyfikowanych fragmentów o dużej entropii i nawet zakładając, że fragmenty te zostały zidentyfikowane prawidłowo, trzeba sprawdzić 7! ułożeń, czyli, bagatela, 5040 możliwych permutacji. Oczywiście - w najgorszym przypadku. Nie jest to liczba prób niemożliwa do wykonania. Cały czas podkreślam, że większym problemem będzie właściwe i precyzyjne zidentyfikowanie fragmentów kontenera.
Jak odzyskać fragmenty pliku
Jak odzyskać fragmenty pliku? Wystarczy znaleźć miejsce, gdzie dany fragment się zaczyna i skopiować określony fragment danych o określonym rozmiarze. W tym miejscu kluczowe jest ustalenie rozmiaru klastra. Można tu posłużyć się dokumentem Default cluster size for NTFS, FAT, and exFAT . Jest tylko jeden mały problem - ta informacja może nie być zgodna z rzeczywistością.
Druga istotna informacja - gdzie właściwie zaczyna się partycja? Jest to istotne, ponieważ w końcu trzeba wiedzieć od którego miejsca (fizycznie) na dysku trzeba liczyć te klastry.
Obu potrzebnych informacji dostarczyć może nam $Boot, a właściwie jego atrybut $DATA. Jest on zawsze na początku partycji (jest jeszcze jego kopia), więc dzięki temu można ustalić gdzie się ona zaczyna. Dodatkowo zapisana jest w nim między innymi informacja o ilości bajtów na sektor i ilości sektorów na klaster. Ponownie obrazek poglądowy:
Na powyższym zrzucie zaznaczone są w kolejności:
- identyfikator systemu ("NTFS " - łącznie 8 znaków, 4 spacje),
- ilość bajtów w sektorze (512),
- ilość sektorów na klaster (2),
Wiemy w takim razie, że rozmiar klastra to 1024 bajty, wiemy również skąd powinniśmy zacząć liczyć. Tak się składa, że tym razem początek partycji zbiega się z początkiem dysku i jest to 0x00. Jest to dość niezwykła sytuacja i wynika ze sposobu przygotowania dysku/obrazu do tego przykładu. Zwykle partycja zaczyna się z pewnym offsetem od początku dysku (np. gdzieś musi się zmieścić informacja o partycjach na dysku. Na typowym dysku gdzie są założone partycje układ może wyglądać następująco (przykład mojego laptopa, mmls to narzędzie ze sleuthkit):
mmls \\.\PHYSICALDRIVE0
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors
Slot Start End Length Description
00: Meta 0000000000 0000000000 0000000001 Primary Table (#0)
01: ----- 0000000000 0000000062 0000000063 Unallocated
02: 00:00 0000000063 0000160649 0000160587 Dell Utilities FAT (0xde)
03: 00:01 0000160650 0625137344 0624976695 NTFS (0x07)
Rzeczywiście, jeśli wyszukamy "NTFS ", to znajdziemy wartość tę dalej na dysku:
Offset 0004e71400 to nic innego jak 160650 pomnożone przez 512 (rozmiar sektora), co daje 82252800 (czyli właśnie 4E71400).
W tej chwili mamy w zasadzie wszystkie dane potrzebne do odzyskania fragmentów pliku i poskładania ich w odpowiedniej kolejności:
- znamy początek partycji,
- znamy rozmiar sektora, ilość sektorów na klaster, a więc i rozmiar klastra,
- mamy informacje o pierwszych klastrach poszczególnych fragmentów oraz długości tych fragmentów w klastrach,
Pozostaje tylko skopiować fragmenty pliku i ułożyć je w kolejności zapisanej w data runs. Do tego zadania wystarczy nawet dd.
Można jeszcze inaczej
Odzyskiwanie plików można przeprowadzić jeszcze inaczej. Tu po raz kolejny polecę narzędzia PhotoRec i TestDisk. Co prawda carving w przypadku kontenerów TrueCrypt nie zadziała (powtarzam - brak cech charakterystycznych), to przy pomocy TestDisk można (przynajmniej próbować):
- odtworzyć tablicę partycji, wyszukać partycje,
- odtworzyć $Boot dla partycji NTFS,
- odtworzyć/naprawić $Mft dla partycji NTFS,
Dzięki temu być może "utracone" pliki znowu staną się normalnie widoczne. Bardzo miłą cechą tych narzędzi jest również możliwość pracy na obrazie dysku zapisanym do pliku. Taką kopię dobrze wykonać jak najszybciej po przypadkowym usunięciu lub innej awarii, która doprowadziła do "zniknięcia" istotnych danych. Im szybciej, tym lepiej. Każda operacja zapisu zwiększa prawdopodobieństwo, że zagubione dane zostaną uszkodzone, lub uszkodzona zostanie informacja dająca szansę na ich odzyskanie. Oryginał można spokojnie "zabezpieczyć", a wszystkie eksperymenty przeprowadzać na jego kopii. Nawet jeśli coś nie wyjdzie, zawsze można zacząć próbować od początku.
Choć "typowy" carver nie zadziała, to można próbować narzędzia nieco inne. Takie, które robią właściwie to, co opisałem wcześniej, ale robią to automatycznie. Tu polecam wyszukiwarkę i słowa kluczowe typu: MFT carver albo MFT carving.