OLE2 / CFB ファイル形式
Compound File Binary 形式 — .doc/.xls/.ppt と .msi インストーラーの土台となる旧来のコンテナ。
- MIME タイプ
- application/x-ole-storage
- 拡張子
- .doc, .xls, .ppt, .msi
- カテゴリ
- ドキュメント
マジックナンバー(ファイル署名)
| 0x00000000 | D0CF11E0A1B11AE1 | ········ | CFB / 'docfile' magic |
OLE2 / CFB 形式について
Compound File Binary 形式は、1 つのファイルに丸ごと収まったファイルシステムです。セクタがあり、アロケーションテーブルがあり、名前付きストリームのディレクトリツリーがあり、小さなストリーム専用の 2 つ目のアロケーション方式まであります。保存のたびに文書全体を書き直さずに、独立した部品をいくつも 1 ファイルに収められるよう Microsoft が作ったもので、Word 97 の文書から Windows Installer のパッケージ、Outlook の .msg まで今もこれに乗っています。読むにはバイトを前方に走査するのではなくセクタ番号の鎖をたどることになり、テキストが無圧縮で入っているのに .doc の hex ダンプがノイズにしか見えないのはそのためです。
ヘッダーの構造(バイト単位)
| オフセット | 長さ | フィールド | 意味 |
|---|---|---|---|
| 0 | 8 | Header signature | 常に D0 CF 11 E0 A1 B1 1A E1。先頭 4 バイトは狙ったダジャレで、D0CF11E は leetspeak で 'docfile' と読めます。拡張子については何も示唆しません。.doc も .xls も .ppt も .msi も .msg も、この同じ 8 バイトで始まります。 |
| 8 | 16 | Header CLSID | 予約されたクラス識別子で、仕様はすべてゼロであることを要求しています。ここが非ゼロなら、正当な派生というより手作りか意図的に壊されたファイルの兆候です。 |
| 24 | 2 | Minor version | リトルエンディアンで、実際に出回っているファイルではほぼ 0x003E です。機能上の影響はなく、読む側は無視します。 |
| 26 | 2 | Major version | 0x0003 か 0x0004 で、こちらは重要です。バージョン 3 はセクタ 512 バイトでストリームサイズは 32 ビット、バージョン 4 はセクタ 4096 バイトでストリームが 4 GiB を超えられます。Office 文書はほぼすべてバージョン 3 です。 |
| 28 | 2 | Byte order mark | FE FF、つまりリトルエンディアン。ビッグエンディアンは結局仕様化されなかったので実質は定数であり、ファイルがずれているのではと疑ったときの 2 つ目の健全性チェックとして使えます。 |
| 30 | 2 | Sector shift | セクタサイズの 2 を底とする対数です。メジャーバージョン 3 の 512 バイトセクタなら 0x0009、バージョン 4 の 4096 バイトセクタなら 0x000C。セクタ N はバイトオフセット (N + 1) × セクタサイズから始まります。セクタ番号が 512 バイトのヘッダの後ろから数え始めるためです。 |
| 32 | 2 | Mini sector shift | ミニセクタサイズの 2 を底とする対数で、0x0006(64 バイト)と定められています。ミニストリームがあるおかげで、小さなプロパティストリームを何百も持つファイルが 1 つあたり 0.5 KB も無駄にせずに済みます。 |
| 34 | 6 | Reserved | ゼロを書き、読む側は無視すべき 6 バイト。後続のフィールドを 4 バイト境界に揃えるためだけに存在します。 |
| 40 | 4 | Number of directory sectors | ディレクトリエントリを保持するセクタの数です。バージョン 3 のファイルはここにゼロを書き、代わりにアロケーションテーブル経由でディレクトリの鎖をたどらなければなりません。この値を使うのはバージョン 4 だけです。 |
| 44 | 4 | Number of FAT sectors | file allocation table に割り当てられたセクタの数。FAT セクタ 1 つはセクタサイズ ÷ 4 個の次セクタポインタを持つので、512 バイトセクタなら 128 セクタ分を写像し、テーブルは文書の大きさに比例して伸びます。 |
| 48 | 4 | First directory sector | ディレクトリの鎖が始まるセクタ番号。最初のディレクトリエントリは必ずルートで、名前は UTF-16 の 'Root Entry'、その子ツリーがファイル内のすべてのストリームに名前を与えています。中に実際に何が入っているかを知りたいなら、たどるべきフィールドはここ 1 つです。 |
| 52 | 4 | Transaction signature | トランザクション保存に対応した実装が増やす値で、それ以外ではゼロです。実務上は、どのアプリが書いたファイルかを見分ける指紋として役に立ちます。 |
| 56 | 4 | Mini stream cutoff size | 0x00001000(4096)と定められています。これより短いストリームは mini FAT 経由でミニストリームに格納され、これ以上なら専用のセクタを丸ごともらいます。1 つのファイルの中に 2 系統のアロケーション経路があり、その分岐をこの数値 1 つが決めています。 |
| 60 | 4 | First mini FAT sector | ミニアロケーションテーブルの先頭。小さなストリームがまったく無い文書では、鎖の終端マーカー FFFFFFFE が入ります。 |
| 64 | 4 | Number of mini FAT sectors | その鎖のセクタ数での長さ。大きなストリームだけでできた文書ならゼロが普通です。 |
| 68 | 4 | First DIFAT sector | ヘッダ内の 109 個の枠を使い切ったあと、二重間接アロケーションテーブルがどこへ続くか。FFFFFFFE なら続かなかったということで、512 バイトセクタでおよそ 7 MB 未満のファイルはすべてこれに当たります。 |
| 72 | 4 | Number of DIFAT sectors | その継続の鎖の長さ。DIFAT では位置がそのまま意味を持ちます。i 番目の枠は FAT インデックスの決まったブロックを担当するので、読めない枠をプレースホルダで埋めずに飛ばしてしまう読み手は、それ以降のセクタ番号を全部黙って読み違えます。 |
| 76 | 436 | DIFAT array | 最初の 109 個の FAT セクタを指す 4 バイトのセクタ番号が 109 個並び、ヘッダをちょうど 512 バイトに埋めます。未使用の枠は FFFFFFFF です。小さな compound file が DIFAT セクタを持たずに済むのはこのためで、ヘッダだけですべてを写像できています。 |
似た形式との見分け方
| 紛らわしい形式 | 見分け方 |
|---|---|
| Word 文書(docx) | Office の世代の分かれ目です。同じ「documents」フォルダに並んでいて、どちらも Word で開きますが、2007 年より前の .doc は D0 CF 11 E0、.docx は 50 4B 03 04 です。拡張子は証拠になりません。Office は旧形式のファイルを平気で .docx という名前で保存しますし、その逆もあり、開くときはバイトを見ています。オフセット 0 を読んでください。 |
| doc/xls/ppt/msi | 4 つとも同じ 8 バイトのマジックナンバーを共有していて、ヘッダからは一切区別できません。コンテナは中身について何も語らないのです。答えはオフセット 48 からたどるディレクトリツリーにあります。'WordDocument' というストリームがあれば .doc、'Workbook'(古くは 'Book')なら .xls、'PowerPoint Document' なら .ppt、base64 めいた読めない名前のストリーム群なら Windows Installer のパッケージです。 |
| msg | Outlook の .msg も compound file で、マジックナンバーも同じなので、判定器は OLE2 と答えてそこで止まります。正体を示すのは、プロパティタグと型が続く __substg1.0_ という名前のストリームで埋まったディレクトリと、__properties_version1.0 の存在です。文書のレイアウトではなく MAPI のプロパティ格納レイアウトということです。 |
| SQLite データベース | 「1 ファイルに丸ごとファイルシステム」という形式として実際に出会う 2 つなので、並べておく価値があります。SQLite は ASCII 文字列 'SQLite format 3' と NUL で始まり、CFB は D0 CF 11 E0 で始まります。どちらも固定サイズのページとフリーリストを使いますが、SQLite の形式は公開・版管理され、余すところなく文書化されています。一方 CFB は、Microsoft が公開するまでの 10 年間リバースエンジニアリングの対象でした。 |
よくある壊れ方
- FAT の鎖の断裂。ストリームは連続した領域ではなくセクタ番号の連結リストなので、アロケーションテーブルの 1 エントリが壊れてもストリームは途中で終わりません。よその領域へ迷い込むのです。文書は開き、途中の段落が別の段落になっていたりバイナリのゴミになっていたりします。しかもそれに気づけるチェックサムはどこにもありません。
- 途中切れ。512 バイトのヘッダは残るので、どのツールもファイルを正しく識別し、バージョンとセクタサイズまで報告します。そのうえで展開に失敗します。オフセット 48 が指すディレクトリセクタがファイルの末尾より先にあるからです。CFB にはトレーラーが無いので、PNG や ZIP のような安上がりな末尾チェックが存在しません。
- DIFAT のループ。二重間接テーブルのセクタ番号は検証されないので、壊れたファイルは鎖を自分自身へ向けられます。素朴なパーサーは永遠にたどり続けます。compound file のリーダーにおける典型的なサービス拒否の形で、防ぎ方はオフセット 44 や 72 の宣言値を信じることではなく、ファイルサイズから見て存在し得るセクタ数で走査に上限を課すことです。
- テキストモード転送。CFB のフィールドはすべてバイナリなので、ASCII モードの改行変換はセクタ境界をずらし、オフセット 24 から先のヘッダを壊します。オフセット 0 のマジックナンバーには 0D 0A が含まれておらず無傷で残るのが、まさに罠です。ファイルは依然 OLE2 と識別され、そして完全に読めません。
- バージョン 4 のファイルをバージョン 3 として読む。オフセット 26 のメジャーバージョンがセクタを 512 バイトとみなすか 4096 バイトとみなすかを決めます。これを無視するとセクタのオフセットが 8 倍ずれ、綺麗に失敗する代わりに、それらしく見えて完全に位置のずれたデータが出てきます。
この形式の現在地
OLE2 は死にきれないレガシーコンテナです。乗っているものが多すぎるからです。Office は 2007 年に ZIP + XML の形式へ移り、それ以前の文書はもう本当に古いのですが、それでも compound file が日々流通し続ける理由が 3 つあります。1 つは Windows Installer のパッケージで、今も CFB のままであり、今も Windows のソフトはこれで配られます。2 つ目は Outlook の .msg で、「このメールを保存」ボタンが吐き出すのはこれです。3 つ目は法務・行政・企業の文書倉庫に眠る膨大な .doc と .xls で、誰も変換する気はありません。セキュリティ上の面倒の種でもあり続けています。古い .doc のマクロと、形式自体の複雑さのせいで、compound file は 20 年にわたりマルウェアの定番の乗り物でした。メールゲートウェイが拡張子を問わず D0 CF 11 E0 を怪しむのはこのためです。新規に作る理由はもう何も残っていません。OOXML は ZIP なので普通のツールで中を覗け、圧縮も遥かに効き、公開された標準です。一方 CFB の仕様が [MS-CFB] として公開されたのは、世間がとっくにリバースエンジニアリングし終えた後でした。
OLE2 / CFB ファイルをこのサイトで開く
どれもブラウザ内で動きます。ファイルが端末から出ることはありません。
関連する形式
OLE2 / CFB と混同しやすい形式です。先頭バイトを見比べてください。
| 形式 | 先頭バイト | 拡張子 |
|---|---|---|
| Word 文書(docx) | 50 4B 03 04 | .docx |
| Excel ブック(xlsx) | 50 4B 03 04 | .xlsx |
| PowerPoint プレゼン(pptx) | 50 4B 03 04 | .pptx |
| 25 50 44 46 2D |