EVTX ファイル形式を解説: チャンク、テンプレート、BinXML 内部
.evtx ファイルがバイト レベルでどうレイアウトされるか: ファイル ヘッダー、64 KB チャンク、テンプレート テーブル、それを参照する BinXML レコード ストリーム。
Windows イベント ログ形式 .evtx は、行指向の .evt を置き換えるため Vista とともに出荷されました。バイナリ、追記専用、チャンク化されたコンテナで、単一プロセス (EventLog サービス) が書き、満杯になるとローテーションされます。ほとんどのアナリストはバイト レベルでどうレイアウトされるかを知る必要はありません。未割り当て領域からレコードをカービングする、損傷したチャンクを手動でパースする、または切り詰められたファイルを読まないパーサーと議論する人は、大いに知っておく必要があります。
この記事は実用版です。壊れたパースをデバッグするには十分で、ゼロからパーサーを書くほどではない程度の内部詳細です。
ファイル ヘッダー
すべての .evtx は 4 KB のヘッダーで始まります。マジックは ElfFile\0\0。調査担当者にとって重要なフィールドは、バージョン、チャンク数、最も古いと現在のチャンク インデックス、ヘッダー自体に対する CRC32 です。ヘッダーはファイルがローテーションされるかチャンクが封緘されるたびに in place で書き換えられるので、Dirty と Full フラグは有用な手がかりです。Dirty セットのファイルは、ホストがクラッシュしたか、ディスク イメージが live で取得されたときに開いていたものです。
ヘッダーの後には固定サイズ チャンクのシーケンスが続きます。
チャンク: 各 64 KB
各チャンクはちょうど 65,536 バイトで、独自の 512 バイト ヘッダーを持ちます。チャンク マジックは ElfChnk\0。ヘッダーはチャンク内の最初と最後のレコードのログ レコード ID、ファイル オフセット、2 つの CRC32 (1 つはヘッダーに対する、1 つはレコード データに対する) を運びます。
チャンクは独立しています。未割り当て領域からチャンクを 1 つカービングし、ファイルの残りなしでパースできます。これが EVTX をディスク フラグメントから復元可能 にし、ファイル全体で圧縮するものよりフォレンジック ツーリングに優しい形式にする理由です。
チャンク内:
- 文字列テーブル。このチャンク内でインターンされた文字列、オフセットで参照される。
- テンプレート テーブル。このチャンク内のレコードが使う XML テンプレート、こちらもオフセット インデックス。
- レコード。BinXML レコードのストリーム、各々が 1 つのテンプレートとレコードごとの置換値を参照。
BinXML とテンプレート
EVTX レコードは XML テキストとして格納されません。BinXML として、XML ドキュメントのトークン化されたバイナリ表現で格納されます。スペースを節約するため、構造的なスケルトン (要素名、属性名、ツリー形状) は、チャンクのテンプレート テーブルに 1 度だけ格納されるテンプレートに分解されます。各レコードは「テンプレート ID 5 を、値 [alice、S-1-5-21-...、3、0xc000006a] と共に使用」と言います。
レコードの XML を再構築するために、パーサーは:
- レコードのトークン ストリームを読みます。
- チャンクのテンプレート テーブルで ID によりテンプレートを検索します。
- テンプレートのプレースホルダ位置にレコードごとの値を代入します。
- 結果の XML を出力します。
これがパーサー (本サイトのブラウザ パーサー、それがラップする Rust omerbenamram/evtx クレート、そして python-evtx すべてがこの要件を共有する) がチャンク ローカル コンテキストを追跡する必要がある理由です。テンプレート ID はファイルをまたいでグローバルではありません。2 つのチャンクは全く異なるテンプレート テーブルを持つことができ、同じテンプレート ID 番号がそれぞれで異なるものを意味します。
これがアナリストが xxd で引き出したレコードを手動でデコードしようとして「ガベージ XML」を見る最も一般的な理由でもあります。テンプレート テーブルなしでは、置換値はスキーマのない型付き値の袋に過ぎません。
封緘済み対ダーティ チャンク
EventLog サービスがチャンクの書き込みを終え次のものに移ると、チャンクの CRC32 を計算して書き、チャンクのヘッダーに Full をマークします。クリーンなファイルは、最後を除くすべてのチャンクがこの状態にあります。
Dirty チャンク (ファイル ヘッダーが最後に更新された後の最終変更時刻) は、ライブの末尾です。多くの場合パース可能ですが、ツールがレコード ストリームがトークンの途中で終了する可能性があるために読み取りを拒否することもあります。フォレンジックではこれが重要です: ライブ ホストから収集 されたバンドルは、アクティブ チャネルでダーティな末尾チャンクを持ち、そのチャンクに対するパーサーの動作を把握しておく必要があります。チャンクをスキップするか、ファイルでエラーにするか、できるところを復元するか?
EvtxECmd、hayabusa、python-evtx はすべて、寛容度に違いはありますが、ダーティ チャンクを復元します。ネイティブの Event Viewer は最も厳格で、他のものよりも頻繁にファイルを完全に拒否します。
実用的な含意
- 切り詰められた
.evtx(多くの場合ライブ ホストから収集 するときに発生する) は、たいてい大部分が復元可能です。完全な各チャンクは独立しています。 - 未割り当てからカービングされたチャンクは、合成ファイル ヘッダーでラップしてパースできます。これは libevtx と python-evtx が
pagefile.sys(pagefile parser 参照) と RAM dump カービング スイープから復旧する方法です。 - 1 つのチャンクのパース失敗はファイルの失敗を意味しません。堅牢なパーサーは次のチャンクに進み、不良なものは別途報告します。
- チャンク CRC32 が改ざんにフラグを立てます。CRC を再計算しない修正レコードは検知可能です。ほとんどの攻撃者はそうしません。ログをクリアする (1102 を発火) 方が簡単な経路だからです。慎重な人は Phant0m を使い、ファイル自体には触れません。
参考資料
- Andreas Schuster: Introducing the Microsoft Vista Event Log File Format (元のリバース エンジニアリング作業)
- libevtx ドキュメント (野生で最も徹底した形式仕様)
- omerbenamram/evtx (Rust パーサー、WASM ビルドは本サイトのブラウザ パーサーを駆動)