Блог · Заметки о форензике и разработке


Что такое $MFT и из чего он состоит

Начнем с определения: MFT (Master File Table) — это главная таблица файлов в NTFS, представляет собой таблицу, в которой строки соответствуют файлам тома, а столбцы — атрибутам файлов.

Структура записи

СмещениеРазмерПолеОписание
0x00 – 0x034 байтаSignatureСигнатура "FILE" (валидная запись) или "BAAD" (запись не прошла проверку Fixup).
0x04 – 0x052 байтаFixup OffsetСмещение до массива Fixup Array (обычно 0x30 / 48 байт).
0x06 – 0x072 байтаFixup CountКоличество элементов в массиве Fixup (обычно 3: 1 проверочный + 2 для секторов).
0x08 – 0x0F8 байт$LogFile LSNПорядковый номер транзакции в журнале NTFS.
0x10 – 0x112 байтаSequence NumberНомер версии записи (увеличивается при каждом удалении/пересоздании файла).
0x12 – 0x132 байтаHard Link CountКоличество жестких ссылок (имён) на этот файл.
0x14 – 0x152 байтаFirst Attr OffsetСмещение до первого атрибута (обычно 0x38 / 56 байт).
0x16 – 0x172 байтаFlags0x00 — удален, 0x01 — активный файл, 0x02 — удаленная папка, 0x03 — активная папка.
0x18 – 0x1B4 байтаUsed SizeРеально занятый размер записи (атрибутами и заголовком).
0x1C – 0x1F4 байтаAllocated SizeВыделенный размер записи на диске (всегда ровно 1024 байта).
0x20 – 0x278 байтBase Record Reference0 для обычного файла. Если ≠ 0 — ссылка на базовую запись (для Extension-записей).
0x28 – 0x292 байтаNext Attribute IDID следующего атрибута, который будет добавлен к файлу.
0x2A – 0x2B2 байтаAlign / ReservedВыравнивание до 4-байтной границы.
0x2C – 0x2F4 байтаRecord NumberПорядковый номер этой MFT-записи в таблице (начиная с 0).
0x30 – 0x378 байтFixup ArrayМассив контрольных байт: 2 байта USN + по 2 байта оригинальных данных из конца первого и второго секторов (итого 6, еще 2 байта выравнивание).
0x38 – 0x3FF~968 байтAttributes ChainЦепочка атрибутов ($SI, $FN, $DATA), завершающаяся маркером 0xFFFFFFFF.

Размер сектора равен 512 байтам, а запись в MFT занимает 1024 байта, то есть два сектора. Чтобы не было разрыва записи (например, когда пишется файл на диск и резко пропадает свет, то первый сектор может записаться, а второй нет) NTFS берет байты 510–511 и 1022–1023 записи и сохраняет их в специальный массив в заголовке: Fixup Array (Update Sequence Array). На их место записывается одинаковое контрольное значение (Update Sequence Number). При чтении NTFS проверяет, что в конце каждого сектора лежит это значение, и возвращает оригинальные байты на место. Если значения не совпали, значит запись порвана

Real Size - показывает реальный размер файла

Allocated Size - показывает сколько он занимает на диске. Разница между этими параметрами в том, что файл занимает целое число кластеров, поэтому Allocated Size всегда кратен размеру кластера (по умолчанию 4 КБ). Если реальный размер файла не делится нацело на размер кластера, то какая-то часть последнего кластера будет просто пустовать. Если нужно посмотреть размер файла - смотрим на Real Size, а если сколько места на диске занимает физически, то Allocated Size. Оба поля лежат в атрибуте $DATA (не путать с Used Size и Allocated Size из заголовка записи, они описывают саму MFT запись, а не файл)

Для маленьких файлов NTFS не выделяет кластеры в ФС, их содержимое сразу пишется в сам атрибут $DATA (такой атрибут называется Resident). Размер ограничен самой записью MFT: в 1024 байтах помимо данных лежат заголовок и другие атрибуты, поэтому на практике это порядка 700 байт. Так что если такой мелкий файл был удален, его можно получить сразу из MFT записи без необходимости доступа к диску (пока эта запись не была переиспользована под другой файл)

Думаю ни для кого не секрет, что при удалении файла MFT запись не стирается, а просто помечается удаленной посредством замены флага In Use на Deleted. Физически данные также остаются на диске, такие программы как recuva берут данные именно из MFT записей, а чтобы понять номера кластеров, в которых хранится файл читают атрибут Data Runs и проверяют, не заняты ли эти кластеры другим файлом, ведь их в любой момент фс может перезаписать.


$FILE_NAME

Смещение в $FNРазмерПолеОписание
0x00 – 0x078 байтParent Directory IDНомер родительской папки (нижние 6 байт — MFT ID, верхние 2 байта — Sequence).
0x08 – 0x0F8 байтCreated (C)Время создания файла (Windows FILETIME, UTC).
0x10 – 0x178 байтModified (M)Время изменения содержимого файла.
0x18 – 0x1F8 байтMFT Modified (B)Время изменения самой MFT-записи.
0x20 – 0x278 байтAccessed (A)Время последнего доступа к файлу.
0x28 – 0x2F8 байтAllocated SizeРазмер, выделенный под файл на диске (на момент создания записи имени).
0x30 – 0x378 байтReal SizeРеальный размер файла в байтах.
0x38 – 0x3B4 байтаFlagsАтрибуты (Read-only 0x1, Hidden 0x2, System 0x4, Archive 0x20, Directory 0x10000000).
0x3C – 0x3F4 байтаReparse TagТег точки повторной обработки (для симлинков и точек монтирования Junction).
0x401 байтName Length (N)Длина имени файла в символах UTF-16 (до 255 символов).
0x411 байтNamespaceПространство имён: 0 = POSIX, 1 = Win32, 2 = DOS 8.3, 3 = Win32 & DOS одновременно.
0x42 ..N × 2 байтFile NameИмя файла в кодировке UTF-16 Little Endian.

В $FILE_NAME содержатся даты создания, модификации, доступа и модификации MFT. Эти даты выставляются системой при создании, переименовании и перемещении файла, а при обычной записи в файл не меняются. Через winAPI их прочитать и изменить нельзя (GetFileTime и SetFileTime работают с $STANDARD_INFORMATION), прочитать можно только напрямую с тома с правами администратора, как это делают парсеры MFT. Поэтому в форензике этим датам доверяют больше

Размеры в $FILE_NAME (Allocated и Real Size) не обновляются при изменении файла, поэтому там часто нули или устаревшие значения, актуальные размеры лежат в $DATA

У файла обычно не один $FILE_NAME, а два (плюс еще по одному на каждую жесткую ссылку). Для совместимости со старыми программами (DOS и 16-битными), которые не понимают длинные имена, NTFS дополнительно хранит короткое имя в формате 8.3 (например, PROGRA~1). Оно лежит в отдельном $FILE_NAME с Namespace = 2, а длинное имя с Namespace = 1. Если имя файла и так укладывается в 8.3, то хранится один $FILE_NAME с Namespace = 3. На современных системах создание коротких имен можно отключить, поэтому второго $FILE_NAME может и не быть


$STANDARD_INFORMATION

Смещение в $SIРазмерПолеОписание
0x00 – 0x078 байтCreated (C)Время создания файла (FILETIME, 100-нс интервалы с 1 января 1601 г.).
0x08 – 0x0F8 байтModified (M)Время последнего изменения содержимого (Data Modified).
0x10 – 0x178 байтMFT Modified (B)Время изменения метаданных в записи MFT (Entry Modified).
0x18 – 0x1F8 байтAccessed (A)Время последнего чтения/доступа (в современных Windows часто отключено для скорости).
0x20 – 0x234 байтаDOS FlagsФлаги проводника Windows (Read-Only 0x1, Hidden 0x2, System 0x4, Archive 0x20).
0x24 – 0x274 байтаMax VersionsМаксимальное количество версий файла (обычно 0).
0x28 – 0x2B4 байтаVersion NumberНомер версии файла (обычно 0).
0x2C – 0x2F4 байтаClass IDИдентификатор класса (обычно 0).
0x30 – 0x334 байтаOwner IDИдентификатор владельца файла в системе дисковых квот NTFS.
0x34 – 0x374 байтаSecurity IDСсылка на дескриптор прав доступа (ACL) в системном файле $Secure.
0x38 – 0x3F8 байтQuota ChargedСколько места файл занимает в дисковой квоте (только NTFS 3.0+).
0x40 – 0x478 байтUSNНомер последней записи об этом файле в журнале $UsnJrnl (только NTFS 3.0+). Не путать с USN из Fixup.

В $STANDARD_INFORMATION также содержатся все даты файла, и именно их читает проводник и пользователь через winAPI. Также не составляет труда их поменять, в отличие от дат в $FILE_NAME. Здесь и находится дата модификации MFT записи. Как бы странно ни звучало, она обновляется всегда, когда обновляется MFT запись, т.е. когда вы изменяете файл, перемещаете его в другую папку, изменяете его флаги (Hidden, System, Read-only и так далее). Через SetFileTime эту дату выставить нельзя, ее ставит система, но через NtSetInformationFile ее тоже можно подменить, так что полностью доверять и ей нельзя

Поэтому, например, программа (распаковщик архива, копировщик) может выставить файлу через winAPI дату 2002 года. Она запишется в $STANDARD_INFORMATION, но при этом в $FILE_NAME окажется дата реального появления файла на диске. Если дата создания в $SI оказывается раньше, чем в $FN - это классический признак timestomping (подмены дат). Само по себе это еще не доказательство, так же будет выглядеть файл, перенесенный с другого тома или распакованный из архива с сохраненными датами, но это повод посмотреть на файл внимательнее

Параметр USN берется из журнала $UsnJrnl, там логируются все изменения файлов (создание, удаление, переименование, запись), но он фиксированного размера, поэтому при активном пользовании компьютером старые записи довольно быстро затираются и можно посмотреть только действия за последние 3-4 дня. Для быстрого анализа советую утилиту JournalTrace


Альтернативные потоки данных (ADS)

У некоторых файлов в NTFS есть скрытый поток данных ADS (Alternate Data Streams), впервые они появились еще в Windows NT 3.1 (1993 год) для совместимости с Macintosh (Apple). Технически это просто дополнительный именованный атрибут $DATA в той же MFT записи, а содержимое самого файла лежит в безымянном $DATA. Альтернативные потоки указываются после имени файла через двоеточие (file.txt:stream). Обычно альтернативный поток носит имя Zone.Identifier (Mark of the Web), но можно задать ему любое. Это один из самых важных артефактов, в него пишут архиваторы и браузеры место появления файла. Для архиваторов это архив из которого был распакован файл: ADS_Archive

Для файлов скачанных при помощи браузера из интернета это точный адрес ADS_Internet

Но конкретного url также может и не быть, firefox, например, записывает просто about:internet ADS_Firefox

Как вы могли заметить на всех скриншотах указан ZoneId=3. Это файлы из интернета. Всего зон пять: 0 - локальная машина, 1 - локальная сеть (интранет), 2 - доверенные сайты, 3 - интернет, 4 - ограниченные сайты. Но кроме тройки их сейчас практически не встретишь. Чего не скажешь о потоке, который создает системная служба Windows SmartScreen, это то самое синее или красное окошко, которое просит не запускать программу. Т.е. все файлы на которых есть этот поток проверялись SmartScreen, что является зацепкой при анализе SmartScreen

Если в ADS пишут в основном маленькие записи, как на скриншотах выше (они, кстати, тоже resident и лежат прямо в MFT записи), не значит, что туда нельзя записать что-то большое. В ADS спокойно записывается любой файл, даже исполняемый. В некоторых случаях кажется, что это поможет вирусу быть более скрытным, учитывая, что запуск из альтернативных потоков ломает некоторые forensic программы, например, в winPrefetchView запись отображаться не будет, т.к. prefetch запись создается в нестандартном виде: file:hash-evil.exe.pf (file - это легитимный пустой файл, в альтернативном потоке которого скрыт evil.exe).

В BAM, AppCompatCache этот бинарник светится как гирлянда, будет тяжело не заметить там обычный файл в котором через двоеточие прописан исполняемый.


Утилиты для анализа MFT

Для дампа MFT обычно используют MFTECmd от Эрика Зиммермана в связке с MFT Explorer от него же, инструменты довольно полезные, но для того чтобы быстро просмотреть содержимое MFT слишком тяжелые, поэтому я написал небольшой парсер на Rust + Egui, советую попробовать: Rmft на GitHub