NTFS の Update Sequence Number ジャーナルは、Windows ホストでディスクに何がいつ触れたかを知る必要があるとき、私が真っ先に手を伸ばすアーティファクトです。Vista 以降に出荷されたすべての NTFS ボリュームで既定で有効になっており、無効化する人はほぼおらず、何か問題が起きるまで読まれることもほぼありません。この非対称性こそが、このアーティファクトを有用にしている理由です。
ボリューム上で行われるあらゆるファイル作成、削除、リネーム、切り詰め、属性切り替え、クローズは $UsnJrnl:$J にレコードを追記します。レコードは小さく(それぞれ数十バイト)、ファイル自体は固定サイズのリングバッファであるため、活動の活発なマシンでもおおよそ直近の数百万件の操作が保持されます。既定の 32 MB が割り当てられたデスクトップなら数日分、1 GB に拡張したファイルサーバなら数週間分です。
ジャーナルが実際に存在する場所
ジャーナルは通常のファイルではありません。ボリュームルートのメタデータファイル \$Extend\$UsnJrnl に付随する、$J という名前の代替データストリームです。初めて扱う人がつまずく点が二つあります。
- エクスプローラのドラッグ&ドロップ、
xcopy、robocopyは、空のデフォルトストリームを黙ってコピーするだけで、結果としてゼロバイトのファイルが残ります。ADS を理解するツールを使用してください。FTK Imager、X-Ways、The Sleuth Kit のicat、RawCopy.exe、PowerForensics などです。コンパニオン記事の ディスクイメージから$Jを抽出する方法 で各ツールを順に解説しています。 - 同じメタデータファイルには
$Maxという兄弟ストリームも存在します。これにはサイズや構成のメタデータしか入っていません。誤って$Maxをカービングしてしまうと、有用なものは何も得られません。常に$Jを選んでください。
ファイル自体はスパースです。NTFS は設定済みの最大サイズをディスク上に予約しますが、先頭の未使用領域はゼロのままにします。イメージから $J をカービングすると、最初の数百メガバイトは最初の本物のレコードに到達するまでほぼすべてゼロというのもよくあります。まともなパーサ (usnrs、PoorBillionaire の USN-Journal-Parser、MFTECmd、本ページの WebAssembly パーサ) はすべて、先頭のゼロを自動的にスキップします。
レコード構造 (USN_RECORD_V2)
各レコードは USN_RECORD_V2 構造体です。Microsoft は winioctl.h でレイアウトを文書化しています。フィールドごとの要約は以下のとおりです。
| フィールド | バイト | 意味 |
|---|---|---|
| RecordLength | 4 | ファイル名を含むレコードの総サイズ |
| Major / Minor version | 2 + 2 | 実運用で目にするのはすべて 2.0 |
| FileReferenceNumber | 8 | ファイルの MFT エントリ + シーケンス |
| ParentFileReferenceNumber | 8 | 親ディレクトリの MFT エントリ + シーケンス |
| USN | 8 | ジャーナル内でのレコード位置 |
| Timestamp | 8 | Windows FILETIME (1601-01-01 からの 100ns ティック) |
| Reason | 4 | 何が変化したかを示すビットマスク |
| SourceInfo | 4 | ヒント (例: OS 起因の変更を示す DATA_MANAGEMENT) |
| SecurityId | 4 | $Secure:$SII へのインデックス |
| FileAttributes | 4 | NTFS の標準属性 |
| FilenameLength / Offset | 2 + 2 | レコード内における UTF-16 ファイル名の位置 |
| Filename | n | UTF-16 LE、null 終端なし |
Reason ビットマスクは、その価値に見合った働きをするフィールドです。これは加算的であって排他的ではありません。一つのレコードが FileCreate | DataExtend | Close をまとめて運ぶこともあります。典型的なファイルのライフサイクルは FileCreate | DataExtend → DataExtend | Close → BasicInfoChange | Close → FileDelete | Close のような形になります。事後にこの系列を再構築することが、ファイルに実際に何が起きたかを語る方法です。reason コードの記事 は、すべてのビットに関するフィールドリファレンスです。
初学者が過小評価しがちなフィールドが二つあります。
FileReferenceNumberは結合キーです。これでレコードをグループ化すれば、リネーム、属性変更、削除を含むファイル全体のストーリーが得られます。上位 16 ビットはシーケンス番号である点に注意してください。削除されたファイルと同じ MFT エントリに割り当てられた新しいファイルは、エントリは共有しますがシーケンスが異なります。ParentFileReferenceNumberは、レコード内で 唯一 のパス情報です。ファイル名はリーフでしかありません。フルパスを得るには、親チェーンを辿るためにパース済みの$MFTが必要です。常に両方を取得してください。
DFIR で価値を発揮する理由
ほぼすべての案件で出てくる二つの理由があります。
第一に、ジャーナルは削除に耐えます。ファイルが名前空間から消え、MFT エントリが別の用途に再利用された後でも、その作成、成長、リネーム、削除の記録はリングバッファが上書きするまで $J に残ります。ゴミ箱 から空にされたファイルでも同様です。ゴミ箱の $I ファイルは消去されますが、ジャーナルは記憶しています。「このファイルが過去に存在したか」を追う場面では、ジャーナルはたいてい MFT エントリよりもゴミ箱のメタデータよりも長く生き延びます。
第二に、安価かつ自己記述的です。100 MB の $J は通常、数日から数週間分にまたがる 500 万から 1000 万件のレコードを保持します。各レコードは自身のタイムスタンプ、reason、ファイル参照、親参照を持ちます。数秒でパーサに通せて、任意のフィールドで切り替えられます。この値段で得られる他の Windows アーティファクトはありません。
ジャーナルが教えてくれないのは「誰が」です。どのレコードにもユーザ、プロセス、コマンドラインは記録されません。アクターを紐付けるには、タイムスタンプで Security.evtx のイベント 4663 (オブジェクトアクセス。ほぼ確実に未設定の SACL を必要とする)、Sysmon イベント 11 (ファイル作成)、または Sysmon イベント 1 のプロセスツリーと相関させます。ジャーナルが答えるのは「何が」と「いつ」です。「誰が」は他のログが答えます。
アーティファクトスタックでの位置づけ
私が使っているメンタルモデルは、「現在の状態」から「過去の履歴」へ並べたものです。
$MFT— 現在の状態のインデックス。ボリューム上のすべてのファイルとディレクトリ、最近削除された一部のエントリも含みます。各属性に四つのタイムスタンプ、履歴はありません。$UsnJrnl:$J— 操作単位の履歴で、数日から数週間分。本記事の主題です。$LogFile— NTFS のクラッシュリカバリ用トランザクションログ。数分から数時間分の生の before/after イメージです。USN ジャーナル vs MFT vs LogFile で、どれを選ぶべきかを整理しています。
ジャーナルを Prefetch、AmCache、Shimcache、LNK ファイル、ジャンプリスト と並べれば、何が実行され、何が開かれ、何がディスクに触れたかを分単位で復元できることが多いです。
次に何を読むか
ジャーナルをまだ開いたことがないなら、感覚をつかむ最速の方法は、テスト機から一つ取得して本ページ上部のパーサに対応する $MFT と一緒に読み込ませることです。FileCreate でフィルタをかけて、Windows が書いたとおりにファイルを読んでみてください。本シリーズの以降の記事では、reason フラグ、抽出手順、そしてランサムウェア、流出、timestomping、削除ファイルの復元についてジャーナルが明らかにするパターンを掘り下げていきます。
参考資料
- Microsoft Learn — Change Journals と USN_RECORD_V2 リファレンス。
- Airbus CERT の usnrs — 最も洗練されたオープンソースの
USN_RECORD_V2パーサで、本サイトの WebAssembly モジュールの中核でもあります。 - Brian Carrier 著 File System Forensic Analysis — いまだに代替が出ていない NTFS 内部構造の書籍級の決定版です。