Журнал транзакцій NTFS ($LogFile)¶
Що це таке і чому це важливо¶
NTFS — файлова система, яку Windows зазвичай використовує для системного тому. Щоб відновлюватися після збою, вона
записує зміни, які збирається внести в метадані файлової системи, у прихований файл тому з назвою
$LogFile. Кожне оновлення може містити байти redo (новий стан) і байти undo (попередній
стан, потрібний для відкату зміни). Цей журнал відокремлений від журналів подій Windows, файлів .LOG1 реєстру
та журналу USN.
Для слідчого попередній стан особливо корисний. Запис undo може зберігати ім'я файлу
до перейменування, видалений запис каталогу або часові позначки до оновлення метаданих.
Деякі записи також містять байти, які NTFS записала до журналу USN, зокрема записи, яких уже немає в
зібраному $J. Однак багато транзакцій стосуються звичайної активності операційної системи і не
описують атаку.
Приклад: видалений запис індексу згадує report.txt; пізніше доданий запис згадує
report.txt.encrypted. Зіставлення повного посилання на файл, батьківського каталогу, ланцюжка транзакцій та інших
артефактів може підтвердити перейменування. Самі лише ці два імені не ідентифікують програму-вимагача, її оператора
чи точний час шифрування.
Розташування та збирання¶
Кожен том NTFS має власний журнал. Отримуйте його інструментом, який читає метадані NTFS або експортує їх
із криміналістичного образу чи знімка; звичайне копіювання файлів у Windows може не мати до нього доступу. Збирайте
$MFT і $Extend\$UsnJrnl:$J того самого тому якомога ближче в часі. Зберігайте вихідні
файли та хеші. Пізніше отриманий журнал може містити інший відрізок активності.
$LogFile — це циклічний журнал: нові записи перезаписують старі. Тривалість зберігання залежить від розміру журналу
та навантаження і може становити лише хвилини або години. Відсутність імені файлу чи операції не є
доказом того, що їх ніколи не було.
Що доводить запис, і застереження¶
- Оновлення фіксує метадані, які NTFS записала в журнал для відновлення. Окремий запис redo не доводить, що вся дія вищого рівня завершилася або що її ініціювала певна особа. Зіставляйте пов'язані записи, завершення транзакції, результуючу файлову систему та незалежні докази.
- Загальної часової позначки транзакції немає. Вбудовані часові позначки описують метадані файлу, іноді застарілі, скопійовані
або змінені зловмисником. Не наносьте
CreatedOnна хронологію інциденту як час перейменування чи зміни часової позначки. Записи USN можуть надати окрему часову позначку. - LSN (log sequence numbers, порядкові номери журналу) упорядковують записи в межах журналу одного тому. Це не час годинника, і їх не можна порівнювати між хостами чи томами. Записи зі старіших проходів циклічного буфера можуть зберігатися поруч із новішою активністю.
- Комірка таблиці транзакцій використовується повторно. Групуйте за
TransactionFirstLsn,TransactionId, ідентичністю клієнта та джерелом, а не лише заTransactionId. Ланцюжки можуть вказувати за межі збережених даних. - Посилання на файли містять номер запису і порядковий номер. Інакше повторне використання запису MFT може змусити не пов'язані між собою файли виглядати як той самий файл.
- Шляхи, надані
$MFT, описують стан зібраної MFT, а не відтворений історичний шлях. Juntei зіставляє порядкові номери й явно залишає невідомі компоненти. Батьківський каталог міг бути перейменований після транзакції, а жорсткі посилання можуть давати більше одного поточного імені. TargetInferredозначає, що зіставлення відкритого атрибута походить з іншого відрізка журналу. Ставтеся до такого зіставлення з додатковою обережністю. Невизначені записи індексу можуть бути декодовані лише за їхньою структурою; перевіряйте їхні імена за необробленими байтами та іншими джерелами.- Розірвана (torn) сторінка пропускається; читабельні сусідні записи залишаються доступними.
Truncatedпозначає запис, продовження якого відсутнє. Перш ніж вважати вивід повним, перегляньте діагностику парсера. Вхідні дані нульової довжини — помилка; незаписаний журнал, що повністю складається зFF, закономірно не має рядків. - Парсер не відтворює транзакції для реконструкції історичної MFT. Він не відновлює довільний нерезидентний вміст файлів і не виконує жодних зібраних даних. Сторінки із захистом DAX CRC не підтримуються. Перевірка на реальних доказах переважно охоплює сторінки журналу розміром 4 KiB.
Аналіз за допомогою Juntei¶
# Single log, current paths from an adjacent $MFT when present
juntei parse-ntfs logfile -f '$LogFile' -o logfile.csv
# Explicit MFT from the SAME volume
juntei parse-ntfs logfile -f '$LogFile' -m '$MFT' -t jsonl -o logfile.jsonl
# Multiple collected logs; SourceFile distinguishes every input
juntei parse-ntfs logfile -d evidence -o collected-logs.csv
# Preserve the underlying payloads for low-level validation
juntei parse-ntfs logfile -f '$LogFile' --raw-data -t jsonl -o logfile-raw.jsonl
# Automatic per-computer output
juntei auto-parse -d evidence -o parsed
Усі параметри див. у довіднику команд. Пошук
у каталогах приймає $LogFile, LogFile, їхні форми .bin та імена на кшталт C_$LogFile або
C_$LogFile.bin без урахування регістру. Окремий файл, переданий через -f, може мати будь-яке ім'я.
Автоматичне зіставлення з MFT враховує префікс тому в експорті: C_$LogFile використовує C_$MFT, а
D_$LogFile.bin спершу пробує D_$MFT.bin, потім D_$MFT. Журнали з префіксом ніколи не використовують простий $MFT
як запасний варіант. LogFile без префікса також приймає MFT або $MFT. Використовуйте -m разом із -f, щоб явно вибрати
відповідну MFT, якщо ваш експорт має іншу схему іменування.
Вивід містить один запис на LSN, включно із записами оновлень, контрольних точок і невідомих клієнтів. JSON — це
потік відформатованих об'єктів; JSONL містить один об'єкт на рядок. CSV має ті самі назви полів;
масиви та структуровані поля подаються як компактний JSON усередині комірки CSV. Невідомі скалярні значення порожні в
CSV і мають значення null у JSON. Порожні масиви означають, що жодного елемента не декодовано, а не доказ того, що їх не існувало.
Одночасно в пам'яті зберігається один цілий журнал і його проаналізовані записи. Визначення шляхів MFT потребує додаткової
пам'яті та роботи. --raw-data може значно збільшити розмір виводу. В auto-parse використовуйте -T, щоб
обмежити кількість паралельних процесів парсерів на комп'ютері з обмеженою пам'яттю.
Якщо суміжну MFT неможливо прочитати, Juntei видає попередження та експортує журнал без шляхів; MftSource
тоді залишається null. Ця невдача збагачення не відкидає читабельних первинних доказів.
Відсутні або неоднозначні збіги MFT також спричиняють попередження та лічильник Logs without a matching MFT
у підсумку; вони не вважаються збоями аналізу.
Поля виводу¶
Ідентичність, цілісність і походження запису¶
| Поле | Значення та інтерпретація |
|---|---|
SourceFile, FileOffset |
Вхідний шлях і зміщення в байтах, за яким було прочитано заголовок цього запису; використовуйте їх для перевірки байтів. |
LSN |
Порядковий номер запису в журналі. Великі значення — цілі числа, а не часові позначки. |
PreviousLSN, UndoNextLSN |
Посилання на попередній запис і запис відкату. Нуль означає відсутність посилання; ненульові посилання можуть вказувати за межі збережених даних. |
TransactionID, TransactionFirstLSN |
Комірка таблиці транзакцій і найраніший LSN, пов'язаний із цією транзакцією; сама комірка не є унікальною. |
RecordType |
ClientRecord (оновлення), ClientRestart (контрольна точка) або Other. |
ClientIndex, ClientSequence |
Ідентичність клієнта журналу; зазвичай NTFS. Це не обліковий запис користувача і не ідентифікатор процесу. |
Flags, SpansPages |
Необроблені прапорці запису і те, чи продовжуються дані клієнта на іншій сторінці. Охоплення кількох сторінок — нормальне явище. |
Truncated |
True, якщо парсер виявив відсутнє продовження для цього LSN. Не підсумовує всі пошкодження в усьому файлі. |
ClusterSize, ClusterSizeSource |
Кількість байтів у кластері, що використовується для зміщень цілі; джерело — Caller, Checkpoint або Inferred. Якщо справжнє значення відоме, вкажіть його через --cluster-size. |
LogVersion |
Версія з останньої читабельної сторінки перезапуску, зазвичай 1.1 або 2.0; null, якщо жодна читабельна сторінка перезапуску не збереглася. |
MFT-Source |
MFT, використана для збагачення поточними шляхами, якщо вона є. Вона має належати тому самому тому. |
У JSON/JSONL з auto-parse додаються Comp і Artifact, як і для інших парсерів. Журнал
завдання .txt містить діагностику; маніфести показують, чи завдання виконано успішно, із помилкою, чи його пропущено.
Пошкодження, які можна обійти, можуть дати успішне завдання з ненульовим лічильником Parse problems у його журналі.
Операції та ціль¶
| Поле | Значення та інтерпретація |
|---|---|
RedoOperation, UndoOperation |
Назви операцій NTFS або шістнадцяткове значення для невідомого коду. Redo описує заплановане оновлення; undo — обернену до нього операцію. |
TargetEntryNumber, TargetSequenceNumber |
Для операції із записом файлу: номер ураженого запису MFT без виведеного порядкового номера. В інших випадках: посилання на файл, якому належить визначений відкритий атрибут. Вбудоване оновлення індексу каталогу спрямоване на каталог, а не на дочірній елемент, описаний його записом. |
TargetAttributeType, TargetAttributeName |
Числовий тип і назва відкритого атрибута, використаного операцією. Для операцій із записами файлів це зазвичай $DATA з $MFT. Поширені типи: 16 ($STANDARD_INFORMATION), 48 ($FILE_NAME), 128 ($DATA), 144 ($INDEX_ROOT), 160 ($INDEX_ALLOCATION), 176 ($BITMAP). |
TargetPath |
Поточний шлях MFT, якщо повне посилання на ціль відоме, або невідомі компоненти. Порожнє для операцій із записами файлів, історичне покоління яких невідоме; сам лише номер запису не повинен визначатися як його поточний власник. |
OpenAttributeEntryNumber, OpenAttributeSequenceNumber, OpenAttributePath |
Файл, якому належить відкритий атрибут, через який операція виконує запис, з його поточним шляхом MFT. Для операцій із записами файлів ці поля зберігають контекст $MFT окремо від ураженого запису. |
TargetAttribute |
Зміщення в таблиці відкритих атрибутів журналу; відрізняється від типу атрибута або ідентифікатора MFT. |
TargetInferred |
True, якщо зіставлення цілі походить з іншого відрізка журналу. Ціль null залишається невизначеною, навіть якщо цей прапорець false. |
TargetVCN, Lcns |
Віртуальний кластер у цілі та номери фізичних кластерів; корисні для низькорівневої перевірки, а не для визначення часу. |
RecordOffset, AttributeOffset |
Зміщення атрибута/запису та оновлення в ньому. |
ClusterBlockOffset, TargetBlockSize |
Зміщення та розмір цілі в одиницях по 512 байтів. |
OffsetInTarget |
Обчислене зміщення в байтах в ураженому записі файлу або нерезидентному потоці. Залежить від розмірів кластера та запису MFT. |
RedoLength, UndoLength |
Заявлені довжини даних. Операція обнулення може заявляти довжину, не містячи такої кількості байтів корисного навантаження. |
RedoBytesAvailable, UndoBytesAvailable |
Байти корисного навантаження, фактично доступні для декодування. Порівнюйте із заявленими довжинами та семантикою операції. |
Зручні стовпці метаданих файлу¶
Ці стовпці показують одне пріоритетне ім'я файлу не у форматі DOS зі сторони redo, а за його відсутності — зі сторони undo.
Якщо жодна сторона не містить імені файлу, поля оновлення часових позначок надають перевагу часовим позначкам redo, потім undo.
Оновлення записів каталогу також безпосередньо показують свої час, розмір і прапорці, надаючи перевагу redo перед
undo. Декодовані атрибути $DATA показують свій розмір, навіть якщо імені файлу немає. Розмір безіменного $DATA
декодованого запису файлу має пріоритет над можливо застарілим розміром з імені файлу.
Ці стовпці — допоміжний засіб для перегляду; для повного порівняння «до/після» використовуйте окремі поля сторін.
| Поле | Значення та інтерпретація |
|---|---|
MetadataSide |
Redo або Undo — сторона, що надає зручні стовпці імені файлу/часових позначок. |
EntryNumber, SequenceNumber |
Файл, описаний декодованими метаданими імені файлу/запису файлу/атрибута або частковим оновленням часових позначок $STANDARD_INFORMATION, з порядковим номером, якщо він відомий. Порожнє для оновлення запису каталогу, яке не ідентифікує дочірній елемент, а також для необроблених оновлень розміру/списку відрізків (run list). Для ураженого запису використовуйте TargetEntryNumber. |
Filename |
Пріоритетне декодоване ім'я; усі імена залишаються в масивах сторін, включно з псевдонімами DOS і жорсткими посиланнями. |
ParentEntryNumber, ParentSequenceNumber, ParentPath |
Посилання на батьківський каталог і поточний шлях MFT; не реконструкція історичного шляху. |
CreatedOn, ContentModifiedOn, RecordModifiedOn, LastAccessedOn |
Час метаданих в UTC з точністю до 100 нс. Це не часові позначки транзакцій. Відсутній/некоректний час залишається порожнім/null. |
FileSize, FileAttributes |
Розмір і числові прапорці Windows з вибраних метаданих; значення записів каталогу можуть бути застарілими. Атрибути $DATA і декодовані записи файлів можуть надати безпосередніший розмір. |
AttrType |
Числовий тип декодованого запису атрибута; 48 для декодованого оновлення метаданих запису каталогу. Відрізняється від відкритого атрибута, на який спрямована операція. |
Повні метадані та корисні навантаження redo/undo¶
| Поле | Значення та інтерпретація |
|---|---|
RedoFileNames, UndoFileNames |
Масиви всіх декодованих $FILE_NAME на цій стороні, що зберігають розрізнення доданих/нових і видалених/старих імен. |
RedoTimes, UndoTimes |
Часові позначки $STANDARD_INFORMATION, записані кожною стороною; часткові оновлення залишають незмінені часові позначки відсутніми, а не вгадують їх. |
RedoDecoded, UndoDecoded |
Структуровані відомості про запис файлу, атрибут, запис індексу або запис каталогу, ідентифіковані за Kind. Null може означати непідтримувану операцію або недостатню кількість байтів. |
JournalWriteOffset, JournalWriteLength |
Зміщення та кількість байтів даних redo, що записують $DATA:$J. Це вказує на можливе джерело інакше втрачених записів USN; реконструйований журнал при цьому не експортується. |
Checkpoint |
Версія контрольної точки, початковий LSN, розмір кластера, а також LSN/довжина дампів таблиці відкритих атрибутів, назв атрибутів, «брудних» сторінок і таблиці транзакцій. |
OtherRecordType |
Необроблений тип запису клієнта для нерозпізнаного або недекодованого тіла. |
RedoData, UndoData, OtherData |
Шістнадцяткові корисні навантаження лише з --raw-data; інакше null/порожнє. Зберігайте їх для перевірки непідтримуваних або пошкоджених структур. |
Кожен об'єкт імені файлу містить посилання на файл і батьківський каталог (Raw, EntryNumber,
SequenceNumber), Name, NameEscaped, Namespace, поточний ParentPath, чотири часові позначки,
Size, AllocatedSize, Attr і ReparseValue. NameEscaped позначає некоректний UTF-16,
представлений екранованими послідовностями. Значення reparse може бути тегом точки повторного розбору (reparse tag) або розміром розширених атрибутів.
Об'єкти часових позначок зберігають і Utc, і цілочисельний Filetime; сигнальне або некоректне значення може
мати null-представлення UTC, зберігаючи при цьому своє вихідне ціле число. RedoTimes і UndoTimes
містять Created, Modified, Changed і Accessed; значення відсутніх членів — null.
Декодовані об'єкти FileRecord містять номер, порядковий номер, кількість жорстких посилань, прапорці, базове посилання,
ознаку того, чи зберігся лише заголовок, і наявні атрибути. Кожен декодований атрибут зберігає
тип, назву, резидентність, прапорці, ідентифікатор, розмір і виділений розмір. $STANDARD_INFORMATION додає
чотири часові позначки, атрибути, ідентифікатори власника/безпеки, квоту та USN; ці ідентифікатори потребують окремого визначення і
не доводять, хто з користувачів змінив файл. Записи індексу містять прапорці, довжину ключа та
необов'язкове ім'я файлу. Оновлення записів каталогу зберігають чотири часові позначки, розміри, атрибути та значення
reparse, навіть якщо саме ім'я не було записано в журнал.
Робочий процес розслідування¶
- Перегляньте діагностику щодо відсутніх сторінок перезапуску, розірваних сторінок і усічених записів.
- Шукайте підозріле ім'я файлу або розширення як у
RedoFileNames, так і вUndoFileNames. - Зіставляйте номери записів і порядкові номери, потім досліджуйте ланцюжок LSN та операції транзакції.
- Порівнюйте часові позначки redo та undo, щоб виявити зміни. Звичайне копіювання, розпакування та відновлення також можуть змінювати часові позначки; розбіжність — це зачіпка, а не автоматичний вердикт про timestomping.
- Зіставляйте з журналом USN, $MFT і $I30. Використовуйте журнали подій, Prefetch і артефакти активності користувача, щоб встановити виконання або атрибуцію.
Наприклад, знайдіть усі записи, що містять зашифроване ім'я файлу на будь-якій стороні:
jq 'select(any(.RedoFileNames[]?, .UndoFileNames[]?;
.Name | endswith(".encrypted"))) |
{Lsn, TransactionId, TransactionFirstLsn, RedoOperation, UndoOperation,
RedoFileNames, UndoFileNames}' logfile.jsonl
Це пошук за іменем файлу, а не повноцінний детектор програм-вимагачів. Щоб дослідити корисні навантаження журналу,
експортуйте з --raw-data і виберіть записи з ненульовим (non-null) JournalWriteOffset; шістнадцяткові дані RedoData
представляють байти, записані за цим зміщенням. Аналіз і усунення дублікатів цих вбудованих записів USN
— окремий крок. Juntei поки що не об'єднує їх із виводом parse-ntfs usn-journal.
Додаткові відомості¶
- Крейт logfile з ys-ntfs-parser: підтримувані структури, корпус для перевірки та відомі обмеження.
- Sootmark ntfslog: вихідна (upstream) реалізація на Rust.
- Документація формату NTFS від libfsntfs: сторінки журналу, виправлення (fixups) і структури метаданих.
- dfir_ntfs: незалежне дослідження та реалізація аналізу NTFS.
- LogFileParser від Joakim Schicht: детальний аналіз журналу транзакцій.
- ntfs-linker від Stroz Friedberg: зіставлення джерел NTFS в історію файлів.
- DFIR Artifact Museum: реальні зразки NTFS з Windows.
- Velocidex go-ntfs: реалізація NTFS і публічний тестовий том.