Крис Касперски - Восстановление данных. Практическое руководство
Практический пример
Рассказ о файловой системе NTFS был бы неполным без практической иллюстрации техники разбора файловой записи вручную. До сих пор мы витали в облаках теоретической абстракции. Пора спускаться на грешную землю.
Воспользовавшись любым дисковым редактором, например, Disk Probe, попробуем декодировать одну файловую запись вручную. Найдем сектор, содержащий сигнатуру FILE в его начале (не обязательно брать первый встретившийся сектор). Он может выглядеть, например, как в листинге 6.4.
Листинг 6.4. Ручное декодирование файловой записи (разные атрибуты выделены разным цветом)
: 00 01 02 03 04 05 06 07 | 08 09 0A 0B 0C 0D 0E 0F
00000000: 46 49 4C 45 2A 00 03 00 | 60 79 1A 04 02 00 00 00 FILE*...`y......
00000010: 01 00 01 00 30 00 01 00 | 50 01 00 00 00 04 00 00 ....0...P.......
00000020: 00 00 00 00 00 00 00 00 | 04 00 03 00 00 00 00 00 ................
00000030: 10 00 00 00 60 00 00 00 | 00 00 00 00 00 00 00 00 ................
00000040: 48 00 00 00 18 00 00 00 | B0 D5 C9 2F C6 0B C4 01 H.......░╒╔/╞.─.
00000050: E0 5A B3 7B A9 FA C3 01 | 90 90 F1 2F C6 0B C4 01 рZ│{й·├.PPё/╞.─.
00000060: 50 7F BC FE C8 0B C4 01 | 20 00 00 00 00 00 00 00 P⌂╝■╚.─. .......
00000070: 00 00 00 00 00 00 00 00 | 00 00 00 00 05 01 00 00 ................
00000080: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 ................
00000090: 30 00 00 00 70 00 00 00 | 00 00 00 00 00 00 02 00 0...p...........
000000A0: 54 00 00 00 18 00 01 00 | DB 1A 01 00 00 00 01 00 T.......█.......
000000B0: B0 D5 C9 2F C6 0B C4 01 | B0 D5 C9 2F C6 0B C4 01 ░╒╔/╞.─.░╒╔/╞.─.
000000C0: B0 D5 C9 2F C6 0B C4 01 | B0 D5 C9 2F C6 CB C4 01 ░╒╔/╞.─.░╒╔/╞.─.
000000D0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 ................
000000E0: 20 00 00 00 00 00 00 00 | 09 03 49 00 6C 00 66 00 ..........I.l.f.
000000F0: 61 00 6B 00 2E 00 64 00 | 62 00 78 00 00 00 00 00 a.k...d.b.x.....
00000100: 80 00 00 00 48 00 00 00 | 01 00 00 00 00 00 03 00 А...H...........
00000110: 00 00 00 00 00 00 00 00 | ED 04 00 00 00 00 00 00 ........э.......
00000120: 40 00 00 00 00 00 00 00 | 00 E0 4E 00 00 00 00 00 @........рN.....
00000130: F0 D1 4E 00 00 00 00 00 | F0 D1 4E 00 00 00 00 00 Ё╤N.....Ё╤N.....
00000140: 32 EE 04 D9 91 00 00 81 | FF FF FF FF 82 79 47 11 2ю.┘С..Б ВyG.
000001F0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 03 00 ................
: 00 01 02 03 04 05 06 07 | 08 09 0A 0B 0C 0D 0F 0F
Первым делом необходимо восстановить оригинальное содержимое последовательности обновления. По смещению 04h от начала сектора лежит 16-разрядный указатель на нее, равный в данном случае 2Ah (значит, это NTFS 3.0 или более ранняя версия). А что у нас лежит по смещению 2Ah? Это — пара байт 03 00. Данная последовательность представляет собой номер последовательности обновления. Сверяем его с содержимым двух последних байт этого и следующего секторов (смещения 1FEh и 3FEh соответственно). Они равны! Следовательно, данная файловая запись цела (по крайней мере, на первый взгляд), и можно переходить к операции ее восстановления. По смещению 2Ch расположен массив, содержащий оригинальные значения последовательности обновления. Количество элементов в нем равно содержимому 16-разрядного поля, расположенному по смещению 06h от начала сектора и уменьшенного на единицу (в данном случае имеем 03h - 01h == 02h). Извлекаем два слова, начиная со смещения 2Ch (в данном случае они равны 00 00 и 00 00) и записываем их в конец первого и последнего секторов.
Теперь нам необходимо выяснить, используется ли данная файловая запись, или же ассоциированный с ней файл или каталог был удален. 16-разрядное поле, расположенное по смещению 16h, содержит значение 01h. Следовательно, перед нами файл, а не каталог, и этот файл еще не удален. Но является ли эта файловая запись базовой для данного файла или мы имеем дело с ее продолжением? 64-разрядное поле, расположенное по смещению 20h, равно нулю, следовательно, данная файловая запись — базовая.
Очень хорошо, теперь переходим к исследованию атрибутов. 16-разрядное поле, находящееся по смещению 14h, равно 30h, следовательно, заголовок первого атрибута начинается со смещения 30h от начала сектора.
Первое двойное слово атрибута равно 10h, значит, перед нами атрибут типа $STANDARD_INFORMATION. 32-разрядное поле длины атрибута, находящееся по смещению 04h и равное в нашем случае 60h байт, позволяет нам вычислить смещение следующего атрибута в списке: 30h (смещение нашего атрибута) + 60h (его длина) == 90h (смещение следующего атрибута). Первое двойное слово следующего атрибута равно 30h, значит, это атрибут типа $NAME, и следующее 32-разрядное поле хранит его длину, равную в данном случае 70h. Сложив длину атрибута с его смещением, мы получим смещение следующего атрибута — 90h + 70h == 100h. Первое двойное слово третьего атрибута равно 80h, следовательно, это атрибут типа $DATA, хранящий основные данные файла. Складываем его смещение с длиной — 100h + 32h == 132h. И вот здесь мы наткнулись на частокол FFFFFFh, сигнализирующий о том, что атрибут $DATA последний в списке.
Теперь, разбив файловую запись на атрибуты, можно приступить к исследованию каждого из атрибутов в отдельности. Начнем с разбора имени. 8-разрядное поле, находящееся по смещению 08h от начала атрибутного заголовка (и по смещению 98h от начала сектора), содержит флаг нерезидентности. В данном случае этот флаг равен нулю. Это значит, атрибут резидентный, и его тело хранится непосредственно в самой файловой записи, что уже хорошо. 16-разрядное поле, расположенное по смещению 0Сh от начала атрибутного заголовка (и по смещению 9Ch от начала сектора) равно нулю, следовательно, тело атрибута не сжато и не зашифровано. Таким образом, можно приступать к разбору тела атрибута. 32-разрядное поле, расположенное по смещению 10h от начала атрибутного заголовка (и по смещению A0h от начала сектора), содержит длину атрибутного тела, равную в данном случае 54h байт. 16-разрядное поле, расположенное по смещению 14h от начала атрибутного заголовка и по смещению A4h от начала сектора, хранит смещение атрибутного тела, равное в данном случае 18h. Следовательно, тело атрибута $FILE_NAME располагается по смещению A8h от начала сектора.
Формат атрибута типа $FILE_NAME описан в табл. 6.9. Первые восемь байт содержат ссылку на родительский каталог этого файла, равную в данном случае 11ADBh:01 (индекс — 11ADBh, номер последовательности — 01h). Следующие 32 байта содержат данные о времени создания, изменения и времени последнего доступа к файлу. По смещению 28h от начала тела атрибута и D0h от начала сектора лежит 64-разрядное поле выделенного размера, а за ним — 64-разрядное поле реального размера. Оба равны нулю, что означает, что за размером файла следует обращаться к атрибутам типа $DATA.
Длина имени файла содержится в 8-разрядном поле, находящемся по смещению 40h байт от начала тела атрибута и по смещению E8h от начала сектора. В данном случае оно равно 09h. Само же имя начинается со смещения 42h от начала тела атрибута и со смещения EAh от начала сектора. И здесь находится имя файла llfak.dbx.
Переходим к атрибуту основных данных файла, пропустив атрибут стандартной информации, который не содержит решительно ничего интересного. 8-разрядный флаг нерезидентности, расположенный по смещению 08h от начала атрибутного заголовка и по смещению 108h от начала сектора, равен 01h, следовательно, атрибут нерезидентный. 16-разрядный флаг, расположенный по смещению 0Ch от начала атрибутного заголовка и по смещению 10Ch от начала сектора, равен нулю, значит, атрибут не сжат и не зашифрован. 8-разрядное поле, расположенное по смещению 09h от начала атрибутного заголовка и по смещению 109h от начала сектора, равно нулю — атрибут безымянный. Реальная длина тела атрибута (в байтах) содержится в 64-разрядном поле, расположенном по смещению 30h от начала атрибутного заголовка и по смещению 130h от начала сектора. В данном случае она равна 4ED1F0h (5.165.552). Два 64-разрядных поля, расположенных по смещениям 10h/110h и 18h/118h байт от начала атрибутного заголовка/сектора соответственно, содержат начальный и конечный номер виртуального кластера нерезидентного тела. В данном случае они равны: 0000h и 4EDh соответственно.
Остается лишь декодировать список отрезков, адрес которого хранится в 16-разрядном поле, находящемся по смещению 20h от начала атрибутного заголовка и 120h от начала сектора. В данном случае поле равно 40h, что соответствует смещению от начала сектора в 140h. Сам же список отрезков выглядит так: 32 EE 04 D9 91 00 00. Ага! Два байта занимает поле длины (равное в данном случае 04EEh кластерам) и три — поле начального кластера (0091h). Завершающий ноль на конце говорит о том, что этот отрезок последний в списке отрезков.