USN ジャーナルを読む時間が少しでもあれば、reason ビットマスクは EVTX EventID と同じように体に染みついてきます。これは NTFS が各 USN_RECORD_V2 にセットして、ファイルについて何が変わったかを要約する 32 ビットのフィールドです。ビットは加算的であり、相互排他的ではありません。これが最初に身につけるべきことです。一つのレコードは FileCreate | DataExtend | Close を運ぶことができ、その組み合わせはどのビット単独とも違う意味を持ちます。
以下は、私がモニターの横の付箋に貼っているフィールドリファレンスです。
実際に出会う順に並べたビット
| フラグ | Hex | 実用上の意味 |
|---|---|---|
DataOverwrite | 0x00000001 | メインデータストリームの一部がその場で上書きされた |
DataExtend | 0x00000002 | メインデータストリームが拡張された |
DataTruncation | 0x00000004 | メインデータストリームが切り詰められた |
NamedDataOverwrite / Extend / Truncation | 0x10 / 0x20 / 0x40 | 上記三つと同じ、ただし代替データストリーム上で |
FileCreate | 0x00000100 | 新しいファイルまたはディレクトリが作成された |
FileDelete | 0x00000200 | ファイルが名前空間からリンク解除された |
EaChange | 0x00000400 | 拡張属性が変更された |
SecurityChange | 0x00000800 | ACL/オーナーが変更された |
RenameOldName | 0x00001000 | リネームの「前」の半分 |
RenameNewName | 0x00002000 | リネームの「後」の半分 |
IndexableChange | 0x00004000 | インデックスフラグが切り替えられた |
BasicInfoChange | 0x00008000 | タイムスタンプ、属性、圧縮フラグが書き換えられた |
HardLinkChange | 0x00010000 | ハードリンクが追加または削除された |
CompressionChange | 0x00020000 | NTFS の圧縮が切り替えられた |
EncryptionChange | 0x00040000 | EFS の状態が変更された |
ObjectIdChange | 0x00080000 | オブジェクト ID が設定またはクリアされた |
ReparsePointChange | 0x00100000 | リパースポイントが追加/変更/削除された |
StreamChange | 0x00200000 | 代替データストリームが追加、リネーム、削除された |
Close | 0x80000000 | 変更を生んだハンドルが解放された |
Close ビットは独立した段落に値します。これを読み違えるとイベント件数が桁違いに狂います。NTFS は同じハンドル下の連続する操作を併合し、フラッシュしながら中間レコードを発行し、ハンドルがなくなった時点で Close がセットされた最終サマリレコードを発行します。中間レコードは実在しますが二重カウントされます。パース済みのジャーナルをトリアージする際は、まず Close 付きのレコードをフィルタして、ハンドルあたり一行に操作を重複排除します。サブハンドル粒度が必要なときだけ非 Close レコードを引きます。
暗記する価値があるほど頻発するパターン
reason の組み合わせの中には、調査を重ねるうちに文字単位ではなく単語として読めるようになるくらい頻発するものがいくつかあります。
アプリが書いた新しいファイル。 FileCreate | DataExtend の後、ファイルが成長する間に DataExtend | Close レコードが続き、最後に BasicInfoChange | Close で締めくくられます。最後のレコードは、クローズ時にファイルが mtime をスタンプされる瞬間です。
ディレクトリをまたぐリネーム。 同じ FileReferenceNumber を共有する 2 つのレコード: RenameOldName | Close の後に RenameNewName | Close。2 つの間で親参照が異なります — そのデルタが移動を復元する方法です。リネームビットのどちらか一方だけでフィルタすると、半分のストーリーを失います。
アトミックな save-by-rename。 Office、VS Code、Notepad++、バックアップ付きの vim、ほぼすべての現代のエディタ。一時ファイル (~$report.docx、report.docx.tmp、.swp など) に対する FileCreate、一つ以上の DataExtend レコード、元のファイルへの FileDelete | Close、そして元の名前を指す一時ファイルへの RenameNewName | Close が見えます。全シーケンスは単一の壁時計秒内に発生します。
ランサムウェアの暗号化→リネーム。 メガバイト単位で各ファイルをカバーする持続的な DataOverwrite レコード、続いて均一なサフィックスや固定長のランダム拡張子を持つ RenameOldName / RenameNewName のペア。DataOverwrite のレートとリネーム上の同一拡張子クラスタが、診断の二つの半分です。プレイブックは ランサムウェア検出記事 にあります。
アンチウイルスの検疫。 同じ MFT エントリに対する最近の FileCreate の直後に続く FileDelete | Close。AV エージェントがファイルを制限付きディレクトリに移動するため、その間に SecurityChange を伴うことがよくあります。ジャーナルは、バイナリ自体が消えた後も AV がファイルに触れた証拠を保持します。
クラウド同期のアップロード。 新しい親が \Users\<u>\OneDrive\、\Dropbox\、\Google Drive\ で、古い親がユーザプロファイルである RenameNewName | Close。同期エージェント自身は、ダウンロード について同期フォルダ内ファイルに対する DataExtend | Close レコードを生成します。アップロードはフォルダへのリネームに見える傾向があります。
Timestomping。 同じ FileReferenceNumber 上で周囲に DataOverwrite、DataExtend、FileCreate、リネームを伴わない裸の BasicInfoChange | Close。正当な touch はタイムスタンプ更新を引き起こした書き込みとペアになります。詳細に展開しているのが timestomping 記事 です。
ストリーム注入 / ADS 悪用。 単独の StreamChange は通常、ブラウザダウンロードからの Zone.Identifier を示します (元ファイルの FileCreate とペア)。Downloads\ から離れた、特に署名済みシステムバイナリ上の同じ FileReferenceNumber における繰り返しの StreamChange レコードは、調査する価値があります。
ビットが決して教えてくれないこと
reason フィールドはファイルに関するもので、アクターに関するものではありません。ユーザ、PID、コマンドライン、整合性レベルはありません。操作にアクターを紐付けるには、常にどこか別の場所に行く必要があります: Security.evtx 4663 (オブジェクトアクセス。SACL が設定されていた場合のみで、通常は設定されていません)、Sysmon イベント 11 (ファイル作成) とイベント 1 (コマンドライン付きのプロセス作成)、または適切な瞬間にバイナリをディスク上に置く プロセス実行証拠 と Shimcache のエントリです。
ビットはコンテンツについても何も教えません。ジャーナルは領域が上書きされたことを知っていますが、以前のバイトが何だったかは知りません。それには $LogFile の before-image またはボリュームシャドウコピーが必要です。
通用する読み方のレシピ
パース済みのジャーナルをコールドで開くとき:
- まず
FileCreateでフィルタします。ウィンドウ内で本当に新規なものは何ですか? ユーザが書き込み可能なディレクトリ (\AppData\Local\Temp\、\Users\Public\、既知のベンダーサブディレクトリ外の\ProgramData\、\PerfLogs\) のパスを探します。 RenameNewName | Closeに切り替えて、新しい拡張子や新しい親でグループ化します。save-by-rename、クラウド同期の移動、ランサムウェアのサフィックスがここに現れます。- 分単位で
DataExtendをバケットします。一括書き込み — バックアップ、暗号化、流出アーカイブの成長 — は、それ以外は静かなベースラインに対する持続的なスパイクとして現れます。 Close付きレコードを最初に読みます。中間レコードは独立したイベントではなく、補助的な証拠として扱います。- 候補ファイルを得たら、同じ
FileReferenceNumber上のすべてのレコードをグループ化して、ライフサイクル全体を時系列で読みます。その系列がほぼ常に、レポートに入る部分です。
本ページのパーサは reason フィルタリングを直接公開しているので、ステップ 1〜3 は数クリックです。ステップ 5 には FileReferenceNumber でソートし、その後 USN でソートします。
参考資料
- Microsoft Learn — USN_RECORD_V2 リファレンス は、すべてのビット定義を出典から直接カバーしています。
- Eric Zimmerman の MFTECmd — その
$Jパーサは、スプレッドシートのピボットに耐えるフラットな CSV に reason ビットマスクを浮上させます。 - Airbus CERT の usnrs — オープンソースの Rust 実装。ビットレイアウトを身に染み込ませる最短の道は
src/reason.rsを読むことです。