方法論

USN Parser が $J のバイト列を構造化レコードに変換する仕組み — アルゴリズム、検証ステップ、既知の限界。

このページは USN Parser が入力に対して何を行い、なぜ出力を信頼してよく、どこに限界があるかを文書化します。フォレンジック チェーン オブ カストディの裏付けに使えるよう、レポートで USN Parser を引用する場合に参照すべきページです。

入力

  • $UsnJrnl:$J: 必須。ボリューム ルートの \$Extend\$UsnJrnl から抽出されたスパースな代替データ ストリーム。必ず $J ストリームでなければなりません — $Max はサイズ メタデータのみです。
  • $MFT: 任意。同一ボリュームのマスター ファイル テーブル。親ディレクトリ参照をフル パスに解決する目的にのみ使用します。

両ファイルともメモリに読み込み、WebAssembly パーサに渡します。ブラウザは postMessage と明示的な転送リストでバイト バッファを worker に手渡します — メモリに残る不用なコピーを避け、高速です。

パーシング アルゴリズム

usnrs (Airbus CERT、Apache-2.0) を wasm32-unknown-unknown 向けにコンパイルします。Microsoft の NTFS リファレンス に記載の USN_RECORD_V2 構造を実装します。

リーダの動作:

  1. 先頭のスパースなゼロ領域を、最初の非ゼロ バイト(最初のレコードの開始)までスキップ。
  2. 前進しつつ、各オフセットで 4 バイトの RecordLength を読み、次のレコードへ進む。
  3. 4 バイトのゼロ列に出会った時点で停止(有効レコードの末尾)。
  4. レコードごとに、major/minor が 2.0 であることを検証し、固定長ヘッダと可変長ファイル名をデコード。

$MFT が与えられた場合は、親参照のチェーンをたどってフル パスを解決します。解決後の末端名がジャーナルの filename と一致する場合に限りフル パスを採用します — 古い MFT エントリが誤った確信のパスを生むことを避けるためです。

出力

各レコードが公開する項目:

  • usn (u64): ジャーナル内の位置
  • timestampMs (i64): Windows FILETIME を UTC の Unix ミリ秒に変換した値
  • filename (UTF-16 をデコードしたもの)
  • fullPath (string または null): $MFT が与えられ、かつ一致した場合のみ
  • reasons (string[]): Reason ビットマスクを可読名に展開した配列
  • attributes (string[]): ファイルに立っている NTFS 属性
  • mftEntrymftSequenceparentMftEntryparentMftSequence

出力はタブを閉じるまでブラウザのメモリに留まります。

限界

  • USN_RECORD_V3 未対応。 V3 (ReFS で導入された可変長ファイル参照) は通常の NTFS ボリュームでは生成されないため、実務ではほぼ問題になりません。
  • リング バッファの巻き戻しは不可視: 最も古いレコードより前に何が上書きされたかは分かりません。さらに古い履歴が必要なら $LogFile とボリューム シャドウ コピーを併用してください。
  • タイムスタンプの検証なし: レコード中の FILETIME を信頼します。タイムスタンプ改ざんを検出するには、SI 対 FN の比較記事$MFT との標準的なクロス チェックを示します。

チェーン オブ カストディ

レポートに引用する参照としては、USN Parser のバージョンは GitHub リポジトリのフッターに表示されるデプロイ コミット ハッシュで識別できます。wasm 成果物は wasm-pack build --release でソースから再現可能で、ソース リビジョンごとに .wasm のハッシュは安定します。